ETH Price: $1,925.25 (+0.38%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

1 address found via
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x60806040183094492023-10-09 0:51:11524 days ago1696812671  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Verifier

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 4 : Verifier.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

// SPDX-License-Identifier: MIT OR Apache-2.0




import "./KeysWithPlonkVerifier.sol";
import "./Config.sol";

// Hardcoded constants to avoid accessing store
contract Verifier is KeysWithPlonkVerifier, KeysWithPlonkVerifierOld, Config {
    // solhint-disable-next-line no-empty-blocks
    function initialize(bytes calldata) external {}

    /// @notice Verifier contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
    /// @param upgradeParameters Encoded representation of upgrade parameters
    // solhint-disable-next-line no-empty-blocks
    function upgrade(bytes calldata upgradeParameters) external {}

    function verifyAggregatedBlockProof(
        uint256[] memory _recursiveInput,
        uint256[] calldata _proof,
        uint8[] memory _vkIndexes,
        uint256[] memory _individualVksInputs,
        uint256[16] memory _subproofsLimbs
    ) external view returns (bool) {
        for (uint256 i = 0; i < _individualVksInputs.length; ++i) {
            uint256 commitment = _individualVksInputs[i];
            _individualVksInputs[i] = commitment & INPUT_MASK;
        }
        VerificationKey memory vk = getVkAggregated(uint32(_vkIndexes.length));

        return
            verify_serialized_proof_with_recursion(
                _recursiveInput,
                _proof,
                VK_TREE_ROOT,
                VK_MAX_INDEX,
                _vkIndexes,
                _individualVksInputs,
                _subproofsLimbs,
                vk
            );
    }

    function verifyExitProof(
        bytes32 _rootHash,
        uint32 _accountId,
        address _owner,
        uint32 _tokenId,
        uint128 _amount,
        uint32 _nftCreatorAccountId,
        address _nftCreatorAddress,
        uint32 _nftSerialId,
        bytes32 _nftContentHash,
        uint256[] calldata _proof
    ) external view returns (bool) {
        bytes32 commitment = sha256(
            abi.encodePacked(
                _rootHash,
                _accountId,
                _owner,
                _tokenId,
                _amount,
                _nftCreatorAccountId,
                _nftCreatorAddress,
                _nftSerialId,
                _nftContentHash
            )
        );

        uint256[] memory inputs = new uint256[](1);
        inputs[0] = uint256(commitment) & INPUT_MASK;
        ProofOld memory proof = deserialize_proof_old(inputs, _proof);
        VerificationKeyOld memory vk = getVkExit();
        require(vk.num_inputs == inputs.length, "n1");
        return verify_old(proof, vk);
    }
}

File 2 of 4 : KeysWithPlonkVerifier.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

// SPDX-License-Identifier: MIT OR Apache-2.0





import "./PlonkCore.sol";

// Hardcoded constants to avoid accessing store
contract KeysWithPlonkVerifier is VerifierWithDeserialize {

    uint256 constant VK_TREE_ROOT = 0x27362ec1b4c43df95bd87dc1b1591227b4095aaeb2e030db431c291daf986550;
    uint8 constant VK_MAX_INDEX = 3;

    function getVkAggregated(uint32 _proofs) internal pure returns (VerificationKey memory vk) {
        if (_proofs == uint32(1)) { return getVkAggregated1(); }
        else if (_proofs == uint32(4)) { return getVkAggregated4(); }
        else if (_proofs == uint32(8)) { return getVkAggregated8(); }
    }

    
    function getVkAggregated1() internal pure returns(VerificationKey memory vk) {
        vk.domain_size = 4194304;
        vk.num_inputs = 1;
        vk.omega = PairingsBn254.new_fr(0x18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede);
        vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
            0x16782f42f191b0b1841c2b6a42b7f0564af065d04818526df6c3ad41fe35f8da,
            0x125b9c68c0b931578f8a18fd23ce08e7b7c082ad76404ccece796fa9b3ec0cb0
        );
        vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
            0x2511833eee308a3936b23b27c929942a60aa780747bf32143dc183e873144bfd,
            0x1b8d88d78fcc4a36ebe90fbbdc4547442411e0c8d484727d5c7c6eec27ad2df0
        );
        vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
            0x2945641d0c5556aa333ef6c8431e24379b73eccbed7ff3e9425cc64aee1e92ed,
            0x25bbf079192cc83f160da9375e7aec3d3d2caac8d831a29b50f5497071fc14c6
        );
        vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
            0x09b3c361e5895a8e074eb9b9a9e57af59966f0464068460adc3f64e58544afa4,
            0x0412a017f775dd05af16cf387a1e822c2a7e0f8b7cfabd0eb4eb0f67b20e4ada
        );
        vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
            0x244b30447ab3e56bb5a5a7f0ef8463a4047476ea269735a887b3de568b3401a3,
            0x2ba860198d5e6e0fd93355cb5f309e7e4c1113a57222830961999b79b83d700f
        );
        vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
            0x0e13af99775bf5555c366e9c8d4af25a2e195807b766b422856525c01a38b12d,
            0x1787389894222dba5371ab55d512460c5205c1baa0421fc877b183025079a472
        );
        vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
            0x233a03f89c094cf39c89020772d9b912bd0c303d211002ee5afc5c59e241f02b,
            0x04fa51fca1b17399bbbf2b99f17bbce6af1f50b085add4c41ac4ea64f65f4674
        );
        vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
            0x1ca088ed531e65b722c8b48568359bbe11051b86f1a8e8951eacc615d9faed3b,
            0x074b06c09de93dd79e070a9ded635e21a34d7178e9a670766e8208149c28e339
        );
        vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
            0x2b4c77c0d47676559061b47968a044aec625cb907181457428e5d08df9b27ef8,
            0x1c1be561bdc3eba16162886a2943882157f98ed8246f2063028497f1c108fa93
        );
        vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
            0x238fd7f2cbc3c3e5899483633c78f051e6d6d25f31aaa6b32b863d55b20d641a,
            0x1f9877b625eaae7a084582a2ffce326a6a5558f3efdb3367037098c4ca25a647
        );
        vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
            0x0b126f60653e371f3f2a85301f16e9cf4af04922a2725fc131b17e90e13d0d84,
            0x13bc3f0c7475b74591827463943b35cfd05adb7094a79eeeee2067e8e28a8e84
        );
        vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
            0x06cae3c1e5b43afb4dda3243c99da693a27eba065fd61a873e99e2c85fd22719,
            0x14343c6bdcc85b01b053f26aa3c473cb2f24747ba6d6b90b2323b24f3dfd127e
        );
        vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
            0x217564e2c710d050161b57ef2700e1676251a6d457c4b0d94c41a4492d6dcea3,
            0x2365779642d63803d0265a7cc666b3af6ad92b7e9ef38d9113db1208b83f0732
        );
        vk.copy_permutation_non_residues[0] = PairingsBn254.new_fr(
            0x0000000000000000000000000000000000000000000000000000000000000005
        );
        vk.copy_permutation_non_residues[1] = PairingsBn254.new_fr(
            0x0000000000000000000000000000000000000000000000000000000000000007
        );
        vk.copy_permutation_non_residues[2] = PairingsBn254.new_fr(
            0x000000000000000000000000000000000000000000000000000000000000000a
        );

        vk.g2_x = PairingsBn254.new_g2(
            [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
            0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
            [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
            0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
        );
    }
    
    function getVkAggregated4() internal pure returns(VerificationKey memory vk) {
        vk.domain_size = 8388608;
        vk.num_inputs = 1;
        vk.omega = PairingsBn254.new_fr(0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863);
        vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
            0x1878d6c837a0f16cb055d3a4e79fba0d85de670dacd708dadd55407b0619796d,
            0x0b3282e52a38ecec63ba42710e8d1ad5c8715c7ed07ce217a3eec747a3f37d76
        );
        vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
            0x07425bcaf480e377886678d5b5432f0945e3fc952126503a7b672dc4b03f2c26,
            0x155b8003ea27945bf43fb5f43291f76e2aa361e0ec81550c0af66dcd1dc8077e
        );
        vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
            0x1292b8795f05fc50782ea7303e2b65a7b2f0e1cc3dead51dfa0b9d2183e5d907,
            0x220d344a384ac53f682e1be6c69407a1fadd0a589de36b95ddc4da05693ba679
        );
        vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
            0x283412c1942c0cb3fffc935aab313a37510888bd5ae5972d8d67edc2312af895,
            0x1040e655967354e7ae9227c6200c2256cdcbb707e7158b66462aba23d96b8de2
        );
        vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
            0x2abe282377038904420434202c11a4f849e64babd436b93192d8d9c34d28ce44,
            0x19f0ed010326da1cf8ac93a0f73617ab7c9acb30a0c23a26db9ec19ab6a52fcb
        );
        vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
            0x236f01e67b19be0e7487100a14fd04a05a83a5660966ace987c5248f8c883459,
            0x0ebe824fb1e778491bcb8091d2adbc18dceda4fa9ee191b71c5834a71c533c41
        );
        vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
            0x2ad3c37aa0b1335f6c70d0e10f0a123a28ea012e857df30e3ced524ef6562c71,
            0x1b52d7ac4ee6082438deab8ab0f2944c9fd53258de305065f8323a3767dd8234
        );
        vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
            0x173c39587688a8967e915959df613aecf44ad0c7d2019ec32311bccdf542c78e,
            0x2421a36a67559ed89afbff081cd45b318835e2b0233c047d030abc48b5011c22
        );
        vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
            0x177d8ef11cac24105d4b38e035b891986d163d9df717fce12d18af324f86d2dc,
            0x02cd01ba1c82c85b4f0f8c7304254de64516857ac4f7bb60f052bb2af98132c5
        );
        vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
            0x21da2c0f2b7849d4c44dbc487d370cccbae78fbd979e79575e04b7a983f2f68a,
            0x14ffb806769ccf0d2c692cd93653491966525554d79efc37cfba5a5c08b15039
        );
        vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
            0x184cc2f37e687a9be2404cd367536f14a505f086fd597cb966c5b753f325adb4,
            0x20aaed49755efed4814025ac679570f62b8c98a1b8d977969242c3ffa67884d6
        );
        vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
            0x0a2dee920031d9cd5ed499dc3cb901657079f6a2dfb0ba389b0181803bb91e24,
            0x272ac2a214f46be0ed7d2b4cf125504ef82d929b1c1ec0a81655c66f39403cd1
        );
        vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
            0x07e360365c7a5363389b2d2449b9471754591f01a623fd5553c5cfe6bad19aaf,
            0x1b814914958835ef86de3c26c6c4bdc27e947f38cb0d2bfaa421d66cabfb7d55
        );
        vk.copy_permutation_non_residues[0] = PairingsBn254.new_fr(
            0x0000000000000000000000000000000000000000000000000000000000000005
        );
        vk.copy_permutation_non_residues[1] = PairingsBn254.new_fr(
            0x0000000000000000000000000000000000000000000000000000000000000007
        );
        vk.copy_permutation_non_residues[2] = PairingsBn254.new_fr(
            0x000000000000000000000000000000000000000000000000000000000000000a
        );

        vk.g2_x = PairingsBn254.new_g2(
            [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
            0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
            [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
            0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
        );
    }
    
    function getVkAggregated8() internal pure returns(VerificationKey memory vk) {
        vk.domain_size = 16777216;
        vk.num_inputs = 1;
        vk.omega = PairingsBn254.new_fr(0x1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb);
        vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
            0x1aab46b9aa3adcac623c360e4d075572e3f56f4c75ac3b8663a7b059bd9b1857,
            0x166ac39283efa3d6cb36423e83e2360f006e5fa374b454dea5fe92cc50d4193f
        );
        vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
            0x13bce0a7bfbf2e7a81f18e84966c32422446b01f54cc7dc2ad3f64e92fe94cad,
            0x0247234b0cdfd8c95a767f84303c3dd65ce7b15856c2840635d9d4754ba99479
        );
        vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
            0x08742bad9a7cbcc9dbb49a25bebce179295d1cf70fd8f9c8e82b8a658ee0b67c,
            0x2a467983257850c5fa27f2f52f0c5c5fc98e7d2e0d440a8fd954ad981ff0ce9f
        );
        vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
            0x16ebdd4b95b872cd09c13b6b54a8b8bf81a01529a71234db26e3b22c6d632723,
            0x034219d7ad9ef204cfb3e32c4a47af82eea40504c2b1bac785104731722ed617
        );
        vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
            0x2e3a7c4458a8dc1535e68bac5dd5c1c9ff3886df4156bad4a08fcd08ebf1db26,
            0x173859705317db06e5b7d260898ab08e72fae987c272b82345105d72bfd00ab8
        );
        vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
            0x0b830132e3325eaaea73c1095e615358db38dfb39248c90f8ff4afde169e7657,
            0x0bfedf8cfce7260c16bb1f76ad9a39f73a68087e5c68e841020aeaa5ba301a9f
        );
        vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
            0x1660c850da793add523f7990b983896e50d5549eec7990ec26aabc220ca58d52,
            0x0ba698e78dee0d41cf8aefde82c5bfda38be071e11025b56db779ddb40a4fe92
        );
        vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
            0x024fe4ce02dd48937e4642b66308ae15d731e0ea82fc5430a0470d9a5dab3694,
            0x177cac2d79a8bfa6aba134e24bded06d06219979c18b2fa4fe71baea9885985d
        );
        vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
            0x00a848bc76c52faf7d4e7cc4086b50e3ccc9b1cebef130ac1bbf1816502df59d,
            0x02f42f326f82b33cb9e4e7cfb332889eec95c2813f7968b3a50d838b3cbfa676
        );
        vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
            0x20c176738979e0d1ea9541bf26e6209d3091b618ae94f3c72e13e954a1614f60,
            0x2a7019c81009c00a7412b6a303b2eb118a362a558837e9ecdb912589bc11ff83
        );
        vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
            0x10a92b3fa2b8280030c9de5cbcab4da3cf9b5b3f63f3ad60284ecded63cc54ea,
            0x1bde2a83db435b8c74e4239b4f8416da88008331a758d8c68a9104f2dfc3e237
        );
        vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
            0x08e2e513d1e548a627e2d4f74d28dea916d8598415b70543bb3e92429f0111cb,
            0x2fb46898f77e32d7fd646fe31b60320423aa4698501e329e206b6acfcfb01337
        );
        vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
            0x145b88d324270872b13784fbb7ccdee6e5593d2d5cbc81f4aaa9b4268cfc5094,
            0x197d826aaf2a9853ca98ec9c0e55376eec1a6a0f5dbbbe02afeb1b567d8eafa0
        );
        vk.copy_permutation_non_residues[0] = PairingsBn254.new_fr(
            0x0000000000000000000000000000000000000000000000000000000000000005
        );
        vk.copy_permutation_non_residues[1] = PairingsBn254.new_fr(
            0x0000000000000000000000000000000000000000000000000000000000000007
        );
        vk.copy_permutation_non_residues[2] = PairingsBn254.new_fr(
            0x000000000000000000000000000000000000000000000000000000000000000a
        );

        vk.g2_x = PairingsBn254.new_g2(
            [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
            0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
            [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
            0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
        );
    }
    

}

// Hardcoded constants to avoid accessing store
contract KeysWithPlonkVerifierOld is VerifierWithDeserializeOld {

    
    function getVkExit() internal pure returns(VerificationKeyOld memory vk) {
        vk.domain_size = 524288;
        vk.num_inputs = 1;
        vk.omega = PairingsBn254.new_fr(0x0cf1526aaafac6bacbb67d11a4077806b123f767e4b0883d14cc0193568fc082);
        vk.selector_commitments[0] = PairingsBn254.new_g1(
            0x114dd473f77a15b602201577dd4b64a32a783cb32fbc02911e512df6a219695d,
            0x04c68f82a5dd7d0cc90318bdff493b3d552d148ad859c373ffe55275e043c43b
        );
        vk.selector_commitments[1] = PairingsBn254.new_g1(
            0x245e8c882af503cb5421f5135b4295a920ccf68b42ae7fb967f044f54e2aaa29,
            0x071322ee387a9ce49fe7ef2edb6e9237203dee49ec47483af85e356b79fb06fd
        );
        vk.selector_commitments[2] = PairingsBn254.new_g1(
            0x0187754ab593b07a420b3b4d215c20ed49acf90fc4c97e4b06e8f5bc0a2eb3f4,
            0x0170f9286ce950286a16ea25136c163c0b32019f31b89c256a612d40b863d0b6
        );
        vk.selector_commitments[3] = PairingsBn254.new_g1(
            0x0defecfae1d2b9ec9b2ee4d4798c625fa50f6a4ddb7747a7293df0c17fcb90c2,
            0x0f91d08fceebf85fb80f12cda78cefa1ee9dbf5cfe7c4f0704b3c6620fa50c55
        );
        vk.selector_commitments[4] = PairingsBn254.new_g1(
            0x2f7fef3b3fb64af6640f93803a18b3e5ce4e0e60aecd4f924c833fa6fa6da961,
            0x03908fc737113ac7f3529fe3b36efca200c66d1d85d2fc081973214c586de732
        );
        vk.selector_commitments[5] = PairingsBn254.new_g1(
            0x14ce3c0e9b78fc331327249e707f58fa4bb0ed746bdc9c2262ad0cf905609627,
            0x09e64fdac452b424e98fc4a92f7222693d0d84ab48aadd9c46151dbe5f1a34a9
        );

        // we only have access to value of the d(x) witness polynomial on the next
        // trace step, so we only need one element here and deal with it in other places
        // by having this in mind
        vk.next_step_selector_commitments[0] = PairingsBn254.new_g1(
            0x1d10bfd923c17d9623ec02db00099355b373021432ae1edef69b0f5f461f78d6,
            0x24e370a93f65f42888781d0158bb6ef9136c8bbd047d7993b8276bc8df8b640a
        );

        vk.permutation_commitments[0] = PairingsBn254.new_g1(
            0x1fd1755ed4d06d91d50db4771d332cfa2bc2ca0e10ac8b77e0d6b73b993e788e,
            0x0bdbf3b7f0d3cffdcf818f1fba18b90914eda59b454bd1858c6c0916b817f883
        );
        vk.permutation_commitments[1] = PairingsBn254.new_g1(
            0x1f3b8d12ffa2ceb2bb42d232ad2cf11bce3183472b622e11cc841d26f42ad507,
            0x0ce815e32b3bd14311cde210cda1bd351617d539ed3e9d96a8605f364f3a29b0
        );
        vk.permutation_commitments[2] = PairingsBn254.new_g1(
            0x123afa8c1cec1956d7330db062498a2a3e3a9862926c02e1228d9cfb63d3c301,
            0x0f5af15ff0a3e35486c541f72956b53ff6d0740384ef6463c866146c1bd2afc8
        );
        vk.permutation_commitments[3] = PairingsBn254.new_g1(
            0x01069e38ea6396af1623921101d3d3d14ee46942fb23bf1d110efb994c3ee573,
            0x232a8ce7151e69601a7867f9dcac8e2de4dd8352d119c90bbb0fb84720c02513
        );

        vk.permutation_non_residues[0] = PairingsBn254.new_fr(
            0x0000000000000000000000000000000000000000000000000000000000000005
        );
        vk.permutation_non_residues[1] = PairingsBn254.new_fr(
            0x0000000000000000000000000000000000000000000000000000000000000007
        );
        vk.permutation_non_residues[2] = PairingsBn254.new_fr(
            0x000000000000000000000000000000000000000000000000000000000000000a
        );

        vk.g2_x = PairingsBn254.new_g2(
            [0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1, 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0],
            [0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4, 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55]
        );
    }
    
}

File 3 of 4 : Config.sol
pragma solidity ^0.7.0;

// SPDX-License-Identifier: MIT OR Apache-2.0



/// @title zkSync configuration constants
/// @author Matter Labs
contract Config {
    /// @dev ERC20 tokens and ETH withdrawals gas limit, used only for complete withdrawals
    uint256 internal constant WITHDRAWAL_GAS_LIMIT = 100000;

    /// @dev NFT withdrawals gas limit, used only for complete withdrawals
    uint256 internal constant WITHDRAWAL_NFT_GAS_LIMIT = 300000;

    /// @dev Bytes in one chunk
    uint8 internal constant CHUNK_BYTES = 10;

    /// @dev zkSync address length
    uint8 internal constant ADDRESS_BYTES = 20;

    uint8 internal constant PUBKEY_HASH_BYTES = 20;

    /// @dev Public key bytes length
    uint8 internal constant PUBKEY_BYTES = 32;

    /// @dev Ethereum signature r/s bytes length
    uint8 internal constant ETH_SIGN_RS_BYTES = 32;

    /// @dev Success flag bytes length
    uint8 internal constant SUCCESS_FLAG_BYTES = 1;

    /// @dev Max amount of tokens registered in the network (excluding ETH, which is hardcoded as tokenId = 0)
    uint32 internal constant MAX_AMOUNT_OF_REGISTERED_TOKENS = 1023;

    /// @dev Max account id that could be registered in the network
    uint32 internal constant MAX_ACCOUNT_ID = 16777215;

    /// @dev Expected average period of block creation
    uint256 internal constant BLOCK_PERIOD = 15 seconds;

    /// @dev ETH blocks verification expectation
    /// @dev Blocks can be reverted if they are not verified for at least EXPECT_VERIFICATION_IN.
    /// @dev If set to 0 validator can revert blocks at any time.
    uint256 internal constant EXPECT_VERIFICATION_IN = 0 hours / BLOCK_PERIOD;

    uint256 internal constant NOOP_BYTES = 1 * CHUNK_BYTES;
    uint256 internal constant DEPOSIT_BYTES = 6 * CHUNK_BYTES;
    uint256 internal constant MINT_NFT_BYTES = 5 * CHUNK_BYTES;
    uint256 internal constant TRANSFER_TO_NEW_BYTES = 6 * CHUNK_BYTES;
    uint256 internal constant PARTIAL_EXIT_BYTES = 6 * CHUNK_BYTES;
    uint256 internal constant TRANSFER_BYTES = 2 * CHUNK_BYTES;
    uint256 internal constant FORCED_EXIT_BYTES = 6 * CHUNK_BYTES;
    uint256 internal constant WITHDRAW_NFT_BYTES = 10 * CHUNK_BYTES;

    /// @dev Full exit operation length
    uint256 internal constant FULL_EXIT_BYTES = 11 * CHUNK_BYTES;

    /// @dev ChangePubKey operation length
    uint256 internal constant CHANGE_PUBKEY_BYTES = 6 * CHUNK_BYTES;

    /// @dev Expiration delta for priority request to be satisfied (in seconds)
    /// @dev NOTE: Priority expiration should be > (EXPECT_VERIFICATION_IN * BLOCK_PERIOD)
    /// @dev otherwise incorrect block with priority op could not be reverted.
    uint256 internal constant PRIORITY_EXPIRATION_PERIOD = 14 days;

    /// @dev Expiration delta for priority request to be satisfied (in ETH blocks)
    uint256 internal constant PRIORITY_EXPIRATION =
        PRIORITY_EXPIRATION_PERIOD/BLOCK_PERIOD;

    /// @dev Maximum number of priority request to clear during verifying the block
    /// @dev Cause deleting storage slots cost 5k gas per each slot it's unprofitable to clear too many slots
    /// @dev Value based on the assumption of ~750k gas cost of verifying and 5 used storage slots per PriorityOperation structure
    uint64 internal constant MAX_PRIORITY_REQUESTS_TO_DELETE_IN_VERIFY = 6;

    /// @dev Reserved time for users to send full exit priority operation in case of an upgrade (in seconds)
    uint256 internal constant MASS_FULL_EXIT_PERIOD = 5 days;

    /// @dev Reserved time for users to withdraw funds from full exit priority operation in case of an upgrade (in seconds)
    uint256 internal constant TIME_TO_WITHDRAW_FUNDS_FROM_FULL_EXIT = 2 days;

    /// @dev Notice period before activation preparation status of upgrade mode (in seconds)
    /// @dev NOTE: we must reserve for users enough time to send full exit operation, wait maximum time for processing this operation and withdraw funds from it.
    uint256 internal constant UPGRADE_NOTICE_PERIOD =
        0;

    /// @dev Timestamp - seconds since unix epoch
    uint256 internal constant COMMIT_TIMESTAMP_NOT_OLDER = 24 hours;

    /// @dev Maximum available error between real commit block timestamp and analog used in the verifier (in seconds)
    /// @dev Must be used cause miner's `block.timestamp` value can differ on some small value (as we know - 15 seconds)
    uint256 internal constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 15 minutes;

    /// @dev Bit mask to apply for verifier public input before verifying.
    uint256 internal constant INPUT_MASK = 14474011154664524427946373126085988481658748083205070504932198000989141204991;

    /// @dev Auth fact reset timelock.
    uint256 internal constant AUTH_FACT_RESET_TIMELOCK = 1 days;

    /// @dev Max deposit of ERC20 token that is possible to deposit
    uint128 internal constant MAX_DEPOSIT_AMOUNT = 20282409603651670423947251286015;

    uint32 internal constant SPECIAL_ACCOUNT_ID = 16777215;
    address internal constant SPECIAL_ACCOUNT_ADDRESS = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);
    uint32 internal constant SPECIAL_NFT_TOKEN_ID = 2147483646;

    uint32 internal constant MAX_FUNGIBLE_TOKEN_ID = 65535;

    uint256 internal constant SECURITY_COUNCIL_MEMBERS_NUMBER = 3;

    string internal constant name = "ZkSync";

    string internal constant version = "1.0";

    bytes32 internal constant EIP712_DOMAIN_TYPEHASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId)");

    bytes32 internal constant EIP712_CHANGEPUBKEY_TYPEHASH =
        keccak256("ChangePubKey(bytes20 pubKeyHash,uint32 nonce,uint32 accountId)");
}

File 4 of 4 : PlonkCore.sol
pragma solidity >=0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;

// SPDX-License-Identifier: MIT OR Apache-2.0
// solhint-disable




library PairingsBn254 {
    uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
    uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
    uint256 constant bn254_b_coeff = 3;

    struct G1Point {
        uint256 X;
        uint256 Y;
    }

    struct Fr {
        uint256 value;
    }

    function new_fr(uint256 fr) internal pure returns (Fr memory) {
        require(fr < r_mod);
        return Fr({value: fr});
    }

    function copy(Fr memory self) internal pure returns (Fr memory n) {
        n.value = self.value;
    }

    function assign(Fr memory self, Fr memory other) internal pure {
        self.value = other.value;
    }

    function inverse(Fr memory fr) internal view returns (Fr memory) {
        require(fr.value != 0);
        return pow(fr, r_mod - 2);
    }

    function add_assign(Fr memory self, Fr memory other) internal pure {
        self.value = addmod(self.value, other.value, r_mod);
    }

    function sub_assign(Fr memory self, Fr memory other) internal pure {
        self.value = addmod(self.value, r_mod - other.value, r_mod);
    }

    function mul_assign(Fr memory self, Fr memory other) internal pure {
        self.value = mulmod(self.value, other.value, r_mod);
    }

    function pow(Fr memory self, uint256 power) internal view returns (Fr memory) {
        uint256[6] memory input = [32, 32, 32, self.value, power, r_mod];
        uint256[1] memory result;
        bool success;
        assembly {
            success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20)
        }
        require(success);
        return Fr({value: result[0]});
    }

    // Encoding of field elements is: X[0] * z + X[1]
    struct G2Point {
        uint256[2] X;
        uint256[2] Y;
    }

    function P1() internal pure returns (G1Point memory) {
        return G1Point(1, 2);
    }

    function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) {
        return G1Point(x, y);
    }

    function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
        if (x == 0 && y == 0) {
            // point of infinity is (0,0)
            return G1Point(x, y);
        }

        // check encoding
        require(x < q_mod);
        require(y < q_mod);
        // check on curve
        uint256 lhs = mulmod(y, y, q_mod); // y^2
        uint256 rhs = mulmod(x, x, q_mod); // x^2
        rhs = mulmod(rhs, x, q_mod); // x^3
        rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b
        require(lhs == rhs);

        return G1Point(x, y);
    }

    function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) {
        return G2Point(x, y);
    }

    function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) {
        result.X = self.X;
        result.Y = self.Y;
    }

    function P2() internal pure returns (G2Point memory) {
        // for some reason ethereum expects to have c1*v + c0 form

        return
            G2Point(
                [
                    0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                    0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                ],
                [
                    0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                    0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                ]
            );
    }

    function negate(G1Point memory self) internal pure {
        // The prime q in the base field F_q for G1
        if (self.Y == 0) {
            require(self.X == 0);
            return;
        }

        self.Y = q_mod - self.Y;
    }

    function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
        point_add_into_dest(p1, p2, r);
        return r;
    }

    function point_add_assign(G1Point memory p1, G1Point memory p2) internal view {
        point_add_into_dest(p1, p2, p1);
    }

    function point_add_into_dest(
        G1Point memory p1,
        G1Point memory p2,
        G1Point memory dest
    ) internal view {
        if (p2.X == 0 && p2.Y == 0) {
            // we add zero, nothing happens
            dest.X = p1.X;
            dest.Y = p1.Y;
            return;
        } else if (p1.X == 0 && p1.Y == 0) {
            // we add into zero, and we add non-zero point
            dest.X = p2.X;
            dest.Y = p2.Y;
            return;
        } else {
            uint256[4] memory input;

            input[0] = p1.X;
            input[1] = p1.Y;
            input[2] = p2.X;
            input[3] = p2.Y;

            bool success = false;
            assembly {
                success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
            }
            require(success);
        }
    }

    function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view {
        point_sub_into_dest(p1, p2, p1);
    }

    function point_sub_into_dest(
        G1Point memory p1,
        G1Point memory p2,
        G1Point memory dest
    ) internal view {
        if (p2.X == 0 && p2.Y == 0) {
            // we subtracted zero, nothing happens
            dest.X = p1.X;
            dest.Y = p1.Y;
            return;
        } else if (p1.X == 0 && p1.Y == 0) {
            // we subtract from zero, and we subtract non-zero point
            dest.X = p2.X;
            dest.Y = q_mod - p2.Y;
            return;
        } else {
            uint256[4] memory input;

            input[0] = p1.X;
            input[1] = p1.Y;
            input[2] = p2.X;
            input[3] = q_mod - p2.Y;

            bool success = false;
            assembly {
                success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
            }
            require(success);
        }
    }

    function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) {
        point_mul_into_dest(p, s, r);
        return r;
    }

    function point_mul_assign(G1Point memory p, Fr memory s) internal view {
        point_mul_into_dest(p, s, p);
    }

    function point_mul_into_dest(
        G1Point memory p,
        Fr memory s,
        G1Point memory dest
    ) internal view {
        uint256[3] memory input;
        input[0] = p.X;
        input[1] = p.Y;
        input[2] = s.value;
        bool success;
        assembly {
            success := staticcall(gas(), 7, input, 0x60, dest, 0x40)
        }
        require(success);
    }

    function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) {
        require(p1.length == p2.length);
        uint256 elements = p1.length;
        uint256 inputSize = elements * 6;
        uint256[] memory input = new uint256[](inputSize);
        for (uint256 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];
        }
        uint256[1] memory out;
        bool success;
        assembly {
            success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
        }
        require(success);
        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);
    }
}

library TranscriptLibrary {
    // flip                    0xe000000000000000000000000000000000000000000000000000000000000000;
    uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

    uint32 constant DST_0 = 0;
    uint32 constant DST_1 = 1;
    uint32 constant DST_CHALLENGE = 2;

    struct Transcript {
        bytes32 state_0;
        bytes32 state_1;
        uint32 challenge_counter;
    }

    function new_transcript() internal pure returns (Transcript memory t) {
        t.state_0 = bytes32(0);
        t.state_1 = bytes32(0);
        t.challenge_counter = 0;
    }

    function update_with_u256(Transcript memory self, uint256 value) internal pure {
        bytes32 old_state_0 = self.state_0;
        self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value));
        self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value));
    }

    function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure {
        update_with_u256(self, value.value);
    }

    function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure {
        update_with_u256(self, p.X);
        update_with_u256(self, p.Y);
    }

    function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) {
        bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter));
        self.challenge_counter += 1;
        challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK});
    }
}

contract Plonk4VerifierWithAccessToDNext {
    uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;

    using PairingsBn254 for PairingsBn254.G1Point;
    using PairingsBn254 for PairingsBn254.G2Point;
    using PairingsBn254 for PairingsBn254.Fr;

    using TranscriptLibrary for TranscriptLibrary.Transcript;

    uint256 constant ZERO = 0;
    uint256 constant ONE = 1;
    uint256 constant TWO = 2;
    uint256 constant THREE = 3;
    uint256 constant FOUR = 4;

    uint256 constant STATE_WIDTH = 4;
    uint256 constant NUM_DIFFERENT_GATES = 2;
    uint256 constant NUM_SETUP_POLYS_FOR_MAIN_GATE = 7;
    uint256 constant NUM_SETUP_POLYS_RANGE_CHECK_GATE = 0;
    uint256 constant ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP = 1;
    uint256 constant NUM_GATE_SELECTORS_OPENED_EXPLICITLY = 1;

    uint256 constant RECURSIVE_CIRCUIT_INPUT_COMMITMENT_MASK =
        0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    uint256 constant LIMB_WIDTH = 68;

    struct VerificationKey {
        uint256 domain_size;
        uint256 num_inputs;
        PairingsBn254.Fr omega;
        PairingsBn254.G1Point[NUM_SETUP_POLYS_FOR_MAIN_GATE + NUM_SETUP_POLYS_RANGE_CHECK_GATE] gate_setup_commitments;
        PairingsBn254.G1Point[NUM_DIFFERENT_GATES] gate_selector_commitments;
        PairingsBn254.G1Point[STATE_WIDTH] copy_permutation_commitments;
        PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_non_residues;
        PairingsBn254.G2Point g2_x;
    }

    struct Proof {
        uint256[] input_values;
        PairingsBn254.G1Point[STATE_WIDTH] wire_commitments;
        PairingsBn254.G1Point copy_permutation_grand_product_commitment;
        PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_commitments;
        PairingsBn254.Fr[STATE_WIDTH] wire_values_at_z;
        PairingsBn254.Fr[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP] wire_values_at_z_omega;
        PairingsBn254.Fr[NUM_GATE_SELECTORS_OPENED_EXPLICITLY] gate_selector_values_at_z;
        PairingsBn254.Fr copy_grand_product_at_z_omega;
        PairingsBn254.Fr quotient_polynomial_at_z;
        PairingsBn254.Fr linearization_polynomial_at_z;
        PairingsBn254.Fr[STATE_WIDTH - 1] permutation_polynomials_at_z;
        PairingsBn254.G1Point opening_at_z_proof;
        PairingsBn254.G1Point opening_at_z_omega_proof;
    }

    struct PartialVerifierState {
        PairingsBn254.Fr alpha;
        PairingsBn254.Fr beta;
        PairingsBn254.Fr gamma;
        PairingsBn254.Fr v;
        PairingsBn254.Fr u;
        PairingsBn254.Fr z;
        PairingsBn254.Fr[] cached_lagrange_evals;
    }

    function evaluate_lagrange_poly_out_of_domain(
        uint256 poly_num,
        uint256 domain_size,
        PairingsBn254.Fr memory omega,
        PairingsBn254.Fr memory at
    ) internal view returns (PairingsBn254.Fr memory res) {
        require(poly_num < domain_size);
        PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
        PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
        res = at.pow(domain_size);
        res.sub_assign(one);
        require(res.value != 0); // Vanishing polynomial can not be zero at point `at`
        res.mul_assign(omega_power);

        PairingsBn254.Fr memory den = PairingsBn254.copy(at);
        den.sub_assign(omega_power);
        den.mul_assign(PairingsBn254.new_fr(domain_size));

        den = den.inverse();

        res.mul_assign(den);
    }

    function batch_evaluate_lagrange_poly_out_of_domain(
        uint256[] memory poly_nums,
        uint256 domain_size,
        PairingsBn254.Fr memory omega,
        PairingsBn254.Fr memory at
    ) internal view returns (PairingsBn254.Fr[] memory res) {
        PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
        PairingsBn254.Fr memory tmp_1 = PairingsBn254.new_fr(0);
        PairingsBn254.Fr memory tmp_2 = PairingsBn254.new_fr(domain_size);
        PairingsBn254.Fr memory vanishing_at_z = at.pow(domain_size);
        vanishing_at_z.sub_assign(one);
        // we can not have random point z be in domain
        require(vanishing_at_z.value != 0);
        PairingsBn254.Fr[] memory nums = new PairingsBn254.Fr[](poly_nums.length);
        PairingsBn254.Fr[] memory dens = new PairingsBn254.Fr[](poly_nums.length);
        // numerators in a form omega^i * (z^n - 1)
        // denoms in a form (z - omega^i) * N
        for (uint256 i = 0; i < poly_nums.length; i++) {
            tmp_1 = omega.pow(poly_nums[i]); // power of omega
            nums[i].assign(vanishing_at_z);
            nums[i].mul_assign(tmp_1);

            dens[i].assign(at); // (X - omega^i) * N
            dens[i].sub_assign(tmp_1);
            dens[i].mul_assign(tmp_2); // mul by domain size
        }

        PairingsBn254.Fr[] memory partial_products = new PairingsBn254.Fr[](poly_nums.length);
        partial_products[0].assign(PairingsBn254.new_fr(1));
        for (uint256 i = 1; i < dens.length - 1; i++) {
            partial_products[i].assign(dens[i - 1]);
            partial_products[i].mul_assign(dens[i]);
        }

        tmp_2.assign(partial_products[partial_products.length - 1]);
        tmp_2.mul_assign(dens[dens.length - 1]);
        tmp_2 = tmp_2.inverse(); // tmp_2 contains a^-1 * b^-1 (with! the last one)

        for (uint256 i = dens.length - 1; i < dens.length; i--) {
            dens[i].assign(tmp_2); // all inversed
            dens[i].mul_assign(partial_products[i]); // clear lowest terms
            tmp_2.mul_assign(dens[i]);
        }

        for (uint256 i = 0; i < nums.length; i++) {
            nums[i].mul_assign(dens[i]);
        }

        return nums;
    }

    function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at)
        internal
        view
        returns (PairingsBn254.Fr memory res)
    {
        res = at.pow(domain_size);
        res.sub_assign(PairingsBn254.new_fr(1));
    }

    function verify_at_z(
        PartialVerifierState memory state,
        Proof memory proof,
        VerificationKey memory vk
    ) internal view returns (bool) {
        PairingsBn254.Fr memory lhs = evaluate_vanishing(vk.domain_size, state.z);
        require(lhs.value != 0); // we can not check a polynomial relationship if point `z` is in the domain
        lhs.mul_assign(proof.quotient_polynomial_at_z);

        PairingsBn254.Fr memory quotient_challenge = PairingsBn254.new_fr(1);
        PairingsBn254.Fr memory rhs = PairingsBn254.copy(proof.linearization_polynomial_at_z);

        // public inputs
        PairingsBn254.Fr memory tmp = PairingsBn254.new_fr(0);
        PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0);
        for (uint256 i = 0; i < proof.input_values.length; i++) {
            tmp.assign(state.cached_lagrange_evals[i]);
            tmp.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
            inputs_term.add_assign(tmp);
        }

        inputs_term.mul_assign(proof.gate_selector_values_at_z[0]);
        rhs.add_assign(inputs_term);

        // now we need 5th power
        quotient_challenge.mul_assign(state.alpha);
        quotient_challenge.mul_assign(state.alpha);
        quotient_challenge.mul_assign(state.alpha);
        quotient_challenge.mul_assign(state.alpha);
        quotient_challenge.mul_assign(state.alpha);

        PairingsBn254.Fr memory z_part = PairingsBn254.copy(proof.copy_grand_product_at_z_omega);
        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            tmp.assign(proof.permutation_polynomials_at_z[i]);
            tmp.mul_assign(state.beta);
            tmp.add_assign(state.gamma);
            tmp.add_assign(proof.wire_values_at_z[i]);

            z_part.mul_assign(tmp);
        }

        tmp.assign(state.gamma);
        // we need a wire value of the last polynomial in enumeration
        tmp.add_assign(proof.wire_values_at_z[STATE_WIDTH - 1]);

        z_part.mul_assign(tmp);
        z_part.mul_assign(quotient_challenge);

        rhs.sub_assign(z_part);

        quotient_challenge.mul_assign(state.alpha);

        tmp.assign(state.cached_lagrange_evals[0]);
        tmp.mul_assign(quotient_challenge);

        rhs.sub_assign(tmp);

        return lhs.value == rhs.value;
    }

    function add_contribution_from_range_constraint_gates(
        PartialVerifierState memory state,
        Proof memory proof,
        PairingsBn254.Fr memory current_alpha
    ) internal pure returns (PairingsBn254.Fr memory res) {
        // now add contribution from range constraint gate
        // we multiply selector commitment by all the factors (alpha*(c - 4d)(c - 4d - 1)(..-2)(..-3) + alpha^2 * (4b - c)()()() + {} + {})

        PairingsBn254.Fr memory one_fr = PairingsBn254.new_fr(ONE);
        PairingsBn254.Fr memory two_fr = PairingsBn254.new_fr(TWO);
        PairingsBn254.Fr memory three_fr = PairingsBn254.new_fr(THREE);
        PairingsBn254.Fr memory four_fr = PairingsBn254.new_fr(FOUR);

        res = PairingsBn254.new_fr(0);
        PairingsBn254.Fr memory t0 = PairingsBn254.new_fr(0);
        PairingsBn254.Fr memory t1 = PairingsBn254.new_fr(0);
        PairingsBn254.Fr memory t2 = PairingsBn254.new_fr(0);

        for (uint256 i = 0; i < 3; i++) {
            current_alpha.mul_assign(state.alpha);

            // high - 4*low

            // this is 4*low
            t0 = PairingsBn254.copy(proof.wire_values_at_z[3 - i]);
            t0.mul_assign(four_fr);

            // high
            t1 = PairingsBn254.copy(proof.wire_values_at_z[2 - i]);
            t1.sub_assign(t0);

            // t0 is now t1 - {0,1,2,3}

            // first unroll manually for -0;
            t2 = PairingsBn254.copy(t1);

            // -1
            t0 = PairingsBn254.copy(t1);
            t0.sub_assign(one_fr);
            t2.mul_assign(t0);

            // -2
            t0 = PairingsBn254.copy(t1);
            t0.sub_assign(two_fr);
            t2.mul_assign(t0);

            // -3
            t0 = PairingsBn254.copy(t1);
            t0.sub_assign(three_fr);
            t2.mul_assign(t0);

            t2.mul_assign(current_alpha);

            res.add_assign(t2);
        }

        // now also d_next - 4a

        current_alpha.mul_assign(state.alpha);

        // high - 4*low

        // this is 4*low
        t0 = PairingsBn254.copy(proof.wire_values_at_z[0]);
        t0.mul_assign(four_fr);

        // high
        t1 = PairingsBn254.copy(proof.wire_values_at_z_omega[0]);
        t1.sub_assign(t0);

        // t0 is now t1 - {0,1,2,3}

        // first unroll manually for -0;
        t2 = PairingsBn254.copy(t1);

        // -1
        t0 = PairingsBn254.copy(t1);
        t0.sub_assign(one_fr);
        t2.mul_assign(t0);

        // -2
        t0 = PairingsBn254.copy(t1);
        t0.sub_assign(two_fr);
        t2.mul_assign(t0);

        // -3
        t0 = PairingsBn254.copy(t1);
        t0.sub_assign(three_fr);
        t2.mul_assign(t0);

        t2.mul_assign(current_alpha);

        res.add_assign(t2);

        return res;
    }

    function reconstruct_linearization_commitment(
        PartialVerifierState memory state,
        Proof memory proof,
        VerificationKey memory vk
    ) internal view returns (PairingsBn254.G1Point memory res) {
        // we compute what power of v is used as a delinearization factor in batch opening of
        // commitments. Let's label W(x) = 1 / (x - z) *
        // [
        // t_0(x) + z^n * t_1(x) + z^2n * t_2(x) + z^3n * t_3(x) - t(z)
        // + v (r(x) - r(z))
        // + v^{2..5} * (witness(x) - witness(z))
        // + v^{6} * (selector(x) - selector(z))
        // + v^{7..9} * (permutation(x) - permutation(z))
        // ]
        // W'(x) = 1 / (x - z*omega) *
        // [
        // + v^10 (z(x) - z(z*omega)) <- we need this power
        // + v^11 * (d(x) - d(z*omega))
        // ]
        //

        // we reconstruct linearization polynomial virtual selector
        // for that purpose we first linearize over main gate (over all it's selectors)
        // and multiply them by value(!) of the corresponding main gate selector
        res = PairingsBn254.copy_g1(vk.gate_setup_commitments[STATE_WIDTH + 1]); // index of q_const(x)

        PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();
        PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(0);

        // addition gates
        for (uint256 i = 0; i < STATE_WIDTH; i++) {
            tmp_g1 = vk.gate_setup_commitments[i].point_mul(proof.wire_values_at_z[i]);
            res.point_add_assign(tmp_g1);
        }

        // multiplication gate
        tmp_fr.assign(proof.wire_values_at_z[0]);
        tmp_fr.mul_assign(proof.wire_values_at_z[1]);
        tmp_g1 = vk.gate_setup_commitments[STATE_WIDTH].point_mul(tmp_fr);
        res.point_add_assign(tmp_g1);

        // d_next
        tmp_g1 = vk.gate_setup_commitments[STATE_WIDTH + 2].point_mul(proof.wire_values_at_z_omega[0]); // index of q_d_next(x)
        res.point_add_assign(tmp_g1);

        // multiply by main gate selector(z)
        res.point_mul_assign(proof.gate_selector_values_at_z[0]); // these is only one explicitly opened selector

        PairingsBn254.Fr memory current_alpha = PairingsBn254.new_fr(ONE);

        // calculate scalar contribution from the range check gate
        tmp_fr = add_contribution_from_range_constraint_gates(state, proof, current_alpha);
        tmp_g1 = vk.gate_selector_commitments[1].point_mul(tmp_fr); // selector commitment for range constraint gate * scalar
        res.point_add_assign(tmp_g1);

        // proceed as normal to copy permutation
        current_alpha.mul_assign(state.alpha); // alpha^5

        PairingsBn254.Fr memory alpha_for_grand_product = PairingsBn254.copy(current_alpha);

        // z * non_res * beta + gamma + a
        PairingsBn254.Fr memory grand_product_part_at_z = PairingsBn254.copy(state.z);
        grand_product_part_at_z.mul_assign(state.beta);
        grand_product_part_at_z.add_assign(proof.wire_values_at_z[0]);
        grand_product_part_at_z.add_assign(state.gamma);
        for (uint256 i = 0; i < vk.copy_permutation_non_residues.length; i++) {
            tmp_fr.assign(state.z);
            tmp_fr.mul_assign(vk.copy_permutation_non_residues[i]);
            tmp_fr.mul_assign(state.beta);
            tmp_fr.add_assign(state.gamma);
            tmp_fr.add_assign(proof.wire_values_at_z[i + 1]);

            grand_product_part_at_z.mul_assign(tmp_fr);
        }

        grand_product_part_at_z.mul_assign(alpha_for_grand_product);

        // alpha^n & L_{0}(z), and we bump current_alpha
        current_alpha.mul_assign(state.alpha);

        tmp_fr.assign(state.cached_lagrange_evals[0]);
        tmp_fr.mul_assign(current_alpha);

        grand_product_part_at_z.add_assign(tmp_fr);

        // prefactor for grand_product(x) is complete

        // add to the linearization a part from the term
        // - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X)
        PairingsBn254.Fr memory last_permutation_part_at_z = PairingsBn254.new_fr(1);
        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            tmp_fr.assign(state.beta);
            tmp_fr.mul_assign(proof.permutation_polynomials_at_z[i]);
            tmp_fr.add_assign(state.gamma);
            tmp_fr.add_assign(proof.wire_values_at_z[i]);

            last_permutation_part_at_z.mul_assign(tmp_fr);
        }

        last_permutation_part_at_z.mul_assign(state.beta);
        last_permutation_part_at_z.mul_assign(proof.copy_grand_product_at_z_omega);
        last_permutation_part_at_z.mul_assign(alpha_for_grand_product); // we multiply by the power of alpha from the argument

        // actually multiply prefactors by z(x) and perm_d(x) and combine them
        tmp_g1 = proof.copy_permutation_grand_product_commitment.point_mul(grand_product_part_at_z);
        tmp_g1.point_sub_assign(vk.copy_permutation_commitments[STATE_WIDTH - 1].point_mul(last_permutation_part_at_z));

        res.point_add_assign(tmp_g1);
        // multiply them by v immedately as linearization has a factor of v^1
        res.point_mul_assign(state.v);
        // res now contains contribution from the gates linearization and
        // copy permutation part

        // now we need to add a part that is the rest
        // for z(x*omega):
        // - (a(z) + beta*perm_a + gamma)*()*()*(d(z) + gamma) * z(x*omega)
    }

    function aggregate_commitments(
        PartialVerifierState memory state,
        Proof memory proof,
        VerificationKey memory vk
    ) internal view returns (PairingsBn254.G1Point[2] memory res) {
        PairingsBn254.G1Point memory d = reconstruct_linearization_commitment(state, proof, vk);

        PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);

        PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();

        PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);

        PairingsBn254.G1Point memory commitment_aggregation = PairingsBn254.copy_g1(proof.quotient_poly_commitments[0]);
        PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(1);
        for (uint256 i = 1; i < proof.quotient_poly_commitments.length; i++) {
            tmp_fr.mul_assign(z_in_domain_size);
            tmp_g1 = proof.quotient_poly_commitments[i].point_mul(tmp_fr);
            commitment_aggregation.point_add_assign(tmp_g1);
        }

        aggregation_challenge.mul_assign(state.v);
        commitment_aggregation.point_add_assign(d);

        for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
            aggregation_challenge.mul_assign(state.v);
            tmp_g1 = proof.wire_commitments[i].point_mul(aggregation_challenge);
            commitment_aggregation.point_add_assign(tmp_g1);
        }

        for (uint256 i = 0; i < NUM_GATE_SELECTORS_OPENED_EXPLICITLY; i++) {
            aggregation_challenge.mul_assign(state.v);
            tmp_g1 = vk.gate_selector_commitments[0].point_mul(aggregation_challenge);
            commitment_aggregation.point_add_assign(tmp_g1);
        }

        for (uint256 i = 0; i < vk.copy_permutation_commitments.length - 1; i++) {
            aggregation_challenge.mul_assign(state.v);
            tmp_g1 = vk.copy_permutation_commitments[i].point_mul(aggregation_challenge);
            commitment_aggregation.point_add_assign(tmp_g1);
        }

        aggregation_challenge.mul_assign(state.v);
        // now do prefactor for grand_product(x*omega)
        tmp_fr.assign(aggregation_challenge);
        tmp_fr.mul_assign(state.u);
        commitment_aggregation.point_add_assign(proof.copy_permutation_grand_product_commitment.point_mul(tmp_fr));

        aggregation_challenge.mul_assign(state.v);

        tmp_fr.assign(aggregation_challenge);
        tmp_fr.mul_assign(state.u);
        tmp_g1 = proof.wire_commitments[STATE_WIDTH - 1].point_mul(tmp_fr);
        commitment_aggregation.point_add_assign(tmp_g1);

        // collect opening values
        aggregation_challenge = PairingsBn254.new_fr(1);

        PairingsBn254.Fr memory aggregated_value = PairingsBn254.copy(proof.quotient_polynomial_at_z);

        aggregation_challenge.mul_assign(state.v);

        tmp_fr.assign(proof.linearization_polynomial_at_z);
        tmp_fr.mul_assign(aggregation_challenge);
        aggregated_value.add_assign(tmp_fr);

        for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
            aggregation_challenge.mul_assign(state.v);

            tmp_fr.assign(proof.wire_values_at_z[i]);
            tmp_fr.mul_assign(aggregation_challenge);
            aggregated_value.add_assign(tmp_fr);
        }

        for (uint256 i = 0; i < proof.gate_selector_values_at_z.length; i++) {
            aggregation_challenge.mul_assign(state.v);
            tmp_fr.assign(proof.gate_selector_values_at_z[i]);
            tmp_fr.mul_assign(aggregation_challenge);
            aggregated_value.add_assign(tmp_fr);
        }

        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            aggregation_challenge.mul_assign(state.v);

            tmp_fr.assign(proof.permutation_polynomials_at_z[i]);
            tmp_fr.mul_assign(aggregation_challenge);
            aggregated_value.add_assign(tmp_fr);
        }

        aggregation_challenge.mul_assign(state.v);

        tmp_fr.assign(proof.copy_grand_product_at_z_omega);
        tmp_fr.mul_assign(aggregation_challenge);
        tmp_fr.mul_assign(state.u);
        aggregated_value.add_assign(tmp_fr);

        aggregation_challenge.mul_assign(state.v);

        tmp_fr.assign(proof.wire_values_at_z_omega[0]);
        tmp_fr.mul_assign(aggregation_challenge);
        tmp_fr.mul_assign(state.u);
        aggregated_value.add_assign(tmp_fr);

        commitment_aggregation.point_sub_assign(PairingsBn254.P1().point_mul(aggregated_value));

        PairingsBn254.G1Point memory pair_with_generator = commitment_aggregation;
        pair_with_generator.point_add_assign(proof.opening_at_z_proof.point_mul(state.z));

        tmp_fr.assign(state.z);
        tmp_fr.mul_assign(vk.omega);
        tmp_fr.mul_assign(state.u);
        pair_with_generator.point_add_assign(proof.opening_at_z_omega_proof.point_mul(tmp_fr));

        PairingsBn254.G1Point memory pair_with_x = proof.opening_at_z_omega_proof.point_mul(state.u);
        pair_with_x.point_add_assign(proof.opening_at_z_proof);
        pair_with_x.negate();

        res[0] = pair_with_generator;
        res[1] = pair_with_x;

        return res;
    }

    function verify_initial(
        PartialVerifierState memory state,
        Proof memory proof,
        VerificationKey memory vk
    ) internal view returns (bool) {
        require(proof.input_values.length == vk.num_inputs);
        require(vk.num_inputs >= 1);
        TranscriptLibrary.Transcript memory transcript = TranscriptLibrary.new_transcript();
        for (uint256 i = 0; i < vk.num_inputs; i++) {
            transcript.update_with_u256(proof.input_values[i]);
        }

        for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
            transcript.update_with_g1(proof.wire_commitments[i]);
        }

        state.beta = transcript.get_challenge();
        state.gamma = transcript.get_challenge();

        transcript.update_with_g1(proof.copy_permutation_grand_product_commitment);
        state.alpha = transcript.get_challenge();

        for (uint256 i = 0; i < proof.quotient_poly_commitments.length; i++) {
            transcript.update_with_g1(proof.quotient_poly_commitments[i]);
        }

        state.z = transcript.get_challenge();

        uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
        for (uint256 i = 0; i < lagrange_poly_numbers.length; i++) {
            lagrange_poly_numbers[i] = i;
        }

        state.cached_lagrange_evals = batch_evaluate_lagrange_poly_out_of_domain(
            lagrange_poly_numbers,
            vk.domain_size,
            vk.omega,
            state.z
        );

        bool valid = verify_at_z(state, proof, vk);

        if (valid == false) {
            return false;
        }

        transcript.update_with_fr(proof.quotient_polynomial_at_z);

        for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
            transcript.update_with_fr(proof.wire_values_at_z[i]);
        }

        for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
            transcript.update_with_fr(proof.wire_values_at_z_omega[i]);
        }

        transcript.update_with_fr(proof.gate_selector_values_at_z[0]);

        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            transcript.update_with_fr(proof.permutation_polynomials_at_z[i]);
        }

        transcript.update_with_fr(proof.copy_grand_product_at_z_omega);
        transcript.update_with_fr(proof.linearization_polynomial_at_z);

        state.v = transcript.get_challenge();
        transcript.update_with_g1(proof.opening_at_z_proof);
        transcript.update_with_g1(proof.opening_at_z_omega_proof);
        state.u = transcript.get_challenge();

        return true;
    }

    // This verifier is for a PLONK with a state width 4
    // and main gate equation
    // q_a(X) * a(X) +
    // q_b(X) * b(X) +
    // q_c(X) * c(X) +
    // q_d(X) * d(X) +
    // q_m(X) * a(X) * b(X) +
    // q_constants(X)+
    // q_d_next(X) * d(X*omega)
    // where q_{}(X) are selectors a, b, c, d - state (witness) polynomials
    // q_d_next(X) "peeks" into the next row of the trace, so it takes
    // the same d(X) polynomial, but shifted

    function aggregate_for_verification(Proof memory proof, VerificationKey memory vk)
        internal
        view
        returns (bool valid, PairingsBn254.G1Point[2] memory part)
    {
        PartialVerifierState memory state;

        valid = verify_initial(state, proof, vk);

        if (valid == false) {
            return (valid, part);
        }

        part = aggregate_commitments(state, proof, vk);

        (valid, part);
    }

    function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) {
        (bool valid, PairingsBn254.G1Point[2] memory recursive_proof_part) = aggregate_for_verification(proof, vk);
        if (valid == false) {
            return false;
        }

        valid = PairingsBn254.pairingProd2(
            recursive_proof_part[0],
            PairingsBn254.P2(),
            recursive_proof_part[1],
            vk.g2_x
        );

        return valid;
    }

    function verify_recursive(
        Proof memory proof,
        VerificationKey memory vk,
        uint256 recursive_vks_root,
        uint8 max_valid_index,
        uint8[] memory recursive_vks_indexes,
        uint256[] memory individual_vks_inputs,
        uint256[16] memory subproofs_limbs
    ) internal view returns (bool) {
        (uint256 recursive_input, PairingsBn254.G1Point[2] memory aggregated_g1s) = reconstruct_recursive_public_input(
            recursive_vks_root,
            max_valid_index,
            recursive_vks_indexes,
            individual_vks_inputs,
            subproofs_limbs
        );

        assert(recursive_input == proof.input_values[0]);

        (bool valid, PairingsBn254.G1Point[2] memory recursive_proof_part) = aggregate_for_verification(proof, vk);
        if (valid == false) {
            return false;
        }

        // aggregated_g1s = inner
        // recursive_proof_part = outer
        PairingsBn254.G1Point[2] memory combined = combine_inner_and_outer(aggregated_g1s, recursive_proof_part);

        valid = PairingsBn254.pairingProd2(combined[0], PairingsBn254.P2(), combined[1], vk.g2_x);

        return valid;
    }

    function combine_inner_and_outer(PairingsBn254.G1Point[2] memory inner, PairingsBn254.G1Point[2] memory outer)
        internal
        view
        returns (PairingsBn254.G1Point[2] memory result)
    {
        // reuse the transcript primitive
        TranscriptLibrary.Transcript memory transcript = TranscriptLibrary.new_transcript();
        transcript.update_with_g1(inner[0]);
        transcript.update_with_g1(inner[1]);
        transcript.update_with_g1(outer[0]);
        transcript.update_with_g1(outer[1]);
        PairingsBn254.Fr memory challenge = transcript.get_challenge();
        // 1 * inner + challenge * outer
        result[0] = PairingsBn254.copy_g1(inner[0]);
        result[1] = PairingsBn254.copy_g1(inner[1]);
        PairingsBn254.G1Point memory tmp = outer[0].point_mul(challenge);
        result[0].point_add_assign(tmp);
        tmp = outer[1].point_mul(challenge);
        result[1].point_add_assign(tmp);

        return result;
    }

    function reconstruct_recursive_public_input(
        uint256 recursive_vks_root,
        uint8 max_valid_index,
        uint8[] memory recursive_vks_indexes,
        uint256[] memory individual_vks_inputs,
        uint256[16] memory subproofs_aggregated
    ) internal pure returns (uint256 recursive_input, PairingsBn254.G1Point[2] memory reconstructed_g1s) {
        assert(recursive_vks_indexes.length == individual_vks_inputs.length);
        bytes memory concatenated = abi.encodePacked(recursive_vks_root);
        uint8 index;
        for (uint256 i = 0; i < recursive_vks_indexes.length; i++) {
            index = recursive_vks_indexes[i];
            assert(index <= max_valid_index);
            concatenated = abi.encodePacked(concatenated, index);
        }
        uint256 input;
        for (uint256 i = 0; i < recursive_vks_indexes.length; i++) {
            input = individual_vks_inputs[i];
            assert(input < r_mod);
            concatenated = abi.encodePacked(concatenated, input);
        }

        concatenated = abi.encodePacked(concatenated, subproofs_aggregated);

        bytes32 commitment = sha256(concatenated);
        recursive_input = uint256(commitment) & RECURSIVE_CIRCUIT_INPUT_COMMITMENT_MASK;

        reconstructed_g1s[0] = PairingsBn254.new_g1_checked(
            subproofs_aggregated[0] +
                (subproofs_aggregated[1] << LIMB_WIDTH) +
                (subproofs_aggregated[2] << (2 * LIMB_WIDTH)) +
                (subproofs_aggregated[3] << (3 * LIMB_WIDTH)),
            subproofs_aggregated[4] +
                (subproofs_aggregated[5] << LIMB_WIDTH) +
                (subproofs_aggregated[6] << (2 * LIMB_WIDTH)) +
                (subproofs_aggregated[7] << (3 * LIMB_WIDTH))
        );

        reconstructed_g1s[1] = PairingsBn254.new_g1_checked(
            subproofs_aggregated[8] +
                (subproofs_aggregated[9] << LIMB_WIDTH) +
                (subproofs_aggregated[10] << (2 * LIMB_WIDTH)) +
                (subproofs_aggregated[11] << (3 * LIMB_WIDTH)),
            subproofs_aggregated[12] +
                (subproofs_aggregated[13] << LIMB_WIDTH) +
                (subproofs_aggregated[14] << (2 * LIMB_WIDTH)) +
                (subproofs_aggregated[15] << (3 * LIMB_WIDTH))
        );

        return (recursive_input, reconstructed_g1s);
    }
}

contract VerifierWithDeserialize is Plonk4VerifierWithAccessToDNext {
    uint256 constant SERIALIZED_PROOF_LENGTH = 34;

    function deserialize_proof(uint256[] memory public_inputs, uint256[] memory serialized_proof)
        internal
        pure
        returns (Proof memory proof)
    {
        require(serialized_proof.length == SERIALIZED_PROOF_LENGTH);
        proof.input_values = new uint256[](public_inputs.length);
        for (uint256 i = 0; i < public_inputs.length; i++) {
            proof.input_values[i] = public_inputs[i];
        }

        uint256 j = 0;
        for (uint256 i = 0; i < STATE_WIDTH; i++) {
            proof.wire_commitments[i] = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);

            j += 2;
        }

        proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked(
            serialized_proof[j],
            serialized_proof[j + 1]
        );
        j += 2;

        for (uint256 i = 0; i < STATE_WIDTH; i++) {
            proof.quotient_poly_commitments[i] = PairingsBn254.new_g1_checked(
                serialized_proof[j],
                serialized_proof[j + 1]
            );

            j += 2;
        }

        for (uint256 i = 0; i < STATE_WIDTH; i++) {
            proof.wire_values_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);

            j += 1;
        }

        for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
            proof.wire_values_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]);

            j += 1;
        }

        for (uint256 i = 0; i < proof.gate_selector_values_at_z.length; i++) {
            proof.gate_selector_values_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);

            j += 1;
        }

        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            proof.permutation_polynomials_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);

            j += 1;
        }

        proof.copy_grand_product_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);

        j += 1;

        proof.quotient_polynomial_at_z = PairingsBn254.new_fr(serialized_proof[j]);

        j += 1;

        proof.linearization_polynomial_at_z = PairingsBn254.new_fr(serialized_proof[j]);

        j += 1;

        proof.opening_at_z_proof = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
        j += 2;

        proof.opening_at_z_omega_proof = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
    }

    function verify_serialized_proof(
        uint256[] memory public_inputs,
        uint256[] memory serialized_proof,
        VerificationKey memory vk
    ) public view returns (bool) {
        require(vk.num_inputs == public_inputs.length);

        Proof memory proof = deserialize_proof(public_inputs, serialized_proof);

        bool valid = verify(proof, vk);

        return valid;
    }

    function verify_serialized_proof_with_recursion(
        uint256[] memory public_inputs,
        uint256[] memory serialized_proof,
        uint256 recursive_vks_root,
        uint8 max_valid_index,
        uint8[] memory recursive_vks_indexes,
        uint256[] memory individual_vks_inputs,
        uint256[16] memory subproofs_limbs,
        VerificationKey memory vk
    ) public view returns (bool) {
        require(vk.num_inputs == public_inputs.length);

        Proof memory proof = deserialize_proof(public_inputs, serialized_proof);

        bool valid = verify_recursive(
            proof,
            vk,
            recursive_vks_root,
            max_valid_index,
            recursive_vks_indexes,
            individual_vks_inputs,
            subproofs_limbs
        );

        return valid;
    }
}

contract Plonk4VerifierWithAccessToDNextOld {
    using PairingsBn254 for PairingsBn254.G1Point;
    using PairingsBn254 for PairingsBn254.G2Point;
    using PairingsBn254 for PairingsBn254.Fr;

    using TranscriptLibrary for TranscriptLibrary.Transcript;

    uint256 constant STATE_WIDTH_OLD = 4;
    uint256 constant ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP_OLD = 1;

    struct VerificationKeyOld {
        uint256 domain_size;
        uint256 num_inputs;
        PairingsBn254.Fr omega;
        PairingsBn254.G1Point[STATE_WIDTH_OLD + 2] selector_commitments; // STATE_WIDTH for witness + multiplication + constant
        PairingsBn254.G1Point[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP_OLD] next_step_selector_commitments;
        PairingsBn254.G1Point[STATE_WIDTH_OLD] permutation_commitments;
        PairingsBn254.Fr[STATE_WIDTH_OLD - 1] permutation_non_residues;
        PairingsBn254.G2Point g2_x;
    }

    struct ProofOld {
        uint256[] input_values;
        PairingsBn254.G1Point[STATE_WIDTH_OLD] wire_commitments;
        PairingsBn254.G1Point grand_product_commitment;
        PairingsBn254.G1Point[STATE_WIDTH_OLD] quotient_poly_commitments;
        PairingsBn254.Fr[STATE_WIDTH_OLD] wire_values_at_z;
        PairingsBn254.Fr[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP_OLD] wire_values_at_z_omega;
        PairingsBn254.Fr grand_product_at_z_omega;
        PairingsBn254.Fr quotient_polynomial_at_z;
        PairingsBn254.Fr linearization_polynomial_at_z;
        PairingsBn254.Fr[STATE_WIDTH_OLD - 1] permutation_polynomials_at_z;
        PairingsBn254.G1Point opening_at_z_proof;
        PairingsBn254.G1Point opening_at_z_omega_proof;
    }

    struct PartialVerifierStateOld {
        PairingsBn254.Fr alpha;
        PairingsBn254.Fr beta;
        PairingsBn254.Fr gamma;
        PairingsBn254.Fr v;
        PairingsBn254.Fr u;
        PairingsBn254.Fr z;
        PairingsBn254.Fr[] cached_lagrange_evals;
    }

    function evaluate_lagrange_poly_out_of_domain_old(
        uint256 poly_num,
        uint256 domain_size,
        PairingsBn254.Fr memory omega,
        PairingsBn254.Fr memory at
    ) internal view returns (PairingsBn254.Fr memory res) {
        require(poly_num < domain_size);
        PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
        PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
        res = at.pow(domain_size);
        res.sub_assign(one);
        require(res.value != 0); // Vanishing polynomial can not be zero at point `at`
        res.mul_assign(omega_power);

        PairingsBn254.Fr memory den = PairingsBn254.copy(at);
        den.sub_assign(omega_power);
        den.mul_assign(PairingsBn254.new_fr(domain_size));

        den = den.inverse();

        res.mul_assign(den);
    }

    function batch_evaluate_lagrange_poly_out_of_domain_old(
        uint256[] memory poly_nums,
        uint256 domain_size,
        PairingsBn254.Fr memory omega,
        PairingsBn254.Fr memory at
    ) internal view returns (PairingsBn254.Fr[] memory res) {
        PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
        PairingsBn254.Fr memory tmp_1 = PairingsBn254.new_fr(0);
        PairingsBn254.Fr memory tmp_2 = PairingsBn254.new_fr(domain_size);
        PairingsBn254.Fr memory vanishing_at_z = at.pow(domain_size);
        vanishing_at_z.sub_assign(one);
        // we can not have random point z be in domain
        require(vanishing_at_z.value != 0);
        PairingsBn254.Fr[] memory nums = new PairingsBn254.Fr[](poly_nums.length);
        PairingsBn254.Fr[] memory dens = new PairingsBn254.Fr[](poly_nums.length);
        // numerators in a form omega^i * (z^n - 1)
        // denoms in a form (z - omega^i) * N
        for (uint256 i = 0; i < poly_nums.length; i++) {
            tmp_1 = omega.pow(poly_nums[i]); // power of omega
            nums[i].assign(vanishing_at_z);
            nums[i].mul_assign(tmp_1);

            dens[i].assign(at); // (X - omega^i) * N
            dens[i].sub_assign(tmp_1);
            dens[i].mul_assign(tmp_2); // mul by domain size
        }

        PairingsBn254.Fr[] memory partial_products = new PairingsBn254.Fr[](poly_nums.length);
        partial_products[0].assign(PairingsBn254.new_fr(1));
        for (uint256 i = 1; i < dens.length - 1; i++) {
            partial_products[i].assign(dens[i - 1]);
            partial_products[i].mul_assign(dens[i]);
        }

        tmp_2.assign(partial_products[partial_products.length - 1]);
        tmp_2.mul_assign(dens[dens.length - 1]);
        tmp_2 = tmp_2.inverse(); // tmp_2 contains a^-1 * b^-1 (with! the last one)

        for (uint256 i = dens.length - 1; i < dens.length; i--) {
            dens[i].assign(tmp_2); // all inversed
            dens[i].mul_assign(partial_products[i]); // clear lowest terms
            tmp_2.mul_assign(dens[i]);
        }

        for (uint256 i = 0; i < nums.length; i++) {
            nums[i].mul_assign(dens[i]);
        }

        return nums;
    }

    function evaluate_vanishing_old(uint256 domain_size, PairingsBn254.Fr memory at)
        internal
        view
        returns (PairingsBn254.Fr memory res)
    {
        res = at.pow(domain_size);
        res.sub_assign(PairingsBn254.new_fr(1));
    }

    function verify_at_z(
        PartialVerifierStateOld memory state,
        ProofOld memory proof,
        VerificationKeyOld memory vk
    ) internal view returns (bool) {
        PairingsBn254.Fr memory lhs = evaluate_vanishing_old(vk.domain_size, state.z);
        require(lhs.value != 0); // we can not check a polynomial relationship if point `z` is in the domain
        lhs.mul_assign(proof.quotient_polynomial_at_z);

        PairingsBn254.Fr memory quotient_challenge = PairingsBn254.new_fr(1);
        PairingsBn254.Fr memory rhs = PairingsBn254.copy(proof.linearization_polynomial_at_z);

        // public inputs
        PairingsBn254.Fr memory tmp = PairingsBn254.new_fr(0);
        for (uint256 i = 0; i < proof.input_values.length; i++) {
            tmp.assign(state.cached_lagrange_evals[i]);
            tmp.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
            rhs.add_assign(tmp);
        }

        quotient_challenge.mul_assign(state.alpha);

        PairingsBn254.Fr memory z_part = PairingsBn254.copy(proof.grand_product_at_z_omega);
        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            tmp.assign(proof.permutation_polynomials_at_z[i]);
            tmp.mul_assign(state.beta);
            tmp.add_assign(state.gamma);
            tmp.add_assign(proof.wire_values_at_z[i]);

            z_part.mul_assign(tmp);
        }

        tmp.assign(state.gamma);
        // we need a wire value of the last polynomial in enumeration
        tmp.add_assign(proof.wire_values_at_z[STATE_WIDTH_OLD - 1]);

        z_part.mul_assign(tmp);
        z_part.mul_assign(quotient_challenge);

        rhs.sub_assign(z_part);

        quotient_challenge.mul_assign(state.alpha);

        tmp.assign(state.cached_lagrange_evals[0]);
        tmp.mul_assign(quotient_challenge);

        rhs.sub_assign(tmp);

        return lhs.value == rhs.value;
    }

    function reconstruct_d(
        PartialVerifierStateOld memory state,
        ProofOld memory proof,
        VerificationKeyOld memory vk
    ) internal view returns (PairingsBn254.G1Point memory res) {
        // we compute what power of v is used as a delinearization factor in batch opening of
        // commitments. Let's label W(x) = 1 / (x - z) *
        // [
        // t_0(x) + z^n * t_1(x) + z^2n * t_2(x) + z^3n * t_3(x) - t(z)
        // + v (r(x) - r(z))
        // + v^{2..5} * (witness(x) - witness(z))
        // + v^(6..8) * (permutation(x) - permutation(z))
        // ]
        // W'(x) = 1 / (x - z*omega) *
        // [
        // + v^9 (z(x) - z(z*omega)) <- we need this power
        // + v^10 * (d(x) - d(z*omega))
        // ]
        //
        // we pay a little for a few arithmetic operations to not introduce another constant
        uint256 power_for_z_omega_opening = 1 + 1 + STATE_WIDTH_OLD + STATE_WIDTH_OLD - 1;
        res = PairingsBn254.copy_g1(vk.selector_commitments[STATE_WIDTH_OLD + 1]);

        PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();
        PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(0);

        // addition gates
        for (uint256 i = 0; i < STATE_WIDTH_OLD; i++) {
            tmp_g1 = vk.selector_commitments[i].point_mul(proof.wire_values_at_z[i]);
            res.point_add_assign(tmp_g1);
        }

        // multiplication gate
        tmp_fr.assign(proof.wire_values_at_z[0]);
        tmp_fr.mul_assign(proof.wire_values_at_z[1]);
        tmp_g1 = vk.selector_commitments[STATE_WIDTH_OLD].point_mul(tmp_fr);
        res.point_add_assign(tmp_g1);

        // d_next
        tmp_g1 = vk.next_step_selector_commitments[0].point_mul(proof.wire_values_at_z_omega[0]);
        res.point_add_assign(tmp_g1);

        // z * non_res * beta + gamma + a
        PairingsBn254.Fr memory grand_product_part_at_z = PairingsBn254.copy(state.z);
        grand_product_part_at_z.mul_assign(state.beta);
        grand_product_part_at_z.add_assign(proof.wire_values_at_z[0]);
        grand_product_part_at_z.add_assign(state.gamma);
        for (uint256 i = 0; i < vk.permutation_non_residues.length; i++) {
            tmp_fr.assign(state.z);
            tmp_fr.mul_assign(vk.permutation_non_residues[i]);
            tmp_fr.mul_assign(state.beta);
            tmp_fr.add_assign(state.gamma);
            tmp_fr.add_assign(proof.wire_values_at_z[i + 1]);

            grand_product_part_at_z.mul_assign(tmp_fr);
        }

        grand_product_part_at_z.mul_assign(state.alpha);

        tmp_fr.assign(state.cached_lagrange_evals[0]);
        tmp_fr.mul_assign(state.alpha);
        tmp_fr.mul_assign(state.alpha);

        grand_product_part_at_z.add_assign(tmp_fr);

        PairingsBn254.Fr memory grand_product_part_at_z_omega = state.v.pow(power_for_z_omega_opening);
        grand_product_part_at_z_omega.mul_assign(state.u);

        PairingsBn254.Fr memory last_permutation_part_at_z = PairingsBn254.new_fr(1);
        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            tmp_fr.assign(state.beta);
            tmp_fr.mul_assign(proof.permutation_polynomials_at_z[i]);
            tmp_fr.add_assign(state.gamma);
            tmp_fr.add_assign(proof.wire_values_at_z[i]);

            last_permutation_part_at_z.mul_assign(tmp_fr);
        }

        last_permutation_part_at_z.mul_assign(state.beta);
        last_permutation_part_at_z.mul_assign(proof.grand_product_at_z_omega);
        last_permutation_part_at_z.mul_assign(state.alpha);

        // add to the linearization
        tmp_g1 = proof.grand_product_commitment.point_mul(grand_product_part_at_z);
        tmp_g1.point_sub_assign(vk.permutation_commitments[STATE_WIDTH_OLD - 1].point_mul(last_permutation_part_at_z));

        res.point_add_assign(tmp_g1);
        res.point_mul_assign(state.v);

        res.point_add_assign(proof.grand_product_commitment.point_mul(grand_product_part_at_z_omega));
    }

    function verify_commitments(
        PartialVerifierStateOld memory state,
        ProofOld memory proof,
        VerificationKeyOld memory vk
    ) internal view returns (bool) {
        PairingsBn254.G1Point memory d = reconstruct_d(state, proof, vk);

        PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);

        PairingsBn254.G1Point memory tmp_g1 = PairingsBn254.P1();

        PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);

        PairingsBn254.G1Point memory commitment_aggregation = PairingsBn254.copy_g1(proof.quotient_poly_commitments[0]);
        PairingsBn254.Fr memory tmp_fr = PairingsBn254.new_fr(1);
        for (uint256 i = 1; i < proof.quotient_poly_commitments.length; i++) {
            tmp_fr.mul_assign(z_in_domain_size);
            tmp_g1 = proof.quotient_poly_commitments[i].point_mul(tmp_fr);
            commitment_aggregation.point_add_assign(tmp_g1);
        }

        aggregation_challenge.mul_assign(state.v);
        commitment_aggregation.point_add_assign(d);

        for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
            aggregation_challenge.mul_assign(state.v);
            tmp_g1 = proof.wire_commitments[i].point_mul(aggregation_challenge);
            commitment_aggregation.point_add_assign(tmp_g1);
        }

        for (uint256 i = 0; i < vk.permutation_commitments.length - 1; i++) {
            aggregation_challenge.mul_assign(state.v);
            tmp_g1 = vk.permutation_commitments[i].point_mul(aggregation_challenge);
            commitment_aggregation.point_add_assign(tmp_g1);
        }

        aggregation_challenge.mul_assign(state.v);

        aggregation_challenge.mul_assign(state.v);

        tmp_fr.assign(aggregation_challenge);
        tmp_fr.mul_assign(state.u);
        tmp_g1 = proof.wire_commitments[STATE_WIDTH_OLD - 1].point_mul(tmp_fr);
        commitment_aggregation.point_add_assign(tmp_g1);

        // collect opening values
        aggregation_challenge = PairingsBn254.new_fr(1);

        PairingsBn254.Fr memory aggregated_value = PairingsBn254.copy(proof.quotient_polynomial_at_z);

        aggregation_challenge.mul_assign(state.v);

        tmp_fr.assign(proof.linearization_polynomial_at_z);
        tmp_fr.mul_assign(aggregation_challenge);
        aggregated_value.add_assign(tmp_fr);

        for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
            aggregation_challenge.mul_assign(state.v);

            tmp_fr.assign(proof.wire_values_at_z[i]);
            tmp_fr.mul_assign(aggregation_challenge);
            aggregated_value.add_assign(tmp_fr);
        }

        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            aggregation_challenge.mul_assign(state.v);

            tmp_fr.assign(proof.permutation_polynomials_at_z[i]);
            tmp_fr.mul_assign(aggregation_challenge);
            aggregated_value.add_assign(tmp_fr);
        }

        aggregation_challenge.mul_assign(state.v);

        tmp_fr.assign(proof.grand_product_at_z_omega);
        tmp_fr.mul_assign(aggregation_challenge);
        tmp_fr.mul_assign(state.u);
        aggregated_value.add_assign(tmp_fr);

        aggregation_challenge.mul_assign(state.v);

        tmp_fr.assign(proof.wire_values_at_z_omega[0]);
        tmp_fr.mul_assign(aggregation_challenge);
        tmp_fr.mul_assign(state.u);
        aggregated_value.add_assign(tmp_fr);

        commitment_aggregation.point_sub_assign(PairingsBn254.P1().point_mul(aggregated_value));

        PairingsBn254.G1Point memory pair_with_generator = commitment_aggregation;
        pair_with_generator.point_add_assign(proof.opening_at_z_proof.point_mul(state.z));

        tmp_fr.assign(state.z);
        tmp_fr.mul_assign(vk.omega);
        tmp_fr.mul_assign(state.u);
        pair_with_generator.point_add_assign(proof.opening_at_z_omega_proof.point_mul(tmp_fr));

        PairingsBn254.G1Point memory pair_with_x = proof.opening_at_z_omega_proof.point_mul(state.u);
        pair_with_x.point_add_assign(proof.opening_at_z_proof);
        pair_with_x.negate();

        return PairingsBn254.pairingProd2(pair_with_generator, PairingsBn254.P2(), pair_with_x, vk.g2_x);
    }

    function verify_initial(
        PartialVerifierStateOld memory state,
        ProofOld memory proof,
        VerificationKeyOld memory vk
    ) internal view returns (bool) {
        require(proof.input_values.length == vk.num_inputs);
        require(vk.num_inputs >= 1);
        TranscriptLibrary.Transcript memory transcript = TranscriptLibrary.new_transcript();
        for (uint256 i = 0; i < vk.num_inputs; i++) {
            transcript.update_with_u256(proof.input_values[i]);
        }

        for (uint256 i = 0; i < proof.wire_commitments.length; i++) {
            transcript.update_with_g1(proof.wire_commitments[i]);
        }

        state.beta = transcript.get_challenge();
        state.gamma = transcript.get_challenge();

        transcript.update_with_g1(proof.grand_product_commitment);
        state.alpha = transcript.get_challenge();

        for (uint256 i = 0; i < proof.quotient_poly_commitments.length; i++) {
            transcript.update_with_g1(proof.quotient_poly_commitments[i]);
        }

        state.z = transcript.get_challenge();

        uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
        for (uint256 i = 0; i < lagrange_poly_numbers.length; i++) {
            lagrange_poly_numbers[i] = i;
        }

        state.cached_lagrange_evals = batch_evaluate_lagrange_poly_out_of_domain_old(
            lagrange_poly_numbers,
            vk.domain_size,
            vk.omega,
            state.z
        );

        bool valid = verify_at_z(state, proof, vk);

        if (valid == false) {
            return false;
        }

        for (uint256 i = 0; i < proof.wire_values_at_z.length; i++) {
            transcript.update_with_fr(proof.wire_values_at_z[i]);
        }

        for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
            transcript.update_with_fr(proof.wire_values_at_z_omega[i]);
        }

        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            transcript.update_with_fr(proof.permutation_polynomials_at_z[i]);
        }

        transcript.update_with_fr(proof.quotient_polynomial_at_z);
        transcript.update_with_fr(proof.linearization_polynomial_at_z);
        transcript.update_with_fr(proof.grand_product_at_z_omega);

        state.v = transcript.get_challenge();
        transcript.update_with_g1(proof.opening_at_z_proof);
        transcript.update_with_g1(proof.opening_at_z_omega_proof);
        state.u = transcript.get_challenge();

        return true;
    }

    // This verifier is for a PLONK with a state width 4
    // and main gate equation
    // q_a(X) * a(X) +
    // q_b(X) * b(X) +
    // q_c(X) * c(X) +
    // q_d(X) * d(X) +
    // q_m(X) * a(X) * b(X) +
    // q_constants(X)+
    // q_d_next(X) * d(X*omega)
    // where q_{}(X) are selectors a, b, c, d - state (witness) polynomials
    // q_d_next(X) "peeks" into the next row of the trace, so it takes
    // the same d(X) polynomial, but shifted

    function verify_old(ProofOld memory proof, VerificationKeyOld memory vk) internal view returns (bool) {
        PartialVerifierStateOld memory state;

        bool valid = verify_initial(state, proof, vk);

        if (valid == false) {
            return false;
        }

        valid = verify_commitments(state, proof, vk);

        return valid;
    }
}

contract VerifierWithDeserializeOld is Plonk4VerifierWithAccessToDNextOld {
    uint256 constant SERIALIZED_PROOF_LENGTH_OLD = 33;

    function deserialize_proof_old(uint256[] memory public_inputs, uint256[] memory serialized_proof)
        internal
        pure
        returns (ProofOld memory proof)
    {
        require(serialized_proof.length == SERIALIZED_PROOF_LENGTH_OLD);
        proof.input_values = new uint256[](public_inputs.length);
        for (uint256 i = 0; i < public_inputs.length; i++) {
            proof.input_values[i] = public_inputs[i];
        }

        uint256 j = 0;
        for (uint256 i = 0; i < STATE_WIDTH_OLD; i++) {
            proof.wire_commitments[i] = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);

            j += 2;
        }

        proof.grand_product_commitment = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
        j += 2;

        for (uint256 i = 0; i < STATE_WIDTH_OLD; i++) {
            proof.quotient_poly_commitments[i] = PairingsBn254.new_g1_checked(
                serialized_proof[j],
                serialized_proof[j + 1]
            );

            j += 2;
        }

        for (uint256 i = 0; i < STATE_WIDTH_OLD; i++) {
            proof.wire_values_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);

            j += 1;
        }

        for (uint256 i = 0; i < proof.wire_values_at_z_omega.length; i++) {
            proof.wire_values_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]);

            j += 1;
        }

        proof.grand_product_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);

        j += 1;

        proof.quotient_polynomial_at_z = PairingsBn254.new_fr(serialized_proof[j]);

        j += 1;

        proof.linearization_polynomial_at_z = PairingsBn254.new_fr(serialized_proof[j]);

        j += 1;

        for (uint256 i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
            proof.permutation_polynomials_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);

            j += 1;
        }

        proof.opening_at_z_proof = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
        j += 2;

        proof.opening_at_z_omega_proof = PairingsBn254.new_g1_checked(serialized_proof[j], serialized_proof[j + 1]);
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"upgradeParameters","type":"bytes"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_recursiveInput","type":"uint256[]"},{"internalType":"uint256[]","name":"_proof","type":"uint256[]"},{"internalType":"uint8[]","name":"_vkIndexes","type":"uint8[]"},{"internalType":"uint256[]","name":"_individualVksInputs","type":"uint256[]"},{"internalType":"uint256[16]","name":"_subproofsLimbs","type":"uint256[16]"}],"name":"verifyAggregatedBlockProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_rootHash","type":"bytes32"},{"internalType":"uint32","name":"_accountId","type":"uint32"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint32","name":"_tokenId","type":"uint32"},{"internalType":"uint128","name":"_amount","type":"uint128"},{"internalType":"uint32","name":"_nftCreatorAccountId","type":"uint32"},{"internalType":"address","name":"_nftCreatorAddress","type":"address"},{"internalType":"uint32","name":"_nftSerialId","type":"uint32"},{"internalType":"bytes32","name":"_nftContentHash","type":"bytes32"},{"internalType":"uint256[]","name":"_proof","type":"uint256[]"}],"name":"verifyExitProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"public_inputs","type":"uint256[]"},{"internalType":"uint256[]","name":"serialized_proof","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"domain_size","type":"uint256"},{"internalType":"uint256","name":"num_inputs","type":"uint256"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct PairingsBn254.Fr","name":"omega","type":"tuple"},{"components":[{"internalType":"uint256","name":"X","type":"uint256"},{"internalType":"uint256","name":"Y","type":"uint256"}],"internalType":"struct PairingsBn254.G1Point[7]","name":"gate_setup_commitments","type":"tuple[7]"},{"components":[{"internalType":"uint256","name":"X","type":"uint256"},{"internalType":"uint256","name":"Y","type":"uint256"}],"internalType":"struct PairingsBn254.G1Point[2]","name":"gate_selector_commitments","type":"tuple[2]"},{"components":[{"internalType":"uint256","name":"X","type":"uint256"},{"internalType":"uint256","name":"Y","type":"uint256"}],"internalType":"struct PairingsBn254.G1Point[4]","name":"copy_permutation_commitments","type":"tuple[4]"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct PairingsBn254.Fr[3]","name":"copy_permutation_non_residues","type":"tuple[3]"},{"components":[{"internalType":"uint256[2]","name":"X","type":"uint256[2]"},{"internalType":"uint256[2]","name":"Y","type":"uint256[2]"}],"internalType":"struct PairingsBn254.G2Point","name":"g2_x","type":"tuple"}],"internalType":"struct Plonk4VerifierWithAccessToDNext.VerificationKey","name":"vk","type":"tuple"}],"name":"verify_serialized_proof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"public_inputs","type":"uint256[]"},{"internalType":"uint256[]","name":"serialized_proof","type":"uint256[]"},{"internalType":"uint256","name":"recursive_vks_root","type":"uint256"},{"internalType":"uint8","name":"max_valid_index","type":"uint8"},{"internalType":"uint8[]","name":"recursive_vks_indexes","type":"uint8[]"},{"internalType":"uint256[]","name":"individual_vks_inputs","type":"uint256[]"},{"internalType":"uint256[16]","name":"subproofs_limbs","type":"uint256[16]"},{"components":[{"internalType":"uint256","name":"domain_size","type":"uint256"},{"internalType":"uint256","name":"num_inputs","type":"uint256"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct PairingsBn254.Fr","name":"omega","type":"tuple"},{"components":[{"internalType":"uint256","name":"X","type":"uint256"},{"internalType":"uint256","name":"Y","type":"uint256"}],"internalType":"struct PairingsBn254.G1Point[7]","name":"gate_setup_commitments","type":"tuple[7]"},{"components":[{"internalType":"uint256","name":"X","type":"uint256"},{"internalType":"uint256","name":"Y","type":"uint256"}],"internalType":"struct PairingsBn254.G1Point[2]","name":"gate_selector_commitments","type":"tuple[2]"},{"components":[{"internalType":"uint256","name":"X","type":"uint256"},{"internalType":"uint256","name":"Y","type":"uint256"}],"internalType":"struct PairingsBn254.G1Point[4]","name":"copy_permutation_commitments","type":"tuple[4]"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct PairingsBn254.Fr[3]","name":"copy_permutation_non_residues","type":"tuple[3]"},{"components":[{"internalType":"uint256[2]","name":"X","type":"uint256[2]"},{"internalType":"uint256[2]","name":"Y","type":"uint256[2]"}],"internalType":"struct PairingsBn254.G2Point","name":"g2_x","type":"tuple"}],"internalType":"struct Plonk4VerifierWithAccessToDNext.VerificationKey","name":"vk","type":"tuple"}],"name":"verify_serialized_proof_with_recursion","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b506159f180620000216000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80631d8a5663146100675780632539464514610090578063439fab9114610090578063a830bd60146100a5578063f7e13636146100b8578063fd1bc844146100cb575b600080fd5b61007a61007536600461548d565b6100de565b60405161008791906158fa565b60405180910390f35b6100a361009e3660046156e1565b610117565b005b61007a6100b33660046153cf565b61011b565b61007a6100c63660046155fd565b6101eb565b61007a6100d93660046154ff565b610350565b600083518260200151146100f157600080fd5b60006100fd8585610391565b9050600061010b8285610697565b925050505b9392505050565b5050565b6000805b835181101561016a57600084828151811061013657fe5b602002602001015190506001600160fd1b03811685838151811061015657fe5b60209081029190910101525060010161011f565b50600061017785516106e3565b90506101df888888808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152507f27362ec1b4c43df95bd87dc1b1591227b4095aaeb2e030db431c291daf9865509250600391508a9050898988610350565b98975050505050505050565b60008060028d8d8d8d8d8d8d8d8d60405160200161021199989796959493929190615786565b60408051601f198184030181529082905261022b9161580b565b602060405180830381855afa158015610248573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061026b91906155e5565b60408051600180825281830190925291925060009190602080830190803683370190505090506001600160fd1b038260001c16816000815181106102ab57fe5b60200260200101818152505060006102f68287878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061073c92505050565b905060006103026109b6565b905082518160200151146103315760405162461bcd60e51b815260040161032890615905565b60405180910390fd5b61033b8282610e76565b9450505050509b9a5050505050505050505050565b6000885182602001511461036357600080fd5b600061036f8a8a610391565b9050600061038282858b8b8b8b8b610eaa565b9b9a5050505050505050505050565b610399614ad5565b60228251146103a757600080fd5b82516001600160401b03811180156103be57600080fd5b506040519080825280602002602001820160405280156103e8578160200160208202803683370190505b50815260005b835181101561042e5783818151811061040357fe5b60200260200101518260000151828151811061041b57fe5b60209081029190910101526001016103ee565b506000805b60048110156104935761046f84838151811061044b57fe5b602002602001015185846001018151811061046257fe5b6020026020010151610f47565b8360200151826004811061047f57fe5b602002015260029190910190600101610433565b506104ba8382815181106104a357fe5b602002602001015184836001018151811061046257fe5b604083015260020160005b6004811015610501576104dd84838151811061044b57fe5b836060015182600481106104ed57fe5b6020020152600291909101906001016104c5565b5060005b600481101561054b5761052a84838151811061051d57fe5b6020026020010151611029565b8360800151826004811061053a57fe5b602002015260019182019101610505565b5060005b60018110156105885761056784838151811061051d57fe5b8360a00151826001811061057757fe5b60200201526001918201910161054f565b5060005b60018110156105c5576105a484838151811061051d57fe5b8360c0015182600181106105b457fe5b60200201526001918201910161058c565b5060005b6003811015610603576105e184838151811061051d57fe5b83610140015182600381106105f257fe5b6020020152600191820191016105c9565b5061061383828151811061051d57fe5b8260e0018190525060018101905061063083828151811061051d57fe5b82610100018190525060018101905061064e83828151811061051d57fe5b82610120018190525060018101905061066c8382815181106104a357fe5b82610160018190525060028101905061068a8382815181106104a357fe5b6101808301525092915050565b60008060006106a6858561105d565b9092509050816106bb576000925050506106dd565b80516106d8906106c961109d565b602084015160e088015161115d565b925050505b92915050565b6106eb614b84565b63ffffffff82166001141561070957610702611239565b9050610737565b63ffffffff821660041415610720576107026116a0565b63ffffffff82166008141561073757610702611b07565b919050565b610744614be7565b602182511461075257600080fd5b82516001600160401b038111801561076957600080fd5b50604051908082528060200260200182016040528015610793578160200160208202803683370190505b50815260005b83518110156107d9578381815181106107ae57fe5b6020026020010151826000015182815181106107c657fe5b6020908102919091010152600101610799565b506000805b600481101561081a576107f684838151811061044b57fe5b8360200151826004811061080657fe5b6020020152600291909101906001016107de565b5061082a8382815181106104a357fe5b604083015260020160005b60048110156108715761084d84838151811061044b57fe5b8360600151826004811061085d57fe5b602002015260029190910190600101610835565b5060005b60048110156108ae5761088d84838151811061051d57fe5b8360800151826004811061089d57fe5b602002015260019182019101610875565b5060005b60018110156108eb576108ca84838151811061051d57fe5b8360a0015182600181106108da57fe5b6020020152600191820191016108b2565b506108fb83828151811061051d57fe5b8260c0018190525060018101905061091883828151811061051d57fe5b8260e0018190525060018101905061093583828151811061051d57fe5b61010083015260010160005b600381101561097b5761095984838151811061051d57fe5b836101200151826003811061096a57fe5b602002015260019182019101610941565b5061098b8382815181106104a357fe5b8261014001819052506002810190506109a98382815181106104a357fe5b6101608301525092915050565b6109be614c29565b620800008152600160208201526109f47f0cf1526aaafac6bacbb67d11a4077806b123f767e4b0883d14cc0193568fc082611029565b6040820152610a437f114dd473f77a15b602201577dd4b64a32a783cb32fbc02911e512df6a219695d7f04c68f82a5dd7d0cc90318bdff493b3d552d148ad859c373ffe55275e043c43b611f6a565b606082015152610a937f245e8c882af503cb5421f5135b4295a920ccf68b42ae7fb967f044f54e2aaa297f071322ee387a9ce49fe7ef2edb6e9237203dee49ec47483af85e356b79fb06fd611f6a565b606082015160200152610ae67f0187754ab593b07a420b3b4d215c20ed49acf90fc4c97e4b06e8f5bc0a2eb3f47f0170f9286ce950286a16ea25136c163c0b32019f31b89c256a612d40b863d0b6611f6a565b606082015160400152610b397f0defecfae1d2b9ec9b2ee4d4798c625fa50f6a4ddb7747a7293df0c17fcb90c27f0f91d08fceebf85fb80f12cda78cefa1ee9dbf5cfe7c4f0704b3c6620fa50c55611f6a565b6060828101510152610b8b7f2f7fef3b3fb64af6640f93803a18b3e5ce4e0e60aecd4f924c833fa6fa6da9617f03908fc737113ac7f3529fe3b36efca200c66d1d85d2fc081973214c586de732611f6a565b606082015160800152610bde7f14ce3c0e9b78fc331327249e707f58fa4bb0ed746bdc9c2262ad0cf9056096277f09e64fdac452b424e98fc4a92f7222693d0d84ab48aadd9c46151dbe5f1a34a9611f6a565b606082015160a00152610c317f1d10bfd923c17d9623ec02db00099355b373021432ae1edef69b0f5f461f78d67f24e370a93f65f42888781d0158bb6ef9136c8bbd047d7993b8276bc8df8b640a611f6a565b608082015152610c817f1fd1755ed4d06d91d50db4771d332cfa2bc2ca0e10ac8b77e0d6b73b993e788e7f0bdbf3b7f0d3cffdcf818f1fba18b90914eda59b454bd1858c6c0916b817f883611f6a565b60a082015152610cd17f1f3b8d12ffa2ceb2bb42d232ad2cf11bce3183472b622e11cc841d26f42ad5077f0ce815e32b3bd14311cde210cda1bd351617d539ed3e9d96a8605f364f3a29b0611f6a565b60a082015160200152610d247f123afa8c1cec1956d7330db062498a2a3e3a9862926c02e1228d9cfb63d3c3017f0f5af15ff0a3e35486c541f72956b53ff6d0740384ef6463c866146c1bd2afc8611f6a565b60a082015160400152610d777f01069e38ea6396af1623921101d3d3d14ee46942fb23bf1d110efb994c3ee5737f232a8ce7151e69601a7867f9dcac8e2de4dd8352d119c90bbb0fb84720c02513611f6a565b60a082015160600152610d8a6005611029565b60c082015152610d9a6007611029565b60c082015160200152610dad600a611029565b60c082015160026020020181905250610e6e60405180604001604052807f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181526020017f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081525060405180604001604052807f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe481526020017f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55815250611f88565b60e082015290565b6000610e80614c65565b6000610e8d828686611f90565b905080610e9f576000925050506106dd565b6106d882868661225e565b6000806000610ebc888888888861268c565b915091508960000151600081518110610ed157fe5b60200260200101518214610ee157fe5b600080610eee8c8c61105d565b909250905081610f05576000945050505050610f3c565b6000610f1184836128d5565b8051909150610f3490610f2261109d565b83600160200201518f60e0015161115d565b955050505050505b979650505050505050565b610f4f614cc6565b82158015610f5b575081155b15610f7c5760405180604001604052808481526020018381525090506106dd565b60008051602061597c8339815191528310610f9657600080fd5b60008051602061597c8339815191528210610fb057600080fd5b600060008051602061597c8339815191528384099050600060008051602061597c833981519152858609905060008051602061597c833981519152858209905060008051602061597c83398151915260038208905080821461101157600080fd5b50506040805180820190915292835250602082015290565b611031614ce0565b60008051602061599c833981519152821061104b57600080fd5b50604080516020810190915290815290565b6000611067614cf3565b61106f614c65565b61107a81868661298c565b9250826110875750611096565b611092818686612c21565b9150505b9250929050565b6110a5614d20565b50604080516080810182527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c28183019081527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6060830152815281518083019092527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b82527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60208381019190915281019190915290565b60408051600280825260608201909252600091829190816020015b611180614cc6565b8152602001906001900390816111785750506040805160028082526060820190925291925060009190602082015b6111b6614d20565b8152602001906001900390816111ae57905050905086826000815181106111d957fe5b602002602001018190525084826001815181106111f257fe5b6020026020010181905250858160008151811061120b57fe5b6020026020010181905250838160018151811061122457fe5b6020026020010181905250610f3c82826130cc565b611241614b84565b624000008152600160208201526112777f18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede611029565b60408201526112c67f16782f42f191b0b1841c2b6a42b7f0564af065d04818526df6c3ad41fe35f8da7f125b9c68c0b931578f8a18fd23ce08e7b7c082ad76404ccece796fa9b3ec0cb0611f6a565b6060820151526113167f2511833eee308a3936b23b27c929942a60aa780747bf32143dc183e873144bfd7f1b8d88d78fcc4a36ebe90fbbdc4547442411e0c8d484727d5c7c6eec27ad2df0611f6a565b6060820151602001526113697f2945641d0c5556aa333ef6c8431e24379b73eccbed7ff3e9425cc64aee1e92ed7f25bbf079192cc83f160da9375e7aec3d3d2caac8d831a29b50f5497071fc14c6611f6a565b6060820151604001526113bc7f09b3c361e5895a8e074eb9b9a9e57af59966f0464068460adc3f64e58544afa47f0412a017f775dd05af16cf387a1e822c2a7e0f8b7cfabd0eb4eb0f67b20e4ada611f6a565b606082810151015261140e7f244b30447ab3e56bb5a5a7f0ef8463a4047476ea269735a887b3de568b3401a37f2ba860198d5e6e0fd93355cb5f309e7e4c1113a57222830961999b79b83d700f611f6a565b6060820151608001526114617f0e13af99775bf5555c366e9c8d4af25a2e195807b766b422856525c01a38b12d7f1787389894222dba5371ab55d512460c5205c1baa0421fc877b183025079a472611f6a565b606082015160a001526114b47f233a03f89c094cf39c89020772d9b912bd0c303d211002ee5afc5c59e241f02b7f04fa51fca1b17399bbbf2b99f17bbce6af1f50b085add4c41ac4ea64f65f4674611f6a565b606082015160c001526115077f1ca088ed531e65b722c8b48568359bbe11051b86f1a8e8951eacc615d9faed3b7f074b06c09de93dd79e070a9ded635e21a34d7178e9a670766e8208149c28e339611f6a565b6080820151526115577f2b4c77c0d47676559061b47968a044aec625cb907181457428e5d08df9b27ef87f1c1be561bdc3eba16162886a2943882157f98ed8246f2063028497f1c108fa93611f6a565b6080820151602001526115aa7f238fd7f2cbc3c3e5899483633c78f051e6d6d25f31aaa6b32b863d55b20d641a7f1f9877b625eaae7a084582a2ffce326a6a5558f3efdb3367037098c4ca25a647611f6a565b60a0820151526115fa7f0b126f60653e371f3f2a85301f16e9cf4af04922a2725fc131b17e90e13d0d847f13bc3f0c7475b74591827463943b35cfd05adb7094a79eeeee2067e8e28a8e84611f6a565b60a08201516020015261164d7f06cae3c1e5b43afb4dda3243c99da693a27eba065fd61a873e99e2c85fd227197f14343c6bdcc85b01b053f26aa3c473cb2f24747ba6d6b90b2323b24f3dfd127e611f6a565b60a082015160400152610d777f217564e2c710d050161b57ef2700e1676251a6d457c4b0d94c41a4492d6dcea37f2365779642d63803d0265a7cc666b3af6ad92b7e9ef38d9113db1208b83f0732611f6a565b6116a8614b84565b628000008152600160208201526116de7f1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863611029565b604082015261172d7f1878d6c837a0f16cb055d3a4e79fba0d85de670dacd708dadd55407b0619796d7f0b3282e52a38ecec63ba42710e8d1ad5c8715c7ed07ce217a3eec747a3f37d76611f6a565b60608201515261177d7f07425bcaf480e377886678d5b5432f0945e3fc952126503a7b672dc4b03f2c267f155b8003ea27945bf43fb5f43291f76e2aa361e0ec81550c0af66dcd1dc8077e611f6a565b6060820151602001526117d07f1292b8795f05fc50782ea7303e2b65a7b2f0e1cc3dead51dfa0b9d2183e5d9077f220d344a384ac53f682e1be6c69407a1fadd0a589de36b95ddc4da05693ba679611f6a565b6060820151604001526118237f283412c1942c0cb3fffc935aab313a37510888bd5ae5972d8d67edc2312af8957f1040e655967354e7ae9227c6200c2256cdcbb707e7158b66462aba23d96b8de2611f6a565b60608281015101526118757f2abe282377038904420434202c11a4f849e64babd436b93192d8d9c34d28ce447f19f0ed010326da1cf8ac93a0f73617ab7c9acb30a0c23a26db9ec19ab6a52fcb611f6a565b6060820151608001526118c87f236f01e67b19be0e7487100a14fd04a05a83a5660966ace987c5248f8c8834597f0ebe824fb1e778491bcb8091d2adbc18dceda4fa9ee191b71c5834a71c533c41611f6a565b606082015160a0015261191b7f2ad3c37aa0b1335f6c70d0e10f0a123a28ea012e857df30e3ced524ef6562c717f1b52d7ac4ee6082438deab8ab0f2944c9fd53258de305065f8323a3767dd8234611f6a565b606082015160c0015261196e7f173c39587688a8967e915959df613aecf44ad0c7d2019ec32311bccdf542c78e7f2421a36a67559ed89afbff081cd45b318835e2b0233c047d030abc48b5011c22611f6a565b6080820151526119be7f177d8ef11cac24105d4b38e035b891986d163d9df717fce12d18af324f86d2dc7f02cd01ba1c82c85b4f0f8c7304254de64516857ac4f7bb60f052bb2af98132c5611f6a565b608082015160200152611a117f21da2c0f2b7849d4c44dbc487d370cccbae78fbd979e79575e04b7a983f2f68a7f14ffb806769ccf0d2c692cd93653491966525554d79efc37cfba5a5c08b15039611f6a565b60a082015152611a617f184cc2f37e687a9be2404cd367536f14a505f086fd597cb966c5b753f325adb47f20aaed49755efed4814025ac679570f62b8c98a1b8d977969242c3ffa67884d6611f6a565b60a082015160200152611ab47f0a2dee920031d9cd5ed499dc3cb901657079f6a2dfb0ba389b0181803bb91e247f272ac2a214f46be0ed7d2b4cf125504ef82d929b1c1ec0a81655c66f39403cd1611f6a565b60a082015160400152610d777f07e360365c7a5363389b2d2449b9471754591f01a623fd5553c5cfe6bad19aaf7f1b814914958835ef86de3c26c6c4bdc27e947f38cb0d2bfaa421d66cabfb7d55611f6a565b611b0f614b84565b6301000000815260016020820152611b467f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb611029565b6040820152611b957f1aab46b9aa3adcac623c360e4d075572e3f56f4c75ac3b8663a7b059bd9b18577f166ac39283efa3d6cb36423e83e2360f006e5fa374b454dea5fe92cc50d4193f611f6a565b606082015152611be57f13bce0a7bfbf2e7a81f18e84966c32422446b01f54cc7dc2ad3f64e92fe94cad7f0247234b0cdfd8c95a767f84303c3dd65ce7b15856c2840635d9d4754ba99479611f6a565b606082015160200152611c387f08742bad9a7cbcc9dbb49a25bebce179295d1cf70fd8f9c8e82b8a658ee0b67c7f2a467983257850c5fa27f2f52f0c5c5fc98e7d2e0d440a8fd954ad981ff0ce9f611f6a565b606082015160400152611c8b7f16ebdd4b95b872cd09c13b6b54a8b8bf81a01529a71234db26e3b22c6d6327237f034219d7ad9ef204cfb3e32c4a47af82eea40504c2b1bac785104731722ed617611f6a565b6060828101510152611cdd7f2e3a7c4458a8dc1535e68bac5dd5c1c9ff3886df4156bad4a08fcd08ebf1db267f173859705317db06e5b7d260898ab08e72fae987c272b82345105d72bfd00ab8611f6a565b606082015160800152611d307f0b830132e3325eaaea73c1095e615358db38dfb39248c90f8ff4afde169e76577f0bfedf8cfce7260c16bb1f76ad9a39f73a68087e5c68e841020aeaa5ba301a9f611f6a565b606082015160a00152611d837f1660c850da793add523f7990b983896e50d5549eec7990ec26aabc220ca58d527f0ba698e78dee0d41cf8aefde82c5bfda38be071e11025b56db779ddb40a4fe92611f6a565b606082015160c00152611dd67f024fe4ce02dd48937e4642b66308ae15d731e0ea82fc5430a0470d9a5dab36947f177cac2d79a8bfa6aba134e24bded06d06219979c18b2fa4fe71baea9885985d611f6a565b608082015152611e257ea848bc76c52faf7d4e7cc4086b50e3ccc9b1cebef130ac1bbf1816502df59d7f02f42f326f82b33cb9e4e7cfb332889eec95c2813f7968b3a50d838b3cbfa676611f6a565b608082015160200152611e787f20c176738979e0d1ea9541bf26e6209d3091b618ae94f3c72e13e954a1614f607f2a7019c81009c00a7412b6a303b2eb118a362a558837e9ecdb912589bc11ff83611f6a565b60a082015152611ec87f10a92b3fa2b8280030c9de5cbcab4da3cf9b5b3f63f3ad60284ecded63cc54ea7f1bde2a83db435b8c74e4239b4f8416da88008331a758d8c68a9104f2dfc3e237611f6a565b60a082015160200152611f1b7f08e2e513d1e548a627e2d4f74d28dea916d8598415b70543bb3e92429f0111cb7f2fb46898f77e32d7fd646fe31b60320423aa4698501e329e206b6acfcfb01337611f6a565b60a082015160400152610d777f145b88d324270872b13784fbb7ccdee6e5593d2d5cbc81f4aaa9b4268cfc50947f197d826aaf2a9853ca98ec9c0e55376eec1a6a0f5dbbbe02afeb1b567d8eafa05b611f72614cc6565b5060408051808201909152918252602082015290565b611f72614d20565b602081015182515160009114611fa557600080fd5b600182602001511015611fb757600080fd5b6000611fc16132dd565b905060005b836020015181101561200457611ffc85600001518281518110611fe557fe5b6020026020010151836132f990919063ffffffff16565b600101611fc6565b5060005b6004811015612038576120308560200151826004811061202457fe5b6020020151839061336c565b600101612008565b5061204281613388565b602086015261205081613388565b60408087019190915284015161206790829061336c565b61207081613388565b855260005b6004811015612099576120918560600151826004811061202457fe5b600101612075565b506120a381613388565b60a086015260208301516000906001600160401b03811180156120c557600080fd5b506040519080825280602002602001820160405280156120ef578160200160208202803683370190505b50905060005b815181101561211e578082828151811061210b57fe5b60209081029190910101526001016120f5565b5061213781856000015186604001518960a001516133f9565b60c087015260006121498787876137ae565b90508061215c5760009350505050610110565b60005b600481101561218f576121878760800151826004811061217b57fe5b6020020151859061398e565b60010161215f565b5060005b60018110156121b7576121af8760a00151826001811061217b57fe5b600101612193565b5060005b60038110156121e0576121d8876101200151826003811061217b57fe5b6001016121bb565b5060e08601516121f190849061398e565b61010086015161220290849061398e565b60c086015161221290849061398e565b61221b83613388565b606088015261014086015161223190849061336c565b61016086015161224290849061336c565b61224b83613388565b6080880152506001925050509392505050565b60008061226c85858561399c565b835160a087015191925060009161228291613cfa565b9050600061228e613d78565b9050600061229c6001611029565b905060006122be88606001516000600481106122b457fe5b6020020151613d99565b905060006122cc6001611029565b905060015b6004811015612317576122e48287613db5565b612303828b6060015183600481106122f857fe5b602002015190613dcf565b945061230f8386613de2565b6001016122d1565b5060608a0151612328908490613db5565b6123328287613de2565b60005b60048110156123765760608b015161234e908590613db5565b612362848b6020015183600481106122f857fe5b945061236e8386613de2565b600101612335565b5060005b60038110156123bb5760608b0151612393908590613db5565b6123a7848a60a0015183600481106122f857fe5b94506123b38386613de2565b60010161237a565b5060608a01516123cc908490613db5565b60608a01516123dc908490613db5565b6123e68184613ded565b60808a01516123f6908290613db5565b602089015161240890829060036122f8565b93506124148285613de2565b61241e6001611029565b9250600061242f8a60e00151613df2565b90506124488b6060015185613db590919063ffffffff16565b6101008a0151612459908390613ded565b6124638285613db5565b61246d8183613e01565b60005b60048110156124c45760608c0151612489908690613db5565b6124a88b60800151826004811061249c57fe5b60200201518490613ded565b6124b28386613db5565b6124bc8284613e01565b600101612470565b5060005b60038110156125115760608c01516124e1908690613db5565b6124f58b6101200151826003811061249c57fe5b6124ff8386613db5565b6125098284613e01565b6001016124c8565b5060608b0151612522908590613db5565b60c08a0151612532908390613ded565b61253c8285613db5565b60808b015161254c908390613db5565b6125568183613e01565b60608b0151612566908590613db5565b60a08a015161257e9060005b60200201518390613ded565b6125888285613db5565b60808b0151612598908390613db5565b6125a28183613e01565b6125be6125b7826125b1613d78565b90613dcf565b8490613e1b565b60a08b01516101408b015184916125df916125d891613dcf565b8290613de2565b60a08c01516125ef908490613ded565b60408a01516125ff908490613db5565b60808c015161260f908490613db5565b6101608b0151612623906125d89085613dcf565b60006126418d608001518d6101600151613dcf90919063ffffffff16565b905061265b8c610140015182613de290919063ffffffff16565b61266481613e26565b61267b8261267061109d565b838e60e0015161115d565b9d9c50505050505050505050505050565b6000612696614cf3565b83518551146126a157fe5b6000876040516020016126b49190615896565b60408051601f1981840301815291905290506000805b8751811015612726578781815181106126df57fe5b602002602001015191508860ff168260ff1611156126f957fe5b828260405160200161270c92919061586e565b60408051601f1981840301815291905292506001016126ca565b506000805b885181101561278e5787818151811061274057fe5b6020026020010151915060008051602061599c833981519152821061276157fe5b8382604051602001612774929190615855565b60408051601f19818403018152919052935060010161272b565b5082866040516020016127a2929190615817565b604051602081830303815290604052925060006002846040516127c5919061580b565b602060405180830381855afa1580156127e2573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061280591906155e5565b6060880151604089015160208a01518a5160e08c015160c08d015160a08e01516001600160f81b0388169d5096975061286796604494851b93909301608895861b0160cc96871b01959190911b931b911b8b60045b6020020151010101610f47565b85526101608701516101408801516101208901516101008a01516101e08b01516101c08c01516101a08d01516128bf96604495861b94909401608896871b0160cc94851b01959290931b93911b91901b8b600c61285a565b8560016020020152505050509550959350505050565b6128dd614cf3565b60006128e76132dd565b90506128fc8460005b6020020151829061336c565b6129078460016128f0565b6129128360006128f0565b61291d8360016128f0565b600061292882613388565b90506129358560006122b4565b83526129428560016122b4565b602084015260006129548286836122f8565b9050612969818560005b602002015190613de2565b612975828660016122f8565b90506129838185600161295e565b50505092915050565b6020810151825151600091146129a157600080fd5b6001826020015110156129b357600080fd5b60006129bd6132dd565b905060005b83602001518110156129e9576129e185600001518281518110611fe557fe5b6001016129c2565b5060005b6004811015612a1157612a098560200151826004811061202457fe5b6001016129ed565b50612a1b81613388565b6020860152612a2981613388565b604080870191909152840151612a4090829061336c565b612a4981613388565b855260005b6004811015612a7257612a6a8560600151826004811061202457fe5b600101612a4e565b50612a7c81613388565b60a086015260208301516000906001600160401b0381118015612a9e57600080fd5b50604051908082528060200260200182016040528015612ac8578160200160208202803683370190505b50905060005b8151811015612af75780828281518110612ae457fe5b6020908102919091010152600101612ace565b50612b1081856000015186604001518960a00151613e5c565b60c08701526000612b22878787614105565b905080612b355760009350505050610110565b610100860151612b4690849061398e565b60005b6004811015612b6d57612b658760800151826004811061217b57fe5b600101612b49565b5060005b6001811015612b9557612b8d8760a00151826001811061217b57fe5b600101612b71565b5060c086015151612ba790849061398e565b60005b6003811015612bcf57612bc7876101400151826003811061217b57fe5b600101612baa565b5060e0860151612be090849061398e565b610120860151612bf190849061398e565b612bfa83613388565b6060880152610160860151612c1090849061336c565b61018086015161224290849061336c565b612c29614cf3565b6000612c3685858561434c565b835160a0870151919250600091612c4c91613cfa565b90506000612c58613d78565b90506000612c666001611029565b90506000612c7e88606001516000600481106122b457fe5b90506000612c8c6001611029565b905060015b6004811015612ccc57612ca48287613db5565b612cb8828b6060015183600481106122f857fe5b9450612cc48386613de2565b600101612c91565b5060608a0151612cdd908490613db5565b612ce78287613de2565b60005b6004811015612d2b5760608b0151612d03908590613db5565b612d17848b6020015183600481106122f857fe5b9450612d238386613de2565b600101612cea565b5060005b6001811015612d6e5760608b0151612d48908590613db5565b6080890151612d5a90859060006122f8565b9450612d668386613de2565b600101612d2f565b5060005b6003811015612db35760608b0151612d8b908590613db5565b612d9f848a60a0015183600481106122f857fe5b9450612dab8386613de2565b600101612d72565b5060608a0151612dc4908490613db5565b612dce8184613ded565b60808a0151612dde908290613db5565b6040890151612df890612df19083613dcf565b8390613de2565b60608a0151612e08908490613db5565b612e128184613ded565b60808a0151612e22908290613db5565b6020890151612e3490829060036122f8565b9350612e408285613de2565b612e4a6001611029565b92506000612e5c8a6101000151613df2565b9050612e758b6060015185613db590919063ffffffff16565b6101208a0151612e86908390613ded565b612e908285613db5565b612e9a8183613e01565b60005b6004811015612ee55760608c0151612eb6908690613db5565b612ec98b60800151826004811061249c57fe5b612ed38386613db5565b612edd8284613e01565b600101612e9d565b5060005b6001811015612f315760608c0151612f02908690613db5565b612f158b60c00151826001811061249c57fe5b612f1f8386613db5565b612f298284613e01565b600101612ee9565b5060005b6003811015612f7e5760608c0151612f4e908690613db5565b612f628b6101400151826003811061249c57fe5b612f6c8386613db5565b612f768284613e01565b600101612f35565b5060608b0151612f8f908590613db5565b60e08a0151612f9f908390613ded565b612fa98285613db5565b60808b0151612fb9908390613db5565b612fc38183613e01565b60608b0151612fd3908590613db5565b60a08a0151612fe3906000612572565b612fed8285613db5565b60808b0151612ffd908390613db5565b6130078183613e01565b6130166125b7826125b1613d78565b60a08b01516101608b01518491613030916125d891613dcf565b60a08c0151613040908490613ded565b60408a0151613050908490613db5565b60808c0151613060908490613db5565b6101808b0151613074906125d89085613dcf565b60006130928d608001518d6101800151613dcf90919063ffffffff16565b90506130ac8c610160015182613de290919063ffffffff16565b6130b581613e26565b908952602089015250959998505050505050505050565b600081518351146130dc57600080fd5b8251600681026000816001600160401b03811180156130fa57600080fd5b50604051908082528060200260200182016040528015613124578160200160208202803683370190505b50905060005b838110156132a95786818151811061313e57fe5b60200260200101516000015182826006026000018151811061315c57fe5b60200260200101818152505086818151811061317457fe5b60200260200101516020015182826006026001018151811061319257fe5b6020026020010181815250508581815181106131aa57fe5b6020908102919091010151515182518390600260068502019081106131cb57fe5b6020026020010181815250508581815181106131e357fe5b6020908102919091010151516001602002015182826006026003018151811061320857fe5b60200260200101818152505085818151811061322057fe5b60200260200101516020015160006002811061323857fe5b602002015182826006026004018151811061324f57fe5b60200260200101818152505085818151811061326757fe5b60200260200101516020015160016002811061327f57fe5b602002015182826006026005018151811061329657fe5b602090810291909101015260010161312a565b506132b2614d40565b6000602082602086026020860160085afa9050806132cf57600080fd5b505115159695505050505050565b6132e5614d5e565b600080825260208201819052604082015290565b8151602080840151604051613317926000928592909187910161589f565b60408051601f198184030181529181528151602092830120855284820151905161334a926001928592909187910161589f565b60408051601f1981840301815291905280516020918201209301929092525050565b61337a8282600001516132f9565b6101178282602001516132f9565b613390614ce0565b600060028360000151846020015185604001516040516020016133b694939291906158c9565b60408051808303601f190181529181528151602092830120948101805160010163ffffffff169052805191820190526001600160fd1b0390931683525090919050565b606060006134076001611029565b905060006134156000611029565b9050600061342287611029565b905060006134308689613cfa565b905061343c81856146a6565b805161344757600080fd5b600089516001600160401b038111801561346057600080fd5b5060405190808252806020026020018201604052801561349a57816020015b613487614ce0565b81526020019060019003908161347f5790505b50905060008a516001600160401b03811180156134b657600080fd5b506040519080825280602002602001820160405280156134f057816020015b6134dd614ce0565b8152602001906001900390816134d55790505b50905060005b8b518110156135c1576135258c828151811061350e57fe5b60200260200101518b613cfa90919063ffffffff16565b955061354d8484838151811061353757fe5b6020026020010151613ded90919063ffffffff16565b6135738684838151811061355d57fe5b6020026020010151613db590919063ffffffff16565b6135838983838151811061353757fe5b6135a98683838151811061359357fe5b60200260200101516146a690919063ffffffff16565b6135b98583838151811061355d57fe5b6001016134f6565b5060008b516001600160401b03811180156135db57600080fd5b5060405190808252806020026020018201604052801561361557816020015b613602614ce0565b8152602001906001900390816135fa5790505b5090506136326136256001611029565b8260008151811061353757fe5b60015b60018351038110156136925761366783600183038151811061365357fe5b602002602001015183838151811061353757fe5b61368a83828151811061367657fe5b602002602001015183838151811061355d57fe5b600101613635565b506136bd816001835103815181106136a657fe5b602002602001015186613ded90919063ffffffff16565b6136e7826001845103815181106136d057fe5b602002602001015186613db590919063ffffffff16565b6136f0856146c4565b8251909550600019015b8251811015613765576137138684838151811061353757fe5b61373682828151811061372257fe5b602002602001015184838151811061355d57fe5b61375c83828151811061374557fe5b602002602001015187613db590919063ffffffff16565b600019016136fa565b5060005b835181101561379d5761379583828151811061378157fe5b602002602001015185838151811061355d57fe5b600101613769565b50919b9a5050505050505050505050565b6000806137c383600001518660a00151614701565b80519091506137d157600080fd5b60e08401516137e1908290613db5565b60006137ed6001611029565b905060006137ff866101000151613df2565b9050600061380d6000611029565b905060005b875151811015613875576138468960c00151828151811061382f57fe5b602002602001015183613ded90919063ffffffff16565b61386361385c8960000151838151811061051d57fe5b8390613db5565b61386d8383613e01565b600101613812565b508751613883908490613db5565b60006138928860c00151613df2565b905060005b6003811015613905576138b4896101200151826003811061249c57fe5b60208a01516138c4908490613db5565b60408a01516138d4908490613e01565b6138f3896080015182600481106138e757fe5b60200201518490613e01565b6138fd8284613db5565b600101613897565b506040890151613916908390613ded565b60808801516060015161392a908390613e01565b6139348183613db5565b61393e8185613db5565b61394883826146a6565b8851613955908590613db5565b6139698960c0015160008151811061382f57fe5b6139738285613db5565b61397d83836146a6565b505051915190911495945050505050565b6101178282600001516132f9565b6139a4614cc6565b60608201516009906139b79060056122b4565b915060006139c3613d78565b905060006139d16000611029565b905060005b6004811015613a1b57613a07876080015182600481106139f257fe5b6020020151876060015183600681106122f857fe5b9250613a138584613de2565b6001016139d6565b506080860151613a349060005b60200201518290613ded565b6080860151613a4c9060015b60200201518290613db5565b6060850151613a5e90829060046122f8565b9150613a6a8483613de2565b60a0860151516080860151613a81919060006122f8565b9150613a8d8483613de2565b6000613a9c8860a00151613df2565b9050613ab5886020015182613db590919063ffffffff16565b6080870151613acd9060005b60200201518290613e01565b6040880151613add908290613e01565b60005b6003811015613b605760a0890151613af9908490613ded565b613b188760c001518260038110613b0c57fe5b60200201518490613db5565b6020890151613b28908490613db5565b6040890151613b38908490613e01565b613b4e886080015182600101600481106138e757fe5b613b588284613db5565b600101613ae0565b508751613b6e908290613db5565b613b828860c0015160008151811061382f57fe5b8751613b8f908390613db5565b8751613b9c908390613db5565b613ba68183613e01565b6060880151600090613bb89086613cfa565b9050613bd1896080015182613db590919063ffffffff16565b6000613bdd6001611029565b905060005b6003811015613c5c5760208b0151613bfb908690613ded565b613c1b8a61012001518260038110613c0f57fe5b60200201518690613db5565b60408b0151613c2b908690613e01565b613c4a8a608001518260048110613c3e57fe5b60200201518690613e01565b613c548286613db5565b600101613be2565b5060208a0151613c6d908290613db5565b60c0890151613c7d908290613db5565b8951613c8a908290613db5565b6040890151613c999084613dcf565b60a0890151909550613cb990613cb290839060036122f8565b8690613e1b565b613cc38786613de2565b60608a0151613cd3908890614729565b6040890151613ced90613ce69084613dcf565b8890613de2565b5050505050509392505050565b613d02614ce0565b6040805160c081018252602080825280820181905291810191909152835160608201526080810183905260008051602061599c83398151915260a0820152613d48614d40565b600060208260c08560055afa905080613d6057600080fd5b50604080516020810190915290518152949350505050565b613d80614cc6565b5060408051808201909152600181526002602082015290565b613da1614cc6565b815181526020918201519181019190915290565b60008051602061599c833981519152815183510990915250565b613dd7614cc6565b6106dd838383614730565b610117828284614772565b519052565b613dfa614ce0565b9051815290565b60008051602061599c833981519152815183510890915250565b61011782828461480a565b6020810151613e4057805115613e3b57600080fd5b613e59565b60208101805160008051602061597c8339815191520390525b50565b60606000613e6a6001611029565b90506000613e786000611029565b90506000613e8587611029565b90506000613e938689613cfa565b9050613e9f81856146a6565b8051613eaa57600080fd5b600089516001600160401b0381118015613ec357600080fd5b50604051908082528060200260200182016040528015613efd57816020015b613eea614ce0565b815260200190600190039081613ee25790505b50905060008a516001600160401b0381118015613f1957600080fd5b50604051908082528060200260200182016040528015613f5357816020015b613f40614ce0565b815260200190600190039081613f385790505b50905060005b8b51811015613fcb57613f718c828151811061350e57fe5b9550613f838484838151811061353757fe5b613f938684838151811061355d57fe5b613fa38983838151811061353757fe5b613fb38683838151811061359357fe5b613fc38583838151811061355d57fe5b600101613f59565b5060008b516001600160401b0381118015613fe557600080fd5b5060405190808252806020026020018201604052801561401f57816020015b61400c614ce0565b8152602001906001900390816140045790505b50905061402f6136256001611029565b60015b60018351038110156140675761405083600183038151811061365357fe5b61405f83828151811061367657fe5b600101614032565b5061407b816001835103815181106136a657fe5b61408e826001845103815181106136d057fe5b614097856146c4565b8251909550600019015b82518110156140e1576140ba8684838151811061353757fe5b6140c982828151811061372257fe5b6140d883828151811061374557fe5b600019016140a1565b5060005b835181101561379d576140fd83828151811061378157fe5b6001016140e5565b60008061411a83600001518660a00151614701565b805190915061412857600080fd5b610100840151614139908290613db5565b60006141456001611029565b90506000614157866101200151613df2565b905060006141656000611029565b905060006141736000611029565b905060005b8851518110156141db576141ac8a60c00151828151811061419557fe5b602002602001015184613ded90919063ffffffff16565b6141c96141c28a60000151838151811061051d57fe5b8490613db5565b6141d38284613e01565b600101614178565b5060c08801516141ec906000613a40565b6141f68382613e01565b8851614203908590613db5565b8851614210908590613db5565b885161421d908590613db5565b885161422a908590613db5565b8851614237908590613db5565b60006142468960e00151613df2565b905060005b60038110156142c5576142748a6101400151826003811061426857fe5b60200201518590613ded565b60208b0151614284908590613db5565b60408b0151614294908590613e01565b6142b38a6080015182600481106142a757fe5b60200201518590613e01565b6142bd8285613db5565b60010161424b565b5060408a01516142d6908490613ded565b60808901516142e69060036138e7565b6142f08184613db5565b6142fa8186613db5565b61430484826146a6565b8951614311908690613db5565b6143258a60c0015160008151811061419557fe5b61432f8386613db5565b61433984846146a6565b5050905192519092149695505050505050565b614354614cc6565b60608201516143649060056122b4565b90506000614370613d78565b9050600061437e6000611029565b905060005b60048110156143c8576143b48660800151826004811061439f57fe5b6020020151866060015183600781106122f857fe5b92506143c08484613de2565b600101614383565b5060808501516143d9906000613a28565b60808501516143e9906001613a40565b60608401516143fb90829060046122f8565b91506144078383613de2565b60a085015151606085015161441e919060066122f8565b915061442a8383613de2565b60c08501515161443b908490614729565b60006144476001611029565b90506144548787836148a4565b915061446b8286608001516001600281106122f857fe5b92506144778484613de2565b8651614484908290613db5565b600061448f82613df2565b905060006144a08960a00151613df2565b90506144b9896020015182613db590919063ffffffff16565b60808801516144c9906000613ac1565b60408901516144d9908290613e01565b60005b60038110156145505760a08a01516144f5908690613ded565b6145088860c001518260038110613c0f57fe5b60208a0151614518908690613db5565b60408a0151614528908690613e01565b61453e89608001518260010160048110613c3e57fe5b6145488286613db5565b6001016144dc565b5061455b8183613db5565b8851614568908490613db5565b6145938960c0015160008151811061457c57fe5b602002602001015185613ded90919063ffffffff16565b61459d8484613db5565b6145a78185613e01565b60006145b36001611029565b905060005b60038110156146325760208b01516145d1908790613ded565b6145f18a610140015182600381106145e557fe5b60200201518790613db5565b60408b0151614601908790613e01565b6146208a60800151826004811061461457fe5b60200201518790613e01565b61462a8287613db5565b6001016145b8565b5060208a0151614643908290613db5565b60e0890151614653908290613db5565b61465d8184613db5565b604089015161466c9083613dcf565b60a089015190965061468c9061468590839060036122f8565b8790613e1b565b6146968787613de2565b60608a0151613ced908890614729565b8051825160008051602061599c833981519152918203900890915250565b6146cc614ce0565b81516146d757600080fd5b6106dd827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff613cfa565b614709614ce0565b6147138284613cfa565b90506106dd6147226001611029565b82906146a6565b6101178282845b614738614d7e565b835181526020840151816001602002015282518160026020020152600060408360608460075afa90508061476b57600080fd5b5050505050565b815115801561478357506020820151155b1561479b578251815260208084015190820152614805565b82511580156147ac57506020830151155b156147c4578151815260208083015190820152614805565b6147cc614d9c565b8351815260208085015181830152835160408301528301518160035b6020020152600060408360808460065afa90508061476b57600080fd5b505050565b815115801561481b57506020820151155b15614833578251815260208084015190820152614805565b825115801561484457506020830151155b1561486c578151815260208083015160008051602061597c8339815191520390820152614805565b614874614d9c565b83518152602080850151818301528351604083015283015160008051602061597c833981519152038160036147e8565b6148ac614ce0565b60006148b86001611029565b905060006148c66002611029565b905060006148d46003611029565b905060006148e26004611029565b90506148ee6000611029565b945060006148fc6000611029565b9050600061490a6000611029565b905060006149186000611029565b905060005b6003811015614a05578b51614933908b90613db5565b6149538b60800151826003036004811061494957fe5b6020020151613df2565b935061495f8486613db5565b6149758b60800151826002036004811061494957fe5b925061498183856146a6565b61498a83613df2565b915061499583613df2565b93506149a184896146a6565b6149ab8285613db5565b6149b483613df2565b93506149c084886146a6565b6149ca8285613db5565b6149d383613df2565b93506149df84876146a6565b6149e98285613db5565b6149f3828b613db5565b6149fd8983613e01565b60010161491d565b508a51614a13908a90613db5565b60808a0151614a23906000614949565b9250614a2f8385613db5565b60a08a0151614a3f906000614949565b9150614a4b82846146a6565b614a5482613df2565b9050614a5f82613df2565b9250614a6b83886146a6565b614a758184613db5565b614a7e82613df2565b9250614a8a83876146a6565b614a948184613db5565b614a9d82613df2565b9250614aa983866146a6565b614ab38184613db5565b614abd818a613db5565b614ac78882613e01565b505050505050509392505050565b604051806101a0016040528060608152602001614af0614dba565b8152602001614afd614cc6565b8152602001614b0a614dba565b8152602001614b17614de7565b8152602001614b24614e14565b8152602001614b31614e14565b8152602001614b3e614ce0565b8152602001614b4b614ce0565b8152602001614b58614ce0565b8152602001614b65614e41565b8152602001614b72614cc6565b8152602001614b7f614cc6565b905290565b6040518061010001604052806000815260200160008152602001614ba6614ce0565b8152602001614bb3614e6e565b8152602001614bc0614cf3565b8152602001614bcd614dba565b8152602001614bda614e41565b8152602001614b7f614d20565b60405180610180016040528060608152602001614c02614dba565b8152602001614c0f614cc6565b8152602001614c1c614dba565b8152602001614b24614de7565b6040518061010001604052806000815260200160008152602001614c4b614ce0565b8152602001614c58614e9b565b8152602001614bc0614ec8565b6040518060e00160405280614c78614ce0565b8152602001614c85614ce0565b8152602001614c92614ce0565b8152602001614c9f614ce0565b8152602001614cac614ce0565b8152602001614cb9614ce0565b8152602001606081525090565b604051806040016040528060008152602001600081525090565b6040518060200160405280600081525090565b60405180604001604052806002905b614d0a614cc6565b815260200190600190039081614d025790505090565b6040518060400160405280614d33614ef5565b8152602001614b7f614ef5565b60405180602001604052806001906020820280368337509192915050565b604080516060810182526000808252602082018190529181019190915290565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b60405180608001604052806004905b614dd1614cc6565b815260200190600190039081614dc95790505090565b60405180608001604052806004905b614dfe614ce0565b815260200190600190039081614df65790505090565b60405180602001604052806001905b614e2b614ce0565b815260200190600190039081614e235790505090565b60405180606001604052806003905b614e58614ce0565b815260200190600190039081614e505790505090565b6040518060e001604052806007905b614e85614cc6565b815260200190600190039081614e7d5790505090565b6040518060c001604052806006905b614eb2614cc6565b815260200190600190039081614eaa5790505090565b60405180602001604052806001905b614edf614cc6565b815260200190600190039081614ed75790505090565b60405180604001604052806002906020820280368337509192915050565b6000614f26614f2184615944565b615921565b9050808260408681870286011115614f3d57600080fd5b60005b86811015614f6657614f528884615268565b845260209093019291810191600101614f40565b505050509392505050565b6000614f7f614f2184615944565b9050808260208681870286011115614f9657600080fd5b60005b86811015614f6657823584529281019291810191600101614f99565b80356001600160a01b038116811461073757600080fd5b600082601f830112614fdc578081fd5b604051606081018181106001600160401b0382111715614ff857fe5b60405280836060810186101561500c578384fd5b835b600381101561503657615021878361522e565b8352602092830192919091019060010161500e565b509195945050505050565b600082601f830112615051578081fd5b604080518181018181106001600160401b038211171561506d57fe5b8252808460808101871015615080578485fd5b845b60028110156150a9576150958883615268565b835260209092019190840190600101615082565b50919695505050505050565b600082601f8301126150c5578081fd5b61011083600484614f13565b600082601f8301126150e1578081fd5b61011083600784614f13565b600082601f8301126150fd578081fd5b61011083601084614f71565b600082601f830112615119578081fd5b61011083600284614f71565b60008083601f840112615136578182fd5b5081356001600160401b0381111561514c578182fd5b602083019150836020808302850101111561109657600080fd5b600082601f830112615176578081fd5b81356020615186614f218361595e565b82815281810190858301838502870184018810156151a2578586fd5b855b858110156151c0578135845292840192908401906001016151a4565b5090979650505050505050565b600082601f8301126151dd578081fd5b813560206151ed614f218361595e565b8281528181019085830183850287018401881015615209578586fd5b855b858110156151c05761521c826153be565b8452928401929084019060010161520b565b60006020828403121561523f578081fd5b604051602081018181106001600160401b038211171561525b57fe5b6040529135825250919050565b600060408284031215615279578081fd5b604051604081018181106001600160401b038211171561529557fe5b604052823581526020928301359281019290925250919050565b6000608082840312156152c0578081fd5b604051604081018181106001600160401b03821117156152dc57fe5b6040529050806152ec8484615109565b81526152fb8460408501615109565b60208201525092915050565b60006104808284031215615319578081fd5b615324610100615921565b90508135815260208201356020820152615341836040840161522e565b604082015261535383606084016150d1565b6060820152615366836102208401615041565b6080820152615379836102a084016150b5565b60a082015261538c836103a08401614fcc565b60c082015261539f8361040084016152af565b60e082015292915050565b803563ffffffff8116811461073757600080fd5b803560ff8116811461073757600080fd5b60008060008060008061028087890312156153e8578182fd5b86356001600160401b03808211156153fe578384fd5b61540a8a838b01615166565b9750602089013591508082111561541f578384fd5b61542b8a838b01615125565b90975095506040890135915080821115615443578384fd5b61544f8a838b016151cd565b94506060890135915080821115615464578384fd5b5061547189828a01615166565b92505061548188608089016150ed565b90509295509295509295565b60008060006104c084860312156154a2578081fd5b83356001600160401b03808211156154b8578283fd5b6154c487838801615166565b945060208601359150808211156154d9578283fd5b506154e686828701615166565b9250506154f68560408601615307565b90509250925092565b600080600080600080600080610740898b03121561551b578586fd5b88356001600160401b0380821115615531578788fd5b61553d8c838d01615166565b995060208b0135915080821115615552578788fd5b61555e8c838d01615166565b985060408b0135975061557360608c016153be565b965060808b0135915080821115615588578384fd5b6155948c838d016151cd565b955060a08b01359150808211156155a9578384fd5b506155b68b828c01615166565b9350506155c68a60c08b016150ed565b91506155d68a6102c08b01615307565b90509295985092959890939650565b6000602082840312156155f6578081fd5b5051919050565b60008060008060008060008060008060006101408c8e03121561561e578485fd5b8b359a5061562e60208d016153aa565b995061563c60408d01614fb5565b985061564a60608d016153aa565b975060808c01356fffffffffffffffffffffffffffffffff8116811461566e578586fd5b965061567c60a08d016153aa565b955061568a60c08d01614fb5565b945061569860e08d016153aa565b93506101008c013592506101208c01356001600160401b038111156156bb578283fd5b6156c78e828f01615125565b915080935050809150509295989b509295989b9093969950565b600080602083850312156156f3578182fd5b82356001600160401b0380821115615709578384fd5b818501915085601f83011261571c578384fd5b81358181111561572a578485fd5b86602082850101111561573b578485fd5b60209290920196919550909350505050565b60008151815b8181101561576d5760208185018101518683015201615753565b8181111561577b5782828601525b509290920192915050565b98895260e097881b6001600160e01b031990811660208b0152606097881b6bffffffffffffffffffffffff1990811660248c015296891b811660388b015260809590951b6fffffffffffffffffffffffffffffffff1916603c8a015292871b8416604c890152941b90921660508601529190921b166064830152606882015260880190565b6000610110828461574d565b6000615823828561574d565b8084835b6010811015615846578151845260209384019390910190600101615827565b50506102000195945050505050565b6000615861828561574d565b9283525050602001919050565b600061587a828561574d565b60f89390931b6001600160f81b03191683525050600101919050565b90815260200190565b60e09490941b6001600160e01b031916845260048401929092526024830152604482015260640190565b6001600160e01b031960e095861b811682526004820194909452602481019290925290921b16604482015260480190565b901515815260200190565b6020808252600290820152616e3160f01b604082015260600190565b6040518181016001600160401b038111828210171561593c57fe5b604052919050565b60006001600160401b0382111561595757fe5b5060200290565b60006001600160401b0382111561597157fe5b506020908102019056fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a26469706673582212200ee33c0c87e372c1c05888ea8e0625de01d982c8eacd1a938626bcd31fc423ad64736f6c63430007060033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100625760003560e01c80631d8a5663146100675780632539464514610090578063439fab9114610090578063a830bd60146100a5578063f7e13636146100b8578063fd1bc844146100cb575b600080fd5b61007a61007536600461548d565b6100de565b60405161008791906158fa565b60405180910390f35b6100a361009e3660046156e1565b610117565b005b61007a6100b33660046153cf565b61011b565b61007a6100c63660046155fd565b6101eb565b61007a6100d93660046154ff565b610350565b600083518260200151146100f157600080fd5b60006100fd8585610391565b9050600061010b8285610697565b925050505b9392505050565b5050565b6000805b835181101561016a57600084828151811061013657fe5b602002602001015190506001600160fd1b03811685838151811061015657fe5b60209081029190910101525060010161011f565b50600061017785516106e3565b90506101df888888808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152507f27362ec1b4c43df95bd87dc1b1591227b4095aaeb2e030db431c291daf9865509250600391508a9050898988610350565b98975050505050505050565b60008060028d8d8d8d8d8d8d8d8d60405160200161021199989796959493929190615786565b60408051601f198184030181529082905261022b9161580b565b602060405180830381855afa158015610248573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061026b91906155e5565b60408051600180825281830190925291925060009190602080830190803683370190505090506001600160fd1b038260001c16816000815181106102ab57fe5b60200260200101818152505060006102f68287878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061073c92505050565b905060006103026109b6565b905082518160200151146103315760405162461bcd60e51b815260040161032890615905565b60405180910390fd5b61033b8282610e76565b9450505050509b9a5050505050505050505050565b6000885182602001511461036357600080fd5b600061036f8a8a610391565b9050600061038282858b8b8b8b8b610eaa565b9b9a5050505050505050505050565b610399614ad5565b60228251146103a757600080fd5b82516001600160401b03811180156103be57600080fd5b506040519080825280602002602001820160405280156103e8578160200160208202803683370190505b50815260005b835181101561042e5783818151811061040357fe5b60200260200101518260000151828151811061041b57fe5b60209081029190910101526001016103ee565b506000805b60048110156104935761046f84838151811061044b57fe5b602002602001015185846001018151811061046257fe5b6020026020010151610f47565b8360200151826004811061047f57fe5b602002015260029190910190600101610433565b506104ba8382815181106104a357fe5b602002602001015184836001018151811061046257fe5b604083015260020160005b6004811015610501576104dd84838151811061044b57fe5b836060015182600481106104ed57fe5b6020020152600291909101906001016104c5565b5060005b600481101561054b5761052a84838151811061051d57fe5b6020026020010151611029565b8360800151826004811061053a57fe5b602002015260019182019101610505565b5060005b60018110156105885761056784838151811061051d57fe5b8360a00151826001811061057757fe5b60200201526001918201910161054f565b5060005b60018110156105c5576105a484838151811061051d57fe5b8360c0015182600181106105b457fe5b60200201526001918201910161058c565b5060005b6003811015610603576105e184838151811061051d57fe5b83610140015182600381106105f257fe5b6020020152600191820191016105c9565b5061061383828151811061051d57fe5b8260e0018190525060018101905061063083828151811061051d57fe5b82610100018190525060018101905061064e83828151811061051d57fe5b82610120018190525060018101905061066c8382815181106104a357fe5b82610160018190525060028101905061068a8382815181106104a357fe5b6101808301525092915050565b60008060006106a6858561105d565b9092509050816106bb576000925050506106dd565b80516106d8906106c961109d565b602084015160e088015161115d565b925050505b92915050565b6106eb614b84565b63ffffffff82166001141561070957610702611239565b9050610737565b63ffffffff821660041415610720576107026116a0565b63ffffffff82166008141561073757610702611b07565b919050565b610744614be7565b602182511461075257600080fd5b82516001600160401b038111801561076957600080fd5b50604051908082528060200260200182016040528015610793578160200160208202803683370190505b50815260005b83518110156107d9578381815181106107ae57fe5b6020026020010151826000015182815181106107c657fe5b6020908102919091010152600101610799565b506000805b600481101561081a576107f684838151811061044b57fe5b8360200151826004811061080657fe5b6020020152600291909101906001016107de565b5061082a8382815181106104a357fe5b604083015260020160005b60048110156108715761084d84838151811061044b57fe5b8360600151826004811061085d57fe5b602002015260029190910190600101610835565b5060005b60048110156108ae5761088d84838151811061051d57fe5b8360800151826004811061089d57fe5b602002015260019182019101610875565b5060005b60018110156108eb576108ca84838151811061051d57fe5b8360a0015182600181106108da57fe5b6020020152600191820191016108b2565b506108fb83828151811061051d57fe5b8260c0018190525060018101905061091883828151811061051d57fe5b8260e0018190525060018101905061093583828151811061051d57fe5b61010083015260010160005b600381101561097b5761095984838151811061051d57fe5b836101200151826003811061096a57fe5b602002015260019182019101610941565b5061098b8382815181106104a357fe5b8261014001819052506002810190506109a98382815181106104a357fe5b6101608301525092915050565b6109be614c29565b620800008152600160208201526109f47f0cf1526aaafac6bacbb67d11a4077806b123f767e4b0883d14cc0193568fc082611029565b6040820152610a437f114dd473f77a15b602201577dd4b64a32a783cb32fbc02911e512df6a219695d7f04c68f82a5dd7d0cc90318bdff493b3d552d148ad859c373ffe55275e043c43b611f6a565b606082015152610a937f245e8c882af503cb5421f5135b4295a920ccf68b42ae7fb967f044f54e2aaa297f071322ee387a9ce49fe7ef2edb6e9237203dee49ec47483af85e356b79fb06fd611f6a565b606082015160200152610ae67f0187754ab593b07a420b3b4d215c20ed49acf90fc4c97e4b06e8f5bc0a2eb3f47f0170f9286ce950286a16ea25136c163c0b32019f31b89c256a612d40b863d0b6611f6a565b606082015160400152610b397f0defecfae1d2b9ec9b2ee4d4798c625fa50f6a4ddb7747a7293df0c17fcb90c27f0f91d08fceebf85fb80f12cda78cefa1ee9dbf5cfe7c4f0704b3c6620fa50c55611f6a565b6060828101510152610b8b7f2f7fef3b3fb64af6640f93803a18b3e5ce4e0e60aecd4f924c833fa6fa6da9617f03908fc737113ac7f3529fe3b36efca200c66d1d85d2fc081973214c586de732611f6a565b606082015160800152610bde7f14ce3c0e9b78fc331327249e707f58fa4bb0ed746bdc9c2262ad0cf9056096277f09e64fdac452b424e98fc4a92f7222693d0d84ab48aadd9c46151dbe5f1a34a9611f6a565b606082015160a00152610c317f1d10bfd923c17d9623ec02db00099355b373021432ae1edef69b0f5f461f78d67f24e370a93f65f42888781d0158bb6ef9136c8bbd047d7993b8276bc8df8b640a611f6a565b608082015152610c817f1fd1755ed4d06d91d50db4771d332cfa2bc2ca0e10ac8b77e0d6b73b993e788e7f0bdbf3b7f0d3cffdcf818f1fba18b90914eda59b454bd1858c6c0916b817f883611f6a565b60a082015152610cd17f1f3b8d12ffa2ceb2bb42d232ad2cf11bce3183472b622e11cc841d26f42ad5077f0ce815e32b3bd14311cde210cda1bd351617d539ed3e9d96a8605f364f3a29b0611f6a565b60a082015160200152610d247f123afa8c1cec1956d7330db062498a2a3e3a9862926c02e1228d9cfb63d3c3017f0f5af15ff0a3e35486c541f72956b53ff6d0740384ef6463c866146c1bd2afc8611f6a565b60a082015160400152610d777f01069e38ea6396af1623921101d3d3d14ee46942fb23bf1d110efb994c3ee5737f232a8ce7151e69601a7867f9dcac8e2de4dd8352d119c90bbb0fb84720c02513611f6a565b60a082015160600152610d8a6005611029565b60c082015152610d9a6007611029565b60c082015160200152610dad600a611029565b60c082015160026020020181905250610e6e60405180604001604052807f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181526020017f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081525060405180604001604052807f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe481526020017f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55815250611f88565b60e082015290565b6000610e80614c65565b6000610e8d828686611f90565b905080610e9f576000925050506106dd565b6106d882868661225e565b6000806000610ebc888888888861268c565b915091508960000151600081518110610ed157fe5b60200260200101518214610ee157fe5b600080610eee8c8c61105d565b909250905081610f05576000945050505050610f3c565b6000610f1184836128d5565b8051909150610f3490610f2261109d565b83600160200201518f60e0015161115d565b955050505050505b979650505050505050565b610f4f614cc6565b82158015610f5b575081155b15610f7c5760405180604001604052808481526020018381525090506106dd565b60008051602061597c8339815191528310610f9657600080fd5b60008051602061597c8339815191528210610fb057600080fd5b600060008051602061597c8339815191528384099050600060008051602061597c833981519152858609905060008051602061597c833981519152858209905060008051602061597c83398151915260038208905080821461101157600080fd5b50506040805180820190915292835250602082015290565b611031614ce0565b60008051602061599c833981519152821061104b57600080fd5b50604080516020810190915290815290565b6000611067614cf3565b61106f614c65565b61107a81868661298c565b9250826110875750611096565b611092818686612c21565b9150505b9250929050565b6110a5614d20565b50604080516080810182527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c28183019081527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6060830152815281518083019092527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b82527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60208381019190915281019190915290565b60408051600280825260608201909252600091829190816020015b611180614cc6565b8152602001906001900390816111785750506040805160028082526060820190925291925060009190602082015b6111b6614d20565b8152602001906001900390816111ae57905050905086826000815181106111d957fe5b602002602001018190525084826001815181106111f257fe5b6020026020010181905250858160008151811061120b57fe5b6020026020010181905250838160018151811061122457fe5b6020026020010181905250610f3c82826130cc565b611241614b84565b624000008152600160208201526112777f18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede611029565b60408201526112c67f16782f42f191b0b1841c2b6a42b7f0564af065d04818526df6c3ad41fe35f8da7f125b9c68c0b931578f8a18fd23ce08e7b7c082ad76404ccece796fa9b3ec0cb0611f6a565b6060820151526113167f2511833eee308a3936b23b27c929942a60aa780747bf32143dc183e873144bfd7f1b8d88d78fcc4a36ebe90fbbdc4547442411e0c8d484727d5c7c6eec27ad2df0611f6a565b6060820151602001526113697f2945641d0c5556aa333ef6c8431e24379b73eccbed7ff3e9425cc64aee1e92ed7f25bbf079192cc83f160da9375e7aec3d3d2caac8d831a29b50f5497071fc14c6611f6a565b6060820151604001526113bc7f09b3c361e5895a8e074eb9b9a9e57af59966f0464068460adc3f64e58544afa47f0412a017f775dd05af16cf387a1e822c2a7e0f8b7cfabd0eb4eb0f67b20e4ada611f6a565b606082810151015261140e7f244b30447ab3e56bb5a5a7f0ef8463a4047476ea269735a887b3de568b3401a37f2ba860198d5e6e0fd93355cb5f309e7e4c1113a57222830961999b79b83d700f611f6a565b6060820151608001526114617f0e13af99775bf5555c366e9c8d4af25a2e195807b766b422856525c01a38b12d7f1787389894222dba5371ab55d512460c5205c1baa0421fc877b183025079a472611f6a565b606082015160a001526114b47f233a03f89c094cf39c89020772d9b912bd0c303d211002ee5afc5c59e241f02b7f04fa51fca1b17399bbbf2b99f17bbce6af1f50b085add4c41ac4ea64f65f4674611f6a565b606082015160c001526115077f1ca088ed531e65b722c8b48568359bbe11051b86f1a8e8951eacc615d9faed3b7f074b06c09de93dd79e070a9ded635e21a34d7178e9a670766e8208149c28e339611f6a565b6080820151526115577f2b4c77c0d47676559061b47968a044aec625cb907181457428e5d08df9b27ef87f1c1be561bdc3eba16162886a2943882157f98ed8246f2063028497f1c108fa93611f6a565b6080820151602001526115aa7f238fd7f2cbc3c3e5899483633c78f051e6d6d25f31aaa6b32b863d55b20d641a7f1f9877b625eaae7a084582a2ffce326a6a5558f3efdb3367037098c4ca25a647611f6a565b60a0820151526115fa7f0b126f60653e371f3f2a85301f16e9cf4af04922a2725fc131b17e90e13d0d847f13bc3f0c7475b74591827463943b35cfd05adb7094a79eeeee2067e8e28a8e84611f6a565b60a08201516020015261164d7f06cae3c1e5b43afb4dda3243c99da693a27eba065fd61a873e99e2c85fd227197f14343c6bdcc85b01b053f26aa3c473cb2f24747ba6d6b90b2323b24f3dfd127e611f6a565b60a082015160400152610d777f217564e2c710d050161b57ef2700e1676251a6d457c4b0d94c41a4492d6dcea37f2365779642d63803d0265a7cc666b3af6ad92b7e9ef38d9113db1208b83f0732611f6a565b6116a8614b84565b628000008152600160208201526116de7f1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863611029565b604082015261172d7f1878d6c837a0f16cb055d3a4e79fba0d85de670dacd708dadd55407b0619796d7f0b3282e52a38ecec63ba42710e8d1ad5c8715c7ed07ce217a3eec747a3f37d76611f6a565b60608201515261177d7f07425bcaf480e377886678d5b5432f0945e3fc952126503a7b672dc4b03f2c267f155b8003ea27945bf43fb5f43291f76e2aa361e0ec81550c0af66dcd1dc8077e611f6a565b6060820151602001526117d07f1292b8795f05fc50782ea7303e2b65a7b2f0e1cc3dead51dfa0b9d2183e5d9077f220d344a384ac53f682e1be6c69407a1fadd0a589de36b95ddc4da05693ba679611f6a565b6060820151604001526118237f283412c1942c0cb3fffc935aab313a37510888bd5ae5972d8d67edc2312af8957f1040e655967354e7ae9227c6200c2256cdcbb707e7158b66462aba23d96b8de2611f6a565b60608281015101526118757f2abe282377038904420434202c11a4f849e64babd436b93192d8d9c34d28ce447f19f0ed010326da1cf8ac93a0f73617ab7c9acb30a0c23a26db9ec19ab6a52fcb611f6a565b6060820151608001526118c87f236f01e67b19be0e7487100a14fd04a05a83a5660966ace987c5248f8c8834597f0ebe824fb1e778491bcb8091d2adbc18dceda4fa9ee191b71c5834a71c533c41611f6a565b606082015160a0015261191b7f2ad3c37aa0b1335f6c70d0e10f0a123a28ea012e857df30e3ced524ef6562c717f1b52d7ac4ee6082438deab8ab0f2944c9fd53258de305065f8323a3767dd8234611f6a565b606082015160c0015261196e7f173c39587688a8967e915959df613aecf44ad0c7d2019ec32311bccdf542c78e7f2421a36a67559ed89afbff081cd45b318835e2b0233c047d030abc48b5011c22611f6a565b6080820151526119be7f177d8ef11cac24105d4b38e035b891986d163d9df717fce12d18af324f86d2dc7f02cd01ba1c82c85b4f0f8c7304254de64516857ac4f7bb60f052bb2af98132c5611f6a565b608082015160200152611a117f21da2c0f2b7849d4c44dbc487d370cccbae78fbd979e79575e04b7a983f2f68a7f14ffb806769ccf0d2c692cd93653491966525554d79efc37cfba5a5c08b15039611f6a565b60a082015152611a617f184cc2f37e687a9be2404cd367536f14a505f086fd597cb966c5b753f325adb47f20aaed49755efed4814025ac679570f62b8c98a1b8d977969242c3ffa67884d6611f6a565b60a082015160200152611ab47f0a2dee920031d9cd5ed499dc3cb901657079f6a2dfb0ba389b0181803bb91e247f272ac2a214f46be0ed7d2b4cf125504ef82d929b1c1ec0a81655c66f39403cd1611f6a565b60a082015160400152610d777f07e360365c7a5363389b2d2449b9471754591f01a623fd5553c5cfe6bad19aaf7f1b814914958835ef86de3c26c6c4bdc27e947f38cb0d2bfaa421d66cabfb7d55611f6a565b611b0f614b84565b6301000000815260016020820152611b467f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb611029565b6040820152611b957f1aab46b9aa3adcac623c360e4d075572e3f56f4c75ac3b8663a7b059bd9b18577f166ac39283efa3d6cb36423e83e2360f006e5fa374b454dea5fe92cc50d4193f611f6a565b606082015152611be57f13bce0a7bfbf2e7a81f18e84966c32422446b01f54cc7dc2ad3f64e92fe94cad7f0247234b0cdfd8c95a767f84303c3dd65ce7b15856c2840635d9d4754ba99479611f6a565b606082015160200152611c387f08742bad9a7cbcc9dbb49a25bebce179295d1cf70fd8f9c8e82b8a658ee0b67c7f2a467983257850c5fa27f2f52f0c5c5fc98e7d2e0d440a8fd954ad981ff0ce9f611f6a565b606082015160400152611c8b7f16ebdd4b95b872cd09c13b6b54a8b8bf81a01529a71234db26e3b22c6d6327237f034219d7ad9ef204cfb3e32c4a47af82eea40504c2b1bac785104731722ed617611f6a565b6060828101510152611cdd7f2e3a7c4458a8dc1535e68bac5dd5c1c9ff3886df4156bad4a08fcd08ebf1db267f173859705317db06e5b7d260898ab08e72fae987c272b82345105d72bfd00ab8611f6a565b606082015160800152611d307f0b830132e3325eaaea73c1095e615358db38dfb39248c90f8ff4afde169e76577f0bfedf8cfce7260c16bb1f76ad9a39f73a68087e5c68e841020aeaa5ba301a9f611f6a565b606082015160a00152611d837f1660c850da793add523f7990b983896e50d5549eec7990ec26aabc220ca58d527f0ba698e78dee0d41cf8aefde82c5bfda38be071e11025b56db779ddb40a4fe92611f6a565b606082015160c00152611dd67f024fe4ce02dd48937e4642b66308ae15d731e0ea82fc5430a0470d9a5dab36947f177cac2d79a8bfa6aba134e24bded06d06219979c18b2fa4fe71baea9885985d611f6a565b608082015152611e257ea848bc76c52faf7d4e7cc4086b50e3ccc9b1cebef130ac1bbf1816502df59d7f02f42f326f82b33cb9e4e7cfb332889eec95c2813f7968b3a50d838b3cbfa676611f6a565b608082015160200152611e787f20c176738979e0d1ea9541bf26e6209d3091b618ae94f3c72e13e954a1614f607f2a7019c81009c00a7412b6a303b2eb118a362a558837e9ecdb912589bc11ff83611f6a565b60a082015152611ec87f10a92b3fa2b8280030c9de5cbcab4da3cf9b5b3f63f3ad60284ecded63cc54ea7f1bde2a83db435b8c74e4239b4f8416da88008331a758d8c68a9104f2dfc3e237611f6a565b60a082015160200152611f1b7f08e2e513d1e548a627e2d4f74d28dea916d8598415b70543bb3e92429f0111cb7f2fb46898f77e32d7fd646fe31b60320423aa4698501e329e206b6acfcfb01337611f6a565b60a082015160400152610d777f145b88d324270872b13784fbb7ccdee6e5593d2d5cbc81f4aaa9b4268cfc50947f197d826aaf2a9853ca98ec9c0e55376eec1a6a0f5dbbbe02afeb1b567d8eafa05b611f72614cc6565b5060408051808201909152918252602082015290565b611f72614d20565b602081015182515160009114611fa557600080fd5b600182602001511015611fb757600080fd5b6000611fc16132dd565b905060005b836020015181101561200457611ffc85600001518281518110611fe557fe5b6020026020010151836132f990919063ffffffff16565b600101611fc6565b5060005b6004811015612038576120308560200151826004811061202457fe5b6020020151839061336c565b600101612008565b5061204281613388565b602086015261205081613388565b60408087019190915284015161206790829061336c565b61207081613388565b855260005b6004811015612099576120918560600151826004811061202457fe5b600101612075565b506120a381613388565b60a086015260208301516000906001600160401b03811180156120c557600080fd5b506040519080825280602002602001820160405280156120ef578160200160208202803683370190505b50905060005b815181101561211e578082828151811061210b57fe5b60209081029190910101526001016120f5565b5061213781856000015186604001518960a001516133f9565b60c087015260006121498787876137ae565b90508061215c5760009350505050610110565b60005b600481101561218f576121878760800151826004811061217b57fe5b6020020151859061398e565b60010161215f565b5060005b60018110156121b7576121af8760a00151826001811061217b57fe5b600101612193565b5060005b60038110156121e0576121d8876101200151826003811061217b57fe5b6001016121bb565b5060e08601516121f190849061398e565b61010086015161220290849061398e565b60c086015161221290849061398e565b61221b83613388565b606088015261014086015161223190849061336c565b61016086015161224290849061336c565b61224b83613388565b6080880152506001925050509392505050565b60008061226c85858561399c565b835160a087015191925060009161228291613cfa565b9050600061228e613d78565b9050600061229c6001611029565b905060006122be88606001516000600481106122b457fe5b6020020151613d99565b905060006122cc6001611029565b905060015b6004811015612317576122e48287613db5565b612303828b6060015183600481106122f857fe5b602002015190613dcf565b945061230f8386613de2565b6001016122d1565b5060608a0151612328908490613db5565b6123328287613de2565b60005b60048110156123765760608b015161234e908590613db5565b612362848b6020015183600481106122f857fe5b945061236e8386613de2565b600101612335565b5060005b60038110156123bb5760608b0151612393908590613db5565b6123a7848a60a0015183600481106122f857fe5b94506123b38386613de2565b60010161237a565b5060608a01516123cc908490613db5565b60608a01516123dc908490613db5565b6123e68184613ded565b60808a01516123f6908290613db5565b602089015161240890829060036122f8565b93506124148285613de2565b61241e6001611029565b9250600061242f8a60e00151613df2565b90506124488b6060015185613db590919063ffffffff16565b6101008a0151612459908390613ded565b6124638285613db5565b61246d8183613e01565b60005b60048110156124c45760608c0151612489908690613db5565b6124a88b60800151826004811061249c57fe5b60200201518490613ded565b6124b28386613db5565b6124bc8284613e01565b600101612470565b5060005b60038110156125115760608c01516124e1908690613db5565b6124f58b6101200151826003811061249c57fe5b6124ff8386613db5565b6125098284613e01565b6001016124c8565b5060608b0151612522908590613db5565b60c08a0151612532908390613ded565b61253c8285613db5565b60808b015161254c908390613db5565b6125568183613e01565b60608b0151612566908590613db5565b60a08a015161257e9060005b60200201518390613ded565b6125888285613db5565b60808b0151612598908390613db5565b6125a28183613e01565b6125be6125b7826125b1613d78565b90613dcf565b8490613e1b565b60a08b01516101408b015184916125df916125d891613dcf565b8290613de2565b60a08c01516125ef908490613ded565b60408a01516125ff908490613db5565b60808c015161260f908490613db5565b6101608b0151612623906125d89085613dcf565b60006126418d608001518d6101600151613dcf90919063ffffffff16565b905061265b8c610140015182613de290919063ffffffff16565b61266481613e26565b61267b8261267061109d565b838e60e0015161115d565b9d9c50505050505050505050505050565b6000612696614cf3565b83518551146126a157fe5b6000876040516020016126b49190615896565b60408051601f1981840301815291905290506000805b8751811015612726578781815181106126df57fe5b602002602001015191508860ff168260ff1611156126f957fe5b828260405160200161270c92919061586e565b60408051601f1981840301815291905292506001016126ca565b506000805b885181101561278e5787818151811061274057fe5b6020026020010151915060008051602061599c833981519152821061276157fe5b8382604051602001612774929190615855565b60408051601f19818403018152919052935060010161272b565b5082866040516020016127a2929190615817565b604051602081830303815290604052925060006002846040516127c5919061580b565b602060405180830381855afa1580156127e2573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061280591906155e5565b6060880151604089015160208a01518a5160e08c015160c08d015160a08e01516001600160f81b0388169d5096975061286796604494851b93909301608895861b0160cc96871b01959190911b931b911b8b60045b6020020151010101610f47565b85526101608701516101408801516101208901516101008a01516101e08b01516101c08c01516101a08d01516128bf96604495861b94909401608896871b0160cc94851b01959290931b93911b91901b8b600c61285a565b8560016020020152505050509550959350505050565b6128dd614cf3565b60006128e76132dd565b90506128fc8460005b6020020151829061336c565b6129078460016128f0565b6129128360006128f0565b61291d8360016128f0565b600061292882613388565b90506129358560006122b4565b83526129428560016122b4565b602084015260006129548286836122f8565b9050612969818560005b602002015190613de2565b612975828660016122f8565b90506129838185600161295e565b50505092915050565b6020810151825151600091146129a157600080fd5b6001826020015110156129b357600080fd5b60006129bd6132dd565b905060005b83602001518110156129e9576129e185600001518281518110611fe557fe5b6001016129c2565b5060005b6004811015612a1157612a098560200151826004811061202457fe5b6001016129ed565b50612a1b81613388565b6020860152612a2981613388565b604080870191909152840151612a4090829061336c565b612a4981613388565b855260005b6004811015612a7257612a6a8560600151826004811061202457fe5b600101612a4e565b50612a7c81613388565b60a086015260208301516000906001600160401b0381118015612a9e57600080fd5b50604051908082528060200260200182016040528015612ac8578160200160208202803683370190505b50905060005b8151811015612af75780828281518110612ae457fe5b6020908102919091010152600101612ace565b50612b1081856000015186604001518960a00151613e5c565b60c08701526000612b22878787614105565b905080612b355760009350505050610110565b610100860151612b4690849061398e565b60005b6004811015612b6d57612b658760800151826004811061217b57fe5b600101612b49565b5060005b6001811015612b9557612b8d8760a00151826001811061217b57fe5b600101612b71565b5060c086015151612ba790849061398e565b60005b6003811015612bcf57612bc7876101400151826003811061217b57fe5b600101612baa565b5060e0860151612be090849061398e565b610120860151612bf190849061398e565b612bfa83613388565b6060880152610160860151612c1090849061336c565b61018086015161224290849061336c565b612c29614cf3565b6000612c3685858561434c565b835160a0870151919250600091612c4c91613cfa565b90506000612c58613d78565b90506000612c666001611029565b90506000612c7e88606001516000600481106122b457fe5b90506000612c8c6001611029565b905060015b6004811015612ccc57612ca48287613db5565b612cb8828b6060015183600481106122f857fe5b9450612cc48386613de2565b600101612c91565b5060608a0151612cdd908490613db5565b612ce78287613de2565b60005b6004811015612d2b5760608b0151612d03908590613db5565b612d17848b6020015183600481106122f857fe5b9450612d238386613de2565b600101612cea565b5060005b6001811015612d6e5760608b0151612d48908590613db5565b6080890151612d5a90859060006122f8565b9450612d668386613de2565b600101612d2f565b5060005b6003811015612db35760608b0151612d8b908590613db5565b612d9f848a60a0015183600481106122f857fe5b9450612dab8386613de2565b600101612d72565b5060608a0151612dc4908490613db5565b612dce8184613ded565b60808a0151612dde908290613db5565b6040890151612df890612df19083613dcf565b8390613de2565b60608a0151612e08908490613db5565b612e128184613ded565b60808a0151612e22908290613db5565b6020890151612e3490829060036122f8565b9350612e408285613de2565b612e4a6001611029565b92506000612e5c8a6101000151613df2565b9050612e758b6060015185613db590919063ffffffff16565b6101208a0151612e86908390613ded565b612e908285613db5565b612e9a8183613e01565b60005b6004811015612ee55760608c0151612eb6908690613db5565b612ec98b60800151826004811061249c57fe5b612ed38386613db5565b612edd8284613e01565b600101612e9d565b5060005b6001811015612f315760608c0151612f02908690613db5565b612f158b60c00151826001811061249c57fe5b612f1f8386613db5565b612f298284613e01565b600101612ee9565b5060005b6003811015612f7e5760608c0151612f4e908690613db5565b612f628b6101400151826003811061249c57fe5b612f6c8386613db5565b612f768284613e01565b600101612f35565b5060608b0151612f8f908590613db5565b60e08a0151612f9f908390613ded565b612fa98285613db5565b60808b0151612fb9908390613db5565b612fc38183613e01565b60608b0151612fd3908590613db5565b60a08a0151612fe3906000612572565b612fed8285613db5565b60808b0151612ffd908390613db5565b6130078183613e01565b6130166125b7826125b1613d78565b60a08b01516101608b01518491613030916125d891613dcf565b60a08c0151613040908490613ded565b60408a0151613050908490613db5565b60808c0151613060908490613db5565b6101808b0151613074906125d89085613dcf565b60006130928d608001518d6101800151613dcf90919063ffffffff16565b90506130ac8c610160015182613de290919063ffffffff16565b6130b581613e26565b908952602089015250959998505050505050505050565b600081518351146130dc57600080fd5b8251600681026000816001600160401b03811180156130fa57600080fd5b50604051908082528060200260200182016040528015613124578160200160208202803683370190505b50905060005b838110156132a95786818151811061313e57fe5b60200260200101516000015182826006026000018151811061315c57fe5b60200260200101818152505086818151811061317457fe5b60200260200101516020015182826006026001018151811061319257fe5b6020026020010181815250508581815181106131aa57fe5b6020908102919091010151515182518390600260068502019081106131cb57fe5b6020026020010181815250508581815181106131e357fe5b6020908102919091010151516001602002015182826006026003018151811061320857fe5b60200260200101818152505085818151811061322057fe5b60200260200101516020015160006002811061323857fe5b602002015182826006026004018151811061324f57fe5b60200260200101818152505085818151811061326757fe5b60200260200101516020015160016002811061327f57fe5b602002015182826006026005018151811061329657fe5b602090810291909101015260010161312a565b506132b2614d40565b6000602082602086026020860160085afa9050806132cf57600080fd5b505115159695505050505050565b6132e5614d5e565b600080825260208201819052604082015290565b8151602080840151604051613317926000928592909187910161589f565b60408051601f198184030181529181528151602092830120855284820151905161334a926001928592909187910161589f565b60408051601f1981840301815291905280516020918201209301929092525050565b61337a8282600001516132f9565b6101178282602001516132f9565b613390614ce0565b600060028360000151846020015185604001516040516020016133b694939291906158c9565b60408051808303601f190181529181528151602092830120948101805160010163ffffffff169052805191820190526001600160fd1b0390931683525090919050565b606060006134076001611029565b905060006134156000611029565b9050600061342287611029565b905060006134308689613cfa565b905061343c81856146a6565b805161344757600080fd5b600089516001600160401b038111801561346057600080fd5b5060405190808252806020026020018201604052801561349a57816020015b613487614ce0565b81526020019060019003908161347f5790505b50905060008a516001600160401b03811180156134b657600080fd5b506040519080825280602002602001820160405280156134f057816020015b6134dd614ce0565b8152602001906001900390816134d55790505b50905060005b8b518110156135c1576135258c828151811061350e57fe5b60200260200101518b613cfa90919063ffffffff16565b955061354d8484838151811061353757fe5b6020026020010151613ded90919063ffffffff16565b6135738684838151811061355d57fe5b6020026020010151613db590919063ffffffff16565b6135838983838151811061353757fe5b6135a98683838151811061359357fe5b60200260200101516146a690919063ffffffff16565b6135b98583838151811061355d57fe5b6001016134f6565b5060008b516001600160401b03811180156135db57600080fd5b5060405190808252806020026020018201604052801561361557816020015b613602614ce0565b8152602001906001900390816135fa5790505b5090506136326136256001611029565b8260008151811061353757fe5b60015b60018351038110156136925761366783600183038151811061365357fe5b602002602001015183838151811061353757fe5b61368a83828151811061367657fe5b602002602001015183838151811061355d57fe5b600101613635565b506136bd816001835103815181106136a657fe5b602002602001015186613ded90919063ffffffff16565b6136e7826001845103815181106136d057fe5b602002602001015186613db590919063ffffffff16565b6136f0856146c4565b8251909550600019015b8251811015613765576137138684838151811061353757fe5b61373682828151811061372257fe5b602002602001015184838151811061355d57fe5b61375c83828151811061374557fe5b602002602001015187613db590919063ffffffff16565b600019016136fa565b5060005b835181101561379d5761379583828151811061378157fe5b602002602001015185838151811061355d57fe5b600101613769565b50919b9a5050505050505050505050565b6000806137c383600001518660a00151614701565b80519091506137d157600080fd5b60e08401516137e1908290613db5565b60006137ed6001611029565b905060006137ff866101000151613df2565b9050600061380d6000611029565b905060005b875151811015613875576138468960c00151828151811061382f57fe5b602002602001015183613ded90919063ffffffff16565b61386361385c8960000151838151811061051d57fe5b8390613db5565b61386d8383613e01565b600101613812565b508751613883908490613db5565b60006138928860c00151613df2565b905060005b6003811015613905576138b4896101200151826003811061249c57fe5b60208a01516138c4908490613db5565b60408a01516138d4908490613e01565b6138f3896080015182600481106138e757fe5b60200201518490613e01565b6138fd8284613db5565b600101613897565b506040890151613916908390613ded565b60808801516060015161392a908390613e01565b6139348183613db5565b61393e8185613db5565b61394883826146a6565b8851613955908590613db5565b6139698960c0015160008151811061382f57fe5b6139738285613db5565b61397d83836146a6565b505051915190911495945050505050565b6101178282600001516132f9565b6139a4614cc6565b60608201516009906139b79060056122b4565b915060006139c3613d78565b905060006139d16000611029565b905060005b6004811015613a1b57613a07876080015182600481106139f257fe5b6020020151876060015183600681106122f857fe5b9250613a138584613de2565b6001016139d6565b506080860151613a349060005b60200201518290613ded565b6080860151613a4c9060015b60200201518290613db5565b6060850151613a5e90829060046122f8565b9150613a6a8483613de2565b60a0860151516080860151613a81919060006122f8565b9150613a8d8483613de2565b6000613a9c8860a00151613df2565b9050613ab5886020015182613db590919063ffffffff16565b6080870151613acd9060005b60200201518290613e01565b6040880151613add908290613e01565b60005b6003811015613b605760a0890151613af9908490613ded565b613b188760c001518260038110613b0c57fe5b60200201518490613db5565b6020890151613b28908490613db5565b6040890151613b38908490613e01565b613b4e886080015182600101600481106138e757fe5b613b588284613db5565b600101613ae0565b508751613b6e908290613db5565b613b828860c0015160008151811061382f57fe5b8751613b8f908390613db5565b8751613b9c908390613db5565b613ba68183613e01565b6060880151600090613bb89086613cfa565b9050613bd1896080015182613db590919063ffffffff16565b6000613bdd6001611029565b905060005b6003811015613c5c5760208b0151613bfb908690613ded565b613c1b8a61012001518260038110613c0f57fe5b60200201518690613db5565b60408b0151613c2b908690613e01565b613c4a8a608001518260048110613c3e57fe5b60200201518690613e01565b613c548286613db5565b600101613be2565b5060208a0151613c6d908290613db5565b60c0890151613c7d908290613db5565b8951613c8a908290613db5565b6040890151613c999084613dcf565b60a0890151909550613cb990613cb290839060036122f8565b8690613e1b565b613cc38786613de2565b60608a0151613cd3908890614729565b6040890151613ced90613ce69084613dcf565b8890613de2565b5050505050509392505050565b613d02614ce0565b6040805160c081018252602080825280820181905291810191909152835160608201526080810183905260008051602061599c83398151915260a0820152613d48614d40565b600060208260c08560055afa905080613d6057600080fd5b50604080516020810190915290518152949350505050565b613d80614cc6565b5060408051808201909152600181526002602082015290565b613da1614cc6565b815181526020918201519181019190915290565b60008051602061599c833981519152815183510990915250565b613dd7614cc6565b6106dd838383614730565b610117828284614772565b519052565b613dfa614ce0565b9051815290565b60008051602061599c833981519152815183510890915250565b61011782828461480a565b6020810151613e4057805115613e3b57600080fd5b613e59565b60208101805160008051602061597c8339815191520390525b50565b60606000613e6a6001611029565b90506000613e786000611029565b90506000613e8587611029565b90506000613e938689613cfa565b9050613e9f81856146a6565b8051613eaa57600080fd5b600089516001600160401b0381118015613ec357600080fd5b50604051908082528060200260200182016040528015613efd57816020015b613eea614ce0565b815260200190600190039081613ee25790505b50905060008a516001600160401b0381118015613f1957600080fd5b50604051908082528060200260200182016040528015613f5357816020015b613f40614ce0565b815260200190600190039081613f385790505b50905060005b8b51811015613fcb57613f718c828151811061350e57fe5b9550613f838484838151811061353757fe5b613f938684838151811061355d57fe5b613fa38983838151811061353757fe5b613fb38683838151811061359357fe5b613fc38583838151811061355d57fe5b600101613f59565b5060008b516001600160401b0381118015613fe557600080fd5b5060405190808252806020026020018201604052801561401f57816020015b61400c614ce0565b8152602001906001900390816140045790505b50905061402f6136256001611029565b60015b60018351038110156140675761405083600183038151811061365357fe5b61405f83828151811061367657fe5b600101614032565b5061407b816001835103815181106136a657fe5b61408e826001845103815181106136d057fe5b614097856146c4565b8251909550600019015b82518110156140e1576140ba8684838151811061353757fe5b6140c982828151811061372257fe5b6140d883828151811061374557fe5b600019016140a1565b5060005b835181101561379d576140fd83828151811061378157fe5b6001016140e5565b60008061411a83600001518660a00151614701565b805190915061412857600080fd5b610100840151614139908290613db5565b60006141456001611029565b90506000614157866101200151613df2565b905060006141656000611029565b905060006141736000611029565b905060005b8851518110156141db576141ac8a60c00151828151811061419557fe5b602002602001015184613ded90919063ffffffff16565b6141c96141c28a60000151838151811061051d57fe5b8490613db5565b6141d38284613e01565b600101614178565b5060c08801516141ec906000613a40565b6141f68382613e01565b8851614203908590613db5565b8851614210908590613db5565b885161421d908590613db5565b885161422a908590613db5565b8851614237908590613db5565b60006142468960e00151613df2565b905060005b60038110156142c5576142748a6101400151826003811061426857fe5b60200201518590613ded565b60208b0151614284908590613db5565b60408b0151614294908590613e01565b6142b38a6080015182600481106142a757fe5b60200201518590613e01565b6142bd8285613db5565b60010161424b565b5060408a01516142d6908490613ded565b60808901516142e69060036138e7565b6142f08184613db5565b6142fa8186613db5565b61430484826146a6565b8951614311908690613db5565b6143258a60c0015160008151811061419557fe5b61432f8386613db5565b61433984846146a6565b5050905192519092149695505050505050565b614354614cc6565b60608201516143649060056122b4565b90506000614370613d78565b9050600061437e6000611029565b905060005b60048110156143c8576143b48660800151826004811061439f57fe5b6020020151866060015183600781106122f857fe5b92506143c08484613de2565b600101614383565b5060808501516143d9906000613a28565b60808501516143e9906001613a40565b60608401516143fb90829060046122f8565b91506144078383613de2565b60a085015151606085015161441e919060066122f8565b915061442a8383613de2565b60c08501515161443b908490614729565b60006144476001611029565b90506144548787836148a4565b915061446b8286608001516001600281106122f857fe5b92506144778484613de2565b8651614484908290613db5565b600061448f82613df2565b905060006144a08960a00151613df2565b90506144b9896020015182613db590919063ffffffff16565b60808801516144c9906000613ac1565b60408901516144d9908290613e01565b60005b60038110156145505760a08a01516144f5908690613ded565b6145088860c001518260038110613c0f57fe5b60208a0151614518908690613db5565b60408a0151614528908690613e01565b61453e89608001518260010160048110613c3e57fe5b6145488286613db5565b6001016144dc565b5061455b8183613db5565b8851614568908490613db5565b6145938960c0015160008151811061457c57fe5b602002602001015185613ded90919063ffffffff16565b61459d8484613db5565b6145a78185613e01565b60006145b36001611029565b905060005b60038110156146325760208b01516145d1908790613ded565b6145f18a610140015182600381106145e557fe5b60200201518790613db5565b60408b0151614601908790613e01565b6146208a60800151826004811061461457fe5b60200201518790613e01565b61462a8287613db5565b6001016145b8565b5060208a0151614643908290613db5565b60e0890151614653908290613db5565b61465d8184613db5565b604089015161466c9083613dcf565b60a089015190965061468c9061468590839060036122f8565b8790613e1b565b6146968787613de2565b60608a0151613ced908890614729565b8051825160008051602061599c833981519152918203900890915250565b6146cc614ce0565b81516146d757600080fd5b6106dd827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff613cfa565b614709614ce0565b6147138284613cfa565b90506106dd6147226001611029565b82906146a6565b6101178282845b614738614d7e565b835181526020840151816001602002015282518160026020020152600060408360608460075afa90508061476b57600080fd5b5050505050565b815115801561478357506020820151155b1561479b578251815260208084015190820152614805565b82511580156147ac57506020830151155b156147c4578151815260208083015190820152614805565b6147cc614d9c565b8351815260208085015181830152835160408301528301518160035b6020020152600060408360808460065afa90508061476b57600080fd5b505050565b815115801561481b57506020820151155b15614833578251815260208084015190820152614805565b825115801561484457506020830151155b1561486c578151815260208083015160008051602061597c8339815191520390820152614805565b614874614d9c565b83518152602080850151818301528351604083015283015160008051602061597c833981519152038160036147e8565b6148ac614ce0565b60006148b86001611029565b905060006148c66002611029565b905060006148d46003611029565b905060006148e26004611029565b90506148ee6000611029565b945060006148fc6000611029565b9050600061490a6000611029565b905060006149186000611029565b905060005b6003811015614a05578b51614933908b90613db5565b6149538b60800151826003036004811061494957fe5b6020020151613df2565b935061495f8486613db5565b6149758b60800151826002036004811061494957fe5b925061498183856146a6565b61498a83613df2565b915061499583613df2565b93506149a184896146a6565b6149ab8285613db5565b6149b483613df2565b93506149c084886146a6565b6149ca8285613db5565b6149d383613df2565b93506149df84876146a6565b6149e98285613db5565b6149f3828b613db5565b6149fd8983613e01565b60010161491d565b508a51614a13908a90613db5565b60808a0151614a23906000614949565b9250614a2f8385613db5565b60a08a0151614a3f906000614949565b9150614a4b82846146a6565b614a5482613df2565b9050614a5f82613df2565b9250614a6b83886146a6565b614a758184613db5565b614a7e82613df2565b9250614a8a83876146a6565b614a948184613db5565b614a9d82613df2565b9250614aa983866146a6565b614ab38184613db5565b614abd818a613db5565b614ac78882613e01565b505050505050509392505050565b604051806101a0016040528060608152602001614af0614dba565b8152602001614afd614cc6565b8152602001614b0a614dba565b8152602001614b17614de7565b8152602001614b24614e14565b8152602001614b31614e14565b8152602001614b3e614ce0565b8152602001614b4b614ce0565b8152602001614b58614ce0565b8152602001614b65614e41565b8152602001614b72614cc6565b8152602001614b7f614cc6565b905290565b6040518061010001604052806000815260200160008152602001614ba6614ce0565b8152602001614bb3614e6e565b8152602001614bc0614cf3565b8152602001614bcd614dba565b8152602001614bda614e41565b8152602001614b7f614d20565b60405180610180016040528060608152602001614c02614dba565b8152602001614c0f614cc6565b8152602001614c1c614dba565b8152602001614b24614de7565b6040518061010001604052806000815260200160008152602001614c4b614ce0565b8152602001614c58614e9b565b8152602001614bc0614ec8565b6040518060e00160405280614c78614ce0565b8152602001614c85614ce0565b8152602001614c92614ce0565b8152602001614c9f614ce0565b8152602001614cac614ce0565b8152602001614cb9614ce0565b8152602001606081525090565b604051806040016040528060008152602001600081525090565b6040518060200160405280600081525090565b60405180604001604052806002905b614d0a614cc6565b815260200190600190039081614d025790505090565b6040518060400160405280614d33614ef5565b8152602001614b7f614ef5565b60405180602001604052806001906020820280368337509192915050565b604080516060810182526000808252602082018190529181019190915290565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b60405180608001604052806004905b614dd1614cc6565b815260200190600190039081614dc95790505090565b60405180608001604052806004905b614dfe614ce0565b815260200190600190039081614df65790505090565b60405180602001604052806001905b614e2b614ce0565b815260200190600190039081614e235790505090565b60405180606001604052806003905b614e58614ce0565b815260200190600190039081614e505790505090565b6040518060e001604052806007905b614e85614cc6565b815260200190600190039081614e7d5790505090565b6040518060c001604052806006905b614eb2614cc6565b815260200190600190039081614eaa5790505090565b60405180602001604052806001905b614edf614cc6565b815260200190600190039081614ed75790505090565b60405180604001604052806002906020820280368337509192915050565b6000614f26614f2184615944565b615921565b9050808260408681870286011115614f3d57600080fd5b60005b86811015614f6657614f528884615268565b845260209093019291810191600101614f40565b505050509392505050565b6000614f7f614f2184615944565b9050808260208681870286011115614f9657600080fd5b60005b86811015614f6657823584529281019291810191600101614f99565b80356001600160a01b038116811461073757600080fd5b600082601f830112614fdc578081fd5b604051606081018181106001600160401b0382111715614ff857fe5b60405280836060810186101561500c578384fd5b835b600381101561503657615021878361522e565b8352602092830192919091019060010161500e565b509195945050505050565b600082601f830112615051578081fd5b604080518181018181106001600160401b038211171561506d57fe5b8252808460808101871015615080578485fd5b845b60028110156150a9576150958883615268565b835260209092019190840190600101615082565b50919695505050505050565b600082601f8301126150c5578081fd5b61011083600484614f13565b600082601f8301126150e1578081fd5b61011083600784614f13565b600082601f8301126150fd578081fd5b61011083601084614f71565b600082601f830112615119578081fd5b61011083600284614f71565b60008083601f840112615136578182fd5b5081356001600160401b0381111561514c578182fd5b602083019150836020808302850101111561109657600080fd5b600082601f830112615176578081fd5b81356020615186614f218361595e565b82815281810190858301838502870184018810156151a2578586fd5b855b858110156151c0578135845292840192908401906001016151a4565b5090979650505050505050565b600082601f8301126151dd578081fd5b813560206151ed614f218361595e565b8281528181019085830183850287018401881015615209578586fd5b855b858110156151c05761521c826153be565b8452928401929084019060010161520b565b60006020828403121561523f578081fd5b604051602081018181106001600160401b038211171561525b57fe5b6040529135825250919050565b600060408284031215615279578081fd5b604051604081018181106001600160401b038211171561529557fe5b604052823581526020928301359281019290925250919050565b6000608082840312156152c0578081fd5b604051604081018181106001600160401b03821117156152dc57fe5b6040529050806152ec8484615109565b81526152fb8460408501615109565b60208201525092915050565b60006104808284031215615319578081fd5b615324610100615921565b90508135815260208201356020820152615341836040840161522e565b604082015261535383606084016150d1565b6060820152615366836102208401615041565b6080820152615379836102a084016150b5565b60a082015261538c836103a08401614fcc565b60c082015261539f8361040084016152af565b60e082015292915050565b803563ffffffff8116811461073757600080fd5b803560ff8116811461073757600080fd5b60008060008060008061028087890312156153e8578182fd5b86356001600160401b03808211156153fe578384fd5b61540a8a838b01615166565b9750602089013591508082111561541f578384fd5b61542b8a838b01615125565b90975095506040890135915080821115615443578384fd5b61544f8a838b016151cd565b94506060890135915080821115615464578384fd5b5061547189828a01615166565b92505061548188608089016150ed565b90509295509295509295565b60008060006104c084860312156154a2578081fd5b83356001600160401b03808211156154b8578283fd5b6154c487838801615166565b945060208601359150808211156154d9578283fd5b506154e686828701615166565b9250506154f68560408601615307565b90509250925092565b600080600080600080600080610740898b03121561551b578586fd5b88356001600160401b0380821115615531578788fd5b61553d8c838d01615166565b995060208b0135915080821115615552578788fd5b61555e8c838d01615166565b985060408b0135975061557360608c016153be565b965060808b0135915080821115615588578384fd5b6155948c838d016151cd565b955060a08b01359150808211156155a9578384fd5b506155b68b828c01615166565b9350506155c68a60c08b016150ed565b91506155d68a6102c08b01615307565b90509295985092959890939650565b6000602082840312156155f6578081fd5b5051919050565b60008060008060008060008060008060006101408c8e03121561561e578485fd5b8b359a5061562e60208d016153aa565b995061563c60408d01614fb5565b985061564a60608d016153aa565b975060808c01356fffffffffffffffffffffffffffffffff8116811461566e578586fd5b965061567c60a08d016153aa565b955061568a60c08d01614fb5565b945061569860e08d016153aa565b93506101008c013592506101208c01356001600160401b038111156156bb578283fd5b6156c78e828f01615125565b915080935050809150509295989b509295989b9093969950565b600080602083850312156156f3578182fd5b82356001600160401b0380821115615709578384fd5b818501915085601f83011261571c578384fd5b81358181111561572a578485fd5b86602082850101111561573b578485fd5b60209290920196919550909350505050565b60008151815b8181101561576d5760208185018101518683015201615753565b8181111561577b5782828601525b509290920192915050565b98895260e097881b6001600160e01b031990811660208b0152606097881b6bffffffffffffffffffffffff1990811660248c015296891b811660388b015260809590951b6fffffffffffffffffffffffffffffffff1916603c8a015292871b8416604c890152941b90921660508601529190921b166064830152606882015260880190565b6000610110828461574d565b6000615823828561574d565b8084835b6010811015615846578151845260209384019390910190600101615827565b50506102000195945050505050565b6000615861828561574d565b9283525050602001919050565b600061587a828561574d565b60f89390931b6001600160f81b03191683525050600101919050565b90815260200190565b60e09490941b6001600160e01b031916845260048401929092526024830152604482015260640190565b6001600160e01b031960e095861b811682526004820194909452602481019290925290921b16604482015260480190565b901515815260200190565b6020808252600290820152616e3160f01b604082015260600190565b6040518181016001600160401b038111828210171561593c57fe5b604052919050565b60006001600160401b0382111561595757fe5b5060200290565b60006001600160401b0382111561597157fe5b506020908102019056fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4730644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a26469706673582212200ee33c0c87e372c1c05888ea8e0625de01d982c8eacd1a938626bcd31fc423ad64736f6c63430007060033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.