Contract Source Code:
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
// Functions named bytesToX, except bytesToBytes20, where X is some type of size N < 32 (size of one word)
// implements the following algorithm:
// f(bytes memory input, uint offset) -> X out
// where byte representation of out is N bytes from input at the given offset
// 1) We compute memory location of the word W such that last N bytes of W is input[offset..offset+N]
// W_address = input + 32 (skip stored length of bytes) + offset - (32 - N) == input + offset + N
// 2) We load W from memory into out, last N bytes of W are placed into out
library Bytes {
function toBytesFromUInt16(uint16 self) internal pure returns (bytes memory _bts) {
return toBytesFromUIntTruncated(uint256(self), 2);
}
function toBytesFromUInt24(uint24 self) internal pure returns (bytes memory _bts) {
return toBytesFromUIntTruncated(uint256(self), 3);
}
function toBytesFromUInt32(uint32 self) internal pure returns (bytes memory _bts) {
return toBytesFromUIntTruncated(uint256(self), 4);
}
function toBytesFromUInt128(uint128 self) internal pure returns (bytes memory _bts) {
return toBytesFromUIntTruncated(uint256(self), 16);
}
// Copies 'len' lower bytes from 'self' into a new 'bytes memory'.
// Returns the newly created 'bytes memory'. The returned bytes will be of length 'len'.
function toBytesFromUIntTruncated(uint256 self, uint8 byteLength) private pure returns (bytes memory bts) {
require(byteLength <= 32, "Q");
bts = new bytes(byteLength);
// Even though the bytes will allocate a full word, we don't want
// any potential garbage bytes in there.
uint256 data = self << ((32 - byteLength) * 8);
assembly {
mstore(
add(bts, 32), // BYTES_HEADER_SIZE
data
)
}
}
// Copies 'self' into a new 'bytes memory'.
// Returns the newly created 'bytes memory'. The returned bytes will be of length '20'.
function toBytesFromAddress(address self) internal pure returns (bytes memory bts) {
bts = toBytesFromUIntTruncated(uint256(self), 20);
}
// See comment at the top of this file for explanation of how this function works.
// NOTE: theoretically possible overflow of (_start + 20)
function bytesToAddress(bytes memory self, uint256 _start) internal pure returns (address addr) {
uint256 offset = _start + 20;
require(self.length >= offset, "R");
assembly {
addr := mload(add(self, offset))
}
}
// Reasoning about why this function works is similar to that of other similar functions, except NOTE below.
// NOTE: that bytes1..32 is stored in the beginning of the word unlike other primitive types
// NOTE: theoretically possible overflow of (_start + 20)
function bytesToBytes20(bytes memory self, uint256 _start) internal pure returns (bytes20 r) {
require(self.length >= (_start + 20), "S");
assembly {
r := mload(add(add(self, 0x20), _start))
}
}
// See comment at the top of this file for explanation of how this function works.
// NOTE: theoretically possible overflow of (_start + 0x2)
function bytesToUInt16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 r) {
uint256 offset = _start + 0x2;
require(_bytes.length >= offset, "T");
assembly {
r := mload(add(_bytes, offset))
}
}
// See comment at the top of this file for explanation of how this function works.
// NOTE: theoretically possible overflow of (_start + 0x3)
function bytesToUInt24(bytes memory _bytes, uint256 _start) internal pure returns (uint24 r) {
uint256 offset = _start + 0x3;
require(_bytes.length >= offset, "U");
assembly {
r := mload(add(_bytes, offset))
}
}
// NOTE: theoretically possible overflow of (_start + 0x4)
function bytesToUInt32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 r) {
uint256 offset = _start + 0x4;
require(_bytes.length >= offset, "V");
assembly {
r := mload(add(_bytes, offset))
}
}
// NOTE: theoretically possible overflow of (_start + 0x10)
function bytesToUInt128(bytes memory _bytes, uint256 _start) internal pure returns (uint128 r) {
uint256 offset = _start + 0x10;
require(_bytes.length >= offset, "W");
assembly {
r := mload(add(_bytes, offset))
}
}
// See comment at the top of this file for explanation of how this function works.
// NOTE: theoretically possible overflow of (_start + 0x14)
function bytesToUInt160(bytes memory _bytes, uint256 _start) internal pure returns (uint160 r) {
uint256 offset = _start + 0x14;
require(_bytes.length >= offset, "X");
assembly {
r := mload(add(_bytes, offset))
}
}
// NOTE: theoretically possible overflow of (_start + 0x20)
function bytesToBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 r) {
uint256 offset = _start + 0x20;
require(_bytes.length >= offset, "Y");
assembly {
r := mload(add(_bytes, offset))
}
}
// Original source code: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol#L228
// Get slice from bytes arrays
// Returns the newly created 'bytes memory'
// NOTE: theoretically possible overflow of (_start + _length)
function slice(
bytes memory _bytes,
uint256 _start,
uint256 _length
) internal pure returns (bytes memory) {
require(_bytes.length >= (_start + _length), "Z"); // bytes length is less then start byte + length bytes
bytes memory tempBytes = new bytes(_length);
if (_length != 0) {
assembly {
let slice_curr := add(tempBytes, 0x20)
let slice_end := add(slice_curr, _length)
for {
let array_current := add(_bytes, add(_start, 0x20))
} lt(slice_curr, slice_end) {
slice_curr := add(slice_curr, 0x20)
array_current := add(array_current, 0x20)
} {
mstore(slice_curr, mload(array_current))
}
}
}
return tempBytes;
}
/// Reads byte stream
/// @return new_offset - offset + amount of bytes read
/// @return data - actually read data
// NOTE: theoretically possible overflow of (_offset + _length)
function read(
bytes memory _data,
uint256 _offset,
uint256 _length
) internal pure returns (uint256 new_offset, bytes memory data) {
data = slice(_data, _offset, _length);
new_offset = _offset + _length;
}
// NOTE: theoretically possible overflow of (_offset + 1)
function readBool(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, bool r) {
new_offset = _offset + 1;
r = uint8(_data[_offset]) != 0;
}
// NOTE: theoretically possible overflow of (_offset + 1)
function readUint8(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, uint8 r) {
new_offset = _offset + 1;
r = uint8(_data[_offset]);
}
// NOTE: theoretically possible overflow of (_offset + 2)
function readUInt16(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, uint16 r) {
new_offset = _offset + 2;
r = bytesToUInt16(_data, _offset);
}
// NOTE: theoretically possible overflow of (_offset + 3)
function readUInt24(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, uint24 r) {
new_offset = _offset + 3;
r = bytesToUInt24(_data, _offset);
}
// NOTE: theoretically possible overflow of (_offset + 4)
function readUInt32(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, uint32 r) {
new_offset = _offset + 4;
r = bytesToUInt32(_data, _offset);
}
// NOTE: theoretically possible overflow of (_offset + 16)
function readUInt128(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, uint128 r) {
new_offset = _offset + 16;
r = bytesToUInt128(_data, _offset);
}
// NOTE: theoretically possible overflow of (_offset + 20)
function readUInt160(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, uint160 r) {
new_offset = _offset + 20;
r = bytesToUInt160(_data, _offset);
}
// NOTE: theoretically possible overflow of (_offset + 20)
function readAddress(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, address r) {
new_offset = _offset + 20;
r = bytesToAddress(_data, _offset);
}
// NOTE: theoretically possible overflow of (_offset + 20)
function readBytes20(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, bytes20 r) {
new_offset = _offset + 20;
r = bytesToBytes20(_data, _offset);
}
// NOTE: theoretically possible overflow of (_offset + 32)
function readBytes32(bytes memory _data, uint256 _offset) internal pure returns (uint256 new_offset, bytes32 r) {
new_offset = _offset + 32;
r = bytesToBytes32(_data, _offset);
}
/// Trim bytes into single word
function trim(bytes memory _data, uint256 _new_length) internal pure returns (uint256 r) {
require(_new_length <= 0x20, "10"); // new_length is longer than word
require(_data.length >= _new_length, "11"); // data is to short
uint256 a;
assembly {
a := mload(add(_data, 0x20)) // load bytes into uint256
}
return a >> ((0x20 - _new_length) * 8);
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
import "../Bytes.sol";
contract BytesTest {
function read(
bytes calldata _data,
uint256 _offset,
uint256 _len
) external pure returns (uint256 new_offset, bytes memory data) {
return Bytes.read(_data, _offset, _len);
}
function testUInt24(uint24 x) external pure returns (uint24 r, uint256 offset) {
require(keccak256(new bytes(0)) == keccak256(new bytes(0)));
bytes memory buf = Bytes.toBytesFromUInt24(x);
(offset, r) = Bytes.readUInt24(buf, 0);
}
}
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./ReentrancyGuard.sol";
import "./SafeMath.sol";
import "./SafeMathUInt128.sol";
import "./SafeCast.sol";
import "./Utils.sol";
import "./Storage.sol";
import "./Config.sol";
import "./Events.sol";
import "./Bytes.sol";
import "./Operations.sol";
import "./UpgradeableMaster.sol";
/// @title zkSync main contract
/// @author Matter Labs
contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
using SafeMath for uint256;
using SafeMathUInt128 for uint128;
bytes32 private constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
/// @notice Data needed to process onchain operation from block public data.
/// @notice Onchain operations is operations that need some processing on L1: Deposits, Withdrawals, ChangePubKey.
/// @param ethWitness Some external data that can be needed for operation processing
/// @param publicDataOffset Byte offset in public data for onchain operation
struct OnchainOperationData {
bytes ethWitness;
uint32 publicDataOffset;
}
/// @notice Data needed to commit new block
struct CommitBlockInfo {
bytes32 newStateHash;
bytes publicData;
uint256 timestamp;
OnchainOperationData[] onchainOperations;
uint32 blockNumber;
uint32 feeAccount;
}
/// @notice Data needed to execute committed and verified block
/// @param commitmentsInSlot verified commitments in one slot
/// @param commitmentIdx index such that commitmentsInSlot[commitmentIdx] is current block commitment
struct ExecuteBlockInfo {
StoredBlockInfo storedBlock;
bytes[] pendingOnchainOpsPubdata;
}
/// @notice Recursive proof input data (individual commitments are constructed onchain)
struct ProofInput {
uint256[] recursiveInput;
uint256[] proof;
uint256[] commitments;
uint8[] vkIndexes;
uint256[16] subproofsLimbs;
}
// Upgrade functional
/// @notice Notice period before activation preparation status of upgrade mode
function getNoticePeriod() external pure override returns (uint256) {
return UPGRADE_NOTICE_PERIOD;
}
/// @notice Notification that upgrade notice period started
/// @dev Can be external because Proxy contract intercepts illegal calls of this function
function upgradeNoticePeriodStarted() external override {}
/// @notice Notification that upgrade preparation status is activated
/// @dev Can be external because Proxy contract intercepts illegal calls of this function
function upgradePreparationStarted() external override {
upgradePreparationActive = true;
upgradePreparationActivationTime = block.timestamp;
}
/// @notice Notification that upgrade canceled
/// @dev Can be external because Proxy contract intercepts illegal calls of this function
function upgradeCanceled() external override {
upgradePreparationActive = false;
upgradePreparationActivationTime = 0;
}
/// @notice Notification that upgrade finishes
/// @dev Can be external because Proxy contract intercepts illegal calls of this function
function upgradeFinishes() external override {
upgradePreparationActive = false;
upgradePreparationActivationTime = 0;
}
/// @notice Checks that contract is ready for upgrade
/// @return bool flag indicating that contract is ready for upgrade
function isReadyForUpgrade() external view override returns (bool) {
return !exodusMode;
}
/// @notice zkSync contract initialization. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param initializationParameters Encoded representation of initialization parameters:
/// @dev _governanceAddress The address of Governance contract
/// @dev _verifierAddress The address of Verifier contract
/// @dev _genesisStateHash Genesis blocks (first block) state tree root hash
function initialize(bytes calldata initializationParameters) external {
initializeReentrancyGuard();
(address _governanceAddress, address _verifierAddress, bytes32 _genesisStateHash) =
abi.decode(initializationParameters, (address, address, bytes32));
verifier = Verifier(_verifierAddress);
governance = Governance(_governanceAddress);
// We need initial state hash because it is used in the commitment of the next block
StoredBlockInfo memory storedBlockZero =
StoredBlockInfo(0, 0, EMPTY_STRING_KECCAK, 0, _genesisStateHash, bytes32(0));
storedBlockHashes[0] = hashStoredBlockInfo(storedBlockZero);
}
/// @notice zkSync contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param upgradeParameters Encoded representation of upgrade parameters
function upgrade(bytes calldata upgradeParameters) external nonReentrant {
// NOTE: this line does not have any effect in contracts-4 upgrade since we require priority queue to be empty,
// but this should be enabled in future upgrades.
activateExodusMode();
require(upgradeParameters.length == 0, "0"); // upgrade parameters should be empty
// Convert last verified block from old format to new format
require(totalBlocksCommitted == totalBlocksExecuted, "1"); // all blocks should be verified
require(numberOfPendingWithdrawals_DEPRECATED == 0, "2"); // pending withdrawal is not used anymore
require(totalOpenPriorityRequests == 0, "3"); // no uncommitted priority requests
Block_DEPRECATED memory lastBlock = blocks_DEPRECATED[totalBlocksExecuted];
require(lastBlock.priorityOperations == 0, "4"); // last block should not contain priority operations
StoredBlockInfo memory rehashedLastBlock =
StoredBlockInfo(
totalBlocksExecuted,
lastBlock.priorityOperations,
EMPTY_STRING_KECCAK,
0,
lastBlock.stateRoot,
lastBlock.commitment
);
storedBlockHashes[totalBlocksExecuted] = hashStoredBlockInfo(rehashedLastBlock);
totalBlocksProven = totalBlocksExecuted;
}
/// @notice Sends tokens
/// @dev NOTE: will revert if transfer call fails or rollup balance difference (before and after transfer) is bigger than _maxAmount
/// @dev This function is used to allow tokens to spend zkSync contract balance up to amount that is requested
/// @param _token Token address
/// @param _to Address of recipient
/// @param _amount Amount of tokens to transfer
/// @param _maxAmount Maximum possible amount of tokens to transfer to this account
function _transferERC20(
IERC20 _token,
address _to,
uint128 _amount,
uint128 _maxAmount
) external returns (uint128 withdrawnAmount) {
require(msg.sender == address(this), "5"); // wtg10 - can be called only from this contract as one "external" call (to revert all this function state changes if it is needed)
uint256 balanceBefore = _token.balanceOf(address(this));
require(Utils.sendERC20(_token, _to, _amount), "6"); // wtg11 - ERC20 transfer fails
uint256 balanceAfter = _token.balanceOf(address(this));
uint256 balanceDiff = balanceBefore.sub(balanceAfter);
require(balanceDiff <= _maxAmount, "7"); // wtg12 - rollup balance difference (before and after transfer) is bigger than _maxAmount
return SafeCast.toUint128(balanceDiff);
}
/// @notice Accrues users balances from deposit priority requests in Exodus mode
/// @dev WARNING: Only for Exodus mode
/// @dev Canceling may take several separate transactions to be completed
/// @param _n number of requests to process
function cancelOutstandingDepositsForExodusMode(uint64 _n, bytes[] memory _depositsPubdata) external nonReentrant {
require(exodusMode, "8"); // exodus mode not active
uint64 toProcess = Utils.minU64(totalOpenPriorityRequests, _n);
require(toProcess > 0, "9"); // no deposits to process
uint64 currentDepositIdx = 0;
for (uint64 id = firstPriorityRequestId; id < firstPriorityRequestId + toProcess; id++) {
if (priorityRequests[id].opType == Operations.OpType.Deposit) {
bytes memory depositPubdata = _depositsPubdata[currentDepositIdx];
require(Utils.hashBytesToBytes20(depositPubdata) == priorityRequests[id].hashedPubData, "a");
++currentDepositIdx;
Operations.Deposit memory op = Operations.readDepositPubdata(depositPubdata);
bytes22 packedBalanceKey = packAddressAndTokenId(op.owner, op.tokenId);
pendingBalances[packedBalanceKey].balanceToWithdraw += op.amount;
}
delete priorityRequests[id];
}
firstPriorityRequestId += toProcess;
totalOpenPriorityRequests -= toProcess;
}
/// @notice Deposit ETH to Layer 2 - transfer ether from user into contract, validate it, register deposit
/// @param _zkSyncAddress The receiver Layer 2 address
function depositETH(address _zkSyncAddress) external payable {
requireActive();
registerDeposit(0, SafeCast.toUint128(msg.value), _zkSyncAddress);
}
/// @notice Deposit ERC20 token to Layer 2 - transfer ERC20 tokens from user into contract, validate it, register deposit
/// @param _token Token address
/// @param _amount Token amount
/// @param _zkSyncAddress Receiver Layer 2 address
function depositERC20(
IERC20 _token,
uint104 _amount,
address _zkSyncAddress
) external nonReentrant {
requireActive();
// Get token id by its address
uint16 tokenId = governance.validateTokenAddress(address(_token));
require(!governance.pausedTokens(tokenId), "b"); // token deposits are paused
uint256 balanceBefore = _token.balanceOf(address(this));
require(Utils.transferFromERC20(_token, msg.sender, address(this), SafeCast.toUint128(_amount)), "c"); // token transfer failed deposit
uint256 balanceAfter = _token.balanceOf(address(this));
uint128 depositAmount = SafeCast.toUint128(balanceAfter.sub(balanceBefore));
registerDeposit(tokenId, depositAmount, _zkSyncAddress);
}
/// @notice Returns amount of tokens that can be withdrawn by `address` from zkSync contract
/// @param _address Address of the tokens owner
/// @param _token Address of token, zero address is used for ETH
function getPendingBalance(address _address, address _token) public view returns (uint128) {
uint16 tokenId = 0;
if (_token != address(0)) {
tokenId = governance.validateTokenAddress(_token);
}
return pendingBalances[packAddressAndTokenId(_address, tokenId)].balanceToWithdraw;
}
/// @notice Withdraws tokens from zkSync contract to the owner
/// @param _owner Address of the tokens owner
/// @param _token Address of tokens, zero address is used for ETH
/// @param _amount Amount to withdraw to request.
/// NOTE: We will call ERC20.transfer(.., _amount), but if according to internal logic of ERC20 token zkSync contract
/// balance will be decreased by value more then _amount we will try to subtract this value from user pending balance
function withdrawPendingBalance(
address payable _owner,
address _token,
uint128 _amount
) external nonReentrant {
if (_token == address(0)) {
registerWithdrawal(0, _amount, _owner);
(bool success, ) = _owner.call{value: _amount}("");
require(success, "d"); // ETH withdraw failed
} else {
uint16 tokenId = governance.validateTokenAddress(_token);
bytes22 packedBalanceKey = packAddressAndTokenId(_owner, tokenId);
uint128 balance = pendingBalances[packedBalanceKey].balanceToWithdraw;
// We will allow withdrawals of `value` such that:
// `value` <= user pending balance
// `value` can be bigger then `_amount` requested if token takes fee from sender in addition to `_amount` requested
uint128 withdrawnAmount = this._transferERC20(IERC20(_token), _owner, _amount, balance);
registerWithdrawal(tokenId, withdrawnAmount, _owner);
}
}
/// @notice Register full exit request - pack pubdata, add priority request
/// @param _accountId Numerical id of the account
/// @param _token Token address, 0 address for ether
function requestFullExit(uint32 _accountId, address _token) external nonReentrant {
requireActive();
require(_accountId <= MAX_ACCOUNT_ID, "e");
uint16 tokenId;
if (_token == address(0)) {
tokenId = 0;
} else {
tokenId = governance.validateTokenAddress(_token);
}
// Priority Queue request
Operations.FullExit memory op =
Operations.FullExit({
accountId: _accountId,
owner: msg.sender,
tokenId: tokenId,
amount: 0 // unknown at this point
});
bytes memory pubData = Operations.writeFullExitPubdataForPriorityQueue(op);
addPriorityRequest(Operations.OpType.FullExit, pubData);
// User must fill storage slot of balancesToWithdraw(msg.sender, tokenId) with nonzero value
// In this case operator should just overwrite this slot during confirming withdrawal
bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, tokenId);
pendingBalances[packedBalanceKey].gasReserveValue = FILLED_GAS_RESERVE_VALUE;
}
/// @dev Process one block commit using previous block StoredBlockInfo,
/// @dev returns new block StoredBlockInfo
/// @dev NOTE: Does not change storage (except events, so we can't mark it view)
function commitOneBlock(StoredBlockInfo memory _previousBlock, CommitBlockInfo memory _newBlock)
internal
view
returns (StoredBlockInfo memory storedNewBlock)
{
require(_newBlock.blockNumber == _previousBlock.blockNumber + 1, "f"); // only commit next block
// Check timestamp of the new block
{
require(_newBlock.timestamp >= _previousBlock.timestamp, "g"); // Block should be after previous block
bool timestampNotTooSmall = block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER <= _newBlock.timestamp;
bool timestampNotTooBig = _newBlock.timestamp <= block.timestamp + COMMIT_TIMESTAMP_APPROXIMATION_DELTA;
require(timestampNotTooSmall && timestampNotTooBig, "h"); // New block timestamp is not valid
}
// Check onchain operations
(bytes32 pendingOnchainOpsHash, uint64 priorityReqCommitted, bytes memory onchainOpsOffsetCommitment) =
collectOnchainOps(_newBlock);
// Create block commitment for verification proof
bytes32 commitment = createBlockCommitment(_previousBlock, _newBlock, onchainOpsOffsetCommitment);
return
StoredBlockInfo(
_newBlock.blockNumber,
priorityReqCommitted,
pendingOnchainOpsHash,
_newBlock.timestamp,
_newBlock.newStateHash,
commitment
);
}
/// @notice Commit block
/// @notice 1. Checks onchain operations, timestamp.
/// @notice 2. Store block commitments
function commitBlocks(StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] memory _newBlocksData)
external
nonReentrant
{
requireActive();
governance.requireActiveValidator(msg.sender);
// Check that we commit blocks after last committed block
require(storedBlockHashes[totalBlocksCommitted] == hashStoredBlockInfo(_lastCommittedBlockData), "i"); // incorrect previous block data
for (uint32 i = 0; i < _newBlocksData.length; ++i) {
_lastCommittedBlockData = commitOneBlock(_lastCommittedBlockData, _newBlocksData[i]);
totalCommittedPriorityRequests += _lastCommittedBlockData.priorityOperations;
storedBlockHashes[_lastCommittedBlockData.blockNumber] = hashStoredBlockInfo(_lastCommittedBlockData);
emit BlockCommit(_lastCommittedBlockData.blockNumber);
}
totalBlocksCommitted += uint32(_newBlocksData.length);
require(totalCommittedPriorityRequests <= totalOpenPriorityRequests, "j");
}
/// @dev 1. Try to send token to _recipients
/// @dev 2. On failure: Increment _recipients balance to withdraw.
function withdrawOrStore(
uint16 _tokenId,
address _recipient,
uint128 _amount
) internal {
bytes22 packedBalanceKey = packAddressAndTokenId(_recipient, _tokenId);
bool sent = false;
if (_tokenId == 0) {
address payable toPayable = address(uint160(_recipient));
sent = sendETHNoRevert(toPayable, _amount);
} else {
address tokenAddr = governance.tokenAddresses(_tokenId);
// We use `_transferERC20` here to check that `ERC20` token indeed transferred `_amount`
// and fail if token subtracted from zkSync balance more then `_amount` that was requested.
// This can happen if token subtracts fee from sender while transferring `_amount` that was requested to transfer.
try this._transferERC20{gas: WITHDRAWAL_GAS_LIMIT}(IERC20(tokenAddr), _recipient, _amount, _amount) {
sent = true;
} catch {
sent = false;
}
}
if (sent) {
emit Withdrawal(_tokenId, _amount);
} else {
increaseBalanceToWithdraw(packedBalanceKey, _amount);
}
}
/// @dev Executes one block
/// @dev 1. Processes all pending operations (Send Exits, Complete priority requests)
/// @dev 2. Finalizes block on Ethereum
/// @dev _executedBlockIdx is index in the array of the blocks that we want to execute together
function executeOneBlock(ExecuteBlockInfo memory _blockExecuteData, uint32 _executedBlockIdx) internal {
// Ensure block was committed
require(
hashStoredBlockInfo(_blockExecuteData.storedBlock) ==
storedBlockHashes[_blockExecuteData.storedBlock.blockNumber],
"exe10" // executing block should be committed
);
require(_blockExecuteData.storedBlock.blockNumber == totalBlocksExecuted + _executedBlockIdx + 1, "k"); // Execute blocks in order
bytes32 pendingOnchainOpsHash = EMPTY_STRING_KECCAK;
for (uint32 i = 0; i < _blockExecuteData.pendingOnchainOpsPubdata.length; ++i) {
bytes memory pubData = _blockExecuteData.pendingOnchainOpsPubdata[i];
Operations.OpType opType = Operations.OpType(uint8(pubData[0]));
if (opType == Operations.OpType.PartialExit) {
Operations.PartialExit memory op = Operations.readPartialExitPubdata(pubData);
withdrawOrStore(op.tokenId, op.owner, op.amount);
} else if (opType == Operations.OpType.ForcedExit) {
Operations.ForcedExit memory op = Operations.readForcedExitPubdata(pubData);
withdrawOrStore(op.tokenId, op.target, op.amount);
} else if (opType == Operations.OpType.FullExit) {
Operations.FullExit memory op = Operations.readFullExitPubdata(pubData);
withdrawOrStore(op.tokenId, op.owner, op.amount);
} else {
revert("l"); // unsupported op in block execution
}
pendingOnchainOpsHash = Utils.concatHash(pendingOnchainOpsHash, pubData);
}
require(pendingOnchainOpsHash == _blockExecuteData.storedBlock.pendingOnchainOperationsHash, "m"); // incorrect onchain ops executed
}
/// @notice Execute blocks, completing priority operations and processing withdrawals.
/// @notice 1. Processes all pending operations (Send Exits, Complete priority requests)
/// @notice 2. Finalizes block on Ethereum
function executeBlocks(ExecuteBlockInfo[] memory _blocksData) external nonReentrant {
requireActive();
governance.requireActiveValidator(msg.sender);
uint64 priorityRequestsExecuted = 0;
uint32 nBlocks = uint32(_blocksData.length);
for (uint32 i = 0; i < nBlocks; ++i) {
executeOneBlock(_blocksData[i], i);
priorityRequestsExecuted += _blocksData[i].storedBlock.priorityOperations;
emit BlockVerification(_blocksData[i].storedBlock.blockNumber);
}
firstPriorityRequestId += priorityRequestsExecuted;
totalCommittedPriorityRequests -= priorityRequestsExecuted;
totalOpenPriorityRequests -= priorityRequestsExecuted;
totalBlocksExecuted += nBlocks;
require(totalBlocksExecuted <= totalBlocksProven, "n"); // Can't execute blocks more then committed and proven currently.
}
/// @notice Blocks commitment verification.
/// @notice Only verifies block commitments without any other processing
function proveBlocks(StoredBlockInfo[] memory _committedBlocks, ProofInput memory _proof) external nonReentrant {
uint32 currentTotalBlocksProven = totalBlocksProven;
for (uint256 i = 0; i < _committedBlocks.length; ++i) {
require(hashStoredBlockInfo(_committedBlocks[i]) == storedBlockHashes[currentTotalBlocksProven + 1], "o1");
++currentTotalBlocksProven;
require(_proof.commitments[i] & INPUT_MASK == uint256(_committedBlocks[i].commitment) & INPUT_MASK, "o"); // incorrect block commitment in proof
}
bool success =
verifier.verifyAggregatedBlockProof(
_proof.recursiveInput,
_proof.proof,
_proof.vkIndexes,
_proof.commitments,
_proof.subproofsLimbs
);
require(success, "p"); // Aggregated proof verification fail
require(currentTotalBlocksProven <= totalBlocksCommitted, "q");
totalBlocksProven = currentTotalBlocksProven;
}
/// @notice Reverts unverified blocks
function revertBlocks(StoredBlockInfo[] memory _blocksToRevert) external nonReentrant {
governance.requireActiveValidator(msg.sender);
uint32 blocksCommitted = totalBlocksCommitted;
uint32 blocksToRevert = Utils.minU32(uint32(_blocksToRevert.length), blocksCommitted - totalBlocksExecuted);
uint64 revertedPriorityRequests = 0;
for (uint32 i = 0; i < blocksToRevert; ++i) {
StoredBlockInfo memory storedBlockInfo = _blocksToRevert[i];
require(storedBlockHashes[blocksCommitted] == hashStoredBlockInfo(storedBlockInfo), "r"); // incorrect stored block info
delete storedBlockHashes[blocksCommitted];
--blocksCommitted;
revertedPriorityRequests += storedBlockInfo.priorityOperations;
}
totalBlocksCommitted = blocksCommitted;
totalCommittedPriorityRequests -= revertedPriorityRequests;
if (totalBlocksCommitted < totalBlocksProven) {
totalBlocksProven = totalBlocksCommitted;
}
emit BlocksRevert(totalBlocksExecuted, blocksCommitted);
}
/// @notice Checks if Exodus mode must be entered. If true - enters exodus mode and emits ExodusMode event.
/// @dev Exodus mode must be entered in case of current ethereum block number is higher than the oldest
/// @dev of existed priority requests expiration block number.
/// @return bool flag that is true if the Exodus mode must be entered.
function activateExodusMode() public returns (bool) {
bool trigger =
block.number >= priorityRequests[firstPriorityRequestId].expirationBlock &&
priorityRequests[firstPriorityRequestId].expirationBlock != 0;
if (trigger) {
if (!exodusMode) {
exodusMode = true;
emit ExodusMode();
}
return true;
} else {
return false;
}
}
/// @notice Withdraws token from ZkSync to root chain in case of exodus mode. User must provide proof that he owns funds
/// @param _storedBlockInfo Last verified block
/// @param _owner Owner of the account
/// @param _accountId Id of the account in the tree
/// @param _proof Proof
/// @param _tokenId Verified token id
/// @param _amount Amount for owner (must be total amount, not part of it)
function performExodus(
StoredBlockInfo memory _storedBlockInfo,
address _owner,
uint32 _accountId,
uint16 _tokenId,
uint128 _amount,
uint256[] memory _proof
) external nonReentrant {
bytes22 packedBalanceKey = packAddressAndTokenId(_owner, _tokenId);
require(exodusMode, "s"); // must be in exodus mode
require(!performedExodus[_accountId][_tokenId], "t"); // already exited
require(storedBlockHashes[totalBlocksExecuted] == hashStoredBlockInfo(_storedBlockInfo), "u"); // incorrect sotred block info
bool proofCorrect =
verifier.verifyExitProof(_storedBlockInfo.stateHash, _accountId, _owner, _tokenId, _amount, _proof);
require(proofCorrect, "x");
increaseBalanceToWithdraw(packedBalanceKey, _amount);
performedExodus[_accountId][_tokenId] = true;
}
/// @notice Set data for changing pubkey hash using onchain authorization.
/// Transaction author (msg.sender) should be L2 account address
/// @notice New pubkey hash can be reset, to do that user should send two transactions:
/// 1) First `setAuthPubkeyHash` transaction for already used `_nonce` will set timer.
/// 2) After `AUTH_FACT_RESET_TIMELOCK` time is passed second `setAuthPubkeyHash` transaction will reset pubkey hash for `_nonce`.
/// @param _pubkey_hash New pubkey hash
/// @param _nonce Nonce of the change pubkey L2 transaction
function setAuthPubkeyHash(bytes calldata _pubkey_hash, uint32 _nonce) external {
require(_pubkey_hash.length == PUBKEY_HASH_BYTES, "y"); // PubKeyHash should be 20 bytes.
if (authFacts[msg.sender][_nonce] == bytes32(0)) {
authFacts[msg.sender][_nonce] = keccak256(_pubkey_hash);
} else {
uint256 currentResetTimer = authFactsResetTimer[msg.sender][_nonce];
if (currentResetTimer == 0) {
authFactsResetTimer[msg.sender][_nonce] = block.timestamp;
} else {
require(block.timestamp.sub(currentResetTimer) >= AUTH_FACT_RESET_TIMELOCK, "z");
authFactsResetTimer[msg.sender][_nonce] = 0;
authFacts[msg.sender][_nonce] = keccak256(_pubkey_hash);
}
}
}
/// @notice Register deposit request - pack pubdata, add priority request and emit OnchainDeposit event
/// @param _tokenId Token by id
/// @param _amount Token amount
/// @param _owner Receiver
function registerDeposit(
uint16 _tokenId,
uint128 _amount,
address _owner
) internal {
// Priority Queue request
Operations.Deposit memory op =
Operations.Deposit({
accountId: 0, // unknown at this point
owner: _owner,
tokenId: _tokenId,
amount: _amount
});
bytes memory pubData = Operations.writeDepositPubdataForPriorityQueue(op);
addPriorityRequest(Operations.OpType.Deposit, pubData);
emit Deposit(_tokenId, _amount);
}
/// @notice Register withdrawal - update user balance and emit OnchainWithdrawal event
/// @param _token - token by id
/// @param _amount - token amount
/// @param _to - address to withdraw to
function registerWithdrawal(
uint16 _token,
uint128 _amount,
address payable _to
) internal {
bytes22 packedBalanceKey = packAddressAndTokenId(_to, _token);
uint128 balance = pendingBalances[packedBalanceKey].balanceToWithdraw;
pendingBalances[packedBalanceKey].balanceToWithdraw = balance.sub(_amount);
emit Withdrawal(_token, _amount);
}
/// @dev Gets operations packed in bytes array. Unpacks it and stores onchain operations.
/// @dev Priority operations must be committed in the same order as they are in the priority queue.
/// @dev NOTE: does not change storage! (only emits events)
/// @dev processableOperationsHash - hash of the all operations that needs to be executed (Deposit, Exits, ChangPubKey)
/// @dev priorityOperationsProcessed - number of priority operations processed in this block (Deposits, FullExits)
/// @dev offsetsCommitment - array where 1 is stored in chunk where onchainOperation begins and other are 0 (used in commitments)
function collectOnchainOps(CommitBlockInfo memory _newBlockData)
internal
view
returns (
bytes32 processableOperationsHash,
uint64 priorityOperationsProcessed,
bytes memory offsetsCommitment
)
{
bytes memory pubData = _newBlockData.publicData;
uint64 uncommittedPriorityRequestsOffset = firstPriorityRequestId + totalCommittedPriorityRequests;
priorityOperationsProcessed = 0;
processableOperationsHash = EMPTY_STRING_KECCAK;
require(pubData.length % CHUNK_BYTES == 0, "A"); // pubdata length must be a multiple of CHUNK_BYTES
offsetsCommitment = new bytes(pubData.length / CHUNK_BYTES);
for (uint256 i = 0; i < _newBlockData.onchainOperations.length; ++i) {
OnchainOperationData memory onchainOpData = _newBlockData.onchainOperations[i];
uint256 pubdataOffset = onchainOpData.publicDataOffset;
require(pubdataOffset % CHUNK_BYTES == 0, "B"); // offsets should be on chunks boundaries
uint256 chunkId = pubdataOffset / CHUNK_BYTES;
require(offsetsCommitment[chunkId] == 0x00, "C"); // offset commitment should be empty
offsetsCommitment[chunkId] = bytes1(0x01);
Operations.OpType opType = Operations.OpType(uint8(pubData[pubdataOffset]));
if (opType == Operations.OpType.Deposit) {
bytes memory opPubData = Bytes.slice(pubData, pubdataOffset, DEPOSIT_BYTES);
Operations.Deposit memory depositData = Operations.readDepositPubdata(opPubData);
checkPriorityOperation(depositData, uncommittedPriorityRequestsOffset + priorityOperationsProcessed);
priorityOperationsProcessed++;
} else if (opType == Operations.OpType.ChangePubKey) {
bytes memory opPubData = Bytes.slice(pubData, pubdataOffset, CHANGE_PUBKEY_BYTES);
Operations.ChangePubKey memory op = Operations.readChangePubKeyPubdata(opPubData);
if (onchainOpData.ethWitness.length != 0) {
bool valid = verifyChangePubkey(onchainOpData.ethWitness, op);
require(valid, "D"); // failed to verify change pubkey hash signature
} else {
bool valid = authFacts[op.owner][op.nonce] == keccak256(abi.encodePacked(op.pubKeyHash));
require(valid, "E"); // new pub key hash is not authenticated properly
}
} else {
bytes memory opPubData;
if (opType == Operations.OpType.PartialExit) {
opPubData = Bytes.slice(pubData, pubdataOffset, PARTIAL_EXIT_BYTES);
} else if (opType == Operations.OpType.ForcedExit) {
opPubData = Bytes.slice(pubData, pubdataOffset, FORCED_EXIT_BYTES);
} else if (opType == Operations.OpType.FullExit) {
opPubData = Bytes.slice(pubData, pubdataOffset, FULL_EXIT_BYTES);
Operations.FullExit memory fullExitData = Operations.readFullExitPubdata(opPubData);
checkPriorityOperation(
fullExitData,
uncommittedPriorityRequestsOffset + priorityOperationsProcessed
);
priorityOperationsProcessed++;
} else {
revert("F"); // unsupported op
}
processableOperationsHash = Utils.concatHash(processableOperationsHash, opPubData);
}
}
}
/// @notice Checks that change operation is correct
function verifyChangePubkey(bytes memory _ethWitness, Operations.ChangePubKey memory _changePk)
internal
pure
returns (bool)
{
Operations.ChangePubkeyType changePkType = Operations.ChangePubkeyType(uint8(_ethWitness[0]));
if (changePkType == Operations.ChangePubkeyType.ECRECOVER) {
return verifyChangePubkeyECRECOVER(_ethWitness, _changePk);
} else if (changePkType == Operations.ChangePubkeyType.CREATE2) {
return verifyChangePubkeyCREATE2(_ethWitness, _changePk);
} else {
revert("G"); // Incorrect ChangePubKey type
}
}
/// @notice Checks that signature is valid for pubkey change message
/// @param _ethWitness Signature (65 bytes) + 32 bytes of the arbitrary signed data
/// @param _changePk Parsed change pubkey operation
function verifyChangePubkeyECRECOVER(bytes memory _ethWitness, Operations.ChangePubKey memory _changePk)
internal
pure
returns (bool)
{
(, bytes memory signature) = Bytes.read(_ethWitness, 1, 65); // offset is 1 because we skip type of ChangePubkey
// (, bytes32 additionalData) = Bytes.readBytes32(_ethWitness, offset);
bytes32 messageHash =
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n60",
_changePk.pubKeyHash,
_changePk.nonce,
_changePk.accountId,
bytes32(0)
)
);
address recoveredAddress = Utils.recoverAddressFromEthSignature(signature, messageHash);
return recoveredAddress == _changePk.owner && recoveredAddress != address(0);
}
/// @notice Checks that signature is valid for pubkey change message
/// @param _ethWitness Create2 deployer address, saltArg, codeHash
/// @param _changePk Parsed change pubkey operation
function verifyChangePubkeyCREATE2(bytes memory _ethWitness, Operations.ChangePubKey memory _changePk)
internal
pure
returns (bool)
{
address creatorAddress;
bytes32 saltArg; // salt arg is additional bytes that are encoded in the CREATE2 salt
bytes32 codeHash;
uint256 offset = 1; // offset is 1 because we skip type of ChangePubkey
(offset, creatorAddress) = Bytes.readAddress(_ethWitness, offset);
(offset, saltArg) = Bytes.readBytes32(_ethWitness, offset);
(offset, codeHash) = Bytes.readBytes32(_ethWitness, offset);
// salt from CREATE2 specification
bytes32 salt = keccak256(abi.encodePacked(saltArg, _changePk.pubKeyHash));
// Address computation according to CREATE2 definition: https://eips.ethereum.org/EIPS/eip-1014
address recoveredAddress =
address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), creatorAddress, salt, codeHash)))));
// This type of change pubkey can be done only once
return recoveredAddress == _changePk.owner && _changePk.nonce == 0;
}
/// @dev Creates block commitment from its data
/// @dev _offsetCommitment - hash of the array where 1 is stored in chunk where onchainOperation begins and 0 for other chunks
function createBlockCommitment(
StoredBlockInfo memory _previousBlock,
CommitBlockInfo memory _newBlockData,
bytes memory _offsetCommitment
) internal view returns (bytes32 commitment) {
bytes32 hash = sha256(abi.encodePacked(uint256(_newBlockData.blockNumber), uint256(_newBlockData.feeAccount)));
hash = sha256(abi.encodePacked(hash, _previousBlock.stateHash));
hash = sha256(abi.encodePacked(hash, _newBlockData.newStateHash));
hash = sha256(abi.encodePacked(hash, uint256(_newBlockData.timestamp)));
bytes memory pubdata = abi.encodePacked(_newBlockData.publicData, _offsetCommitment);
/// The code below is equivalent to `commitment = sha256(abi.encodePacked(hash, _publicData))`
/// We use inline assembly instead of this concise and readable code in order to avoid copying of `_publicData` (which saves ~90 gas per transfer operation).
/// Specifically, we perform the following trick:
/// First, replace the first 32 bytes of `_publicData` (where normally its length is stored) with the value of `hash`.
/// Then, we call `sha256` precompile passing the `_publicData` pointer and the length of the concatenated byte buffer.
/// Finally, we put the `_publicData.length` back to its original location (to the first word of `_publicData`).
assembly {
let hashResult := mload(0x40)
let pubDataLen := mload(pubdata)
mstore(pubdata, hash)
// staticcall to the sha256 precompile at address 0x2
let success := staticcall(gas(), 0x2, pubdata, add(pubDataLen, 0x20), hashResult, 0x20)
mstore(pubdata, pubDataLen)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
commitment := mload(hashResult)
}
}
/// @notice Checks that deposit is same as operation in priority queue
/// @param _deposit Deposit data
/// @param _priorityRequestId Operation's id in priority queue
function checkPriorityOperation(Operations.Deposit memory _deposit, uint64 _priorityRequestId) internal view {
Operations.OpType priorReqType = priorityRequests[_priorityRequestId].opType;
require(priorReqType == Operations.OpType.Deposit, "H"); // incorrect priority op type
bytes20 hashedPubdata = priorityRequests[_priorityRequestId].hashedPubData;
require(Operations.checkDepositInPriorityQueue(_deposit, hashedPubdata), "I");
}
/// @notice Checks that FullExit is same as operation in priority queue
/// @param _fullExit FullExit data
/// @param _priorityRequestId Operation's id in priority queue
function checkPriorityOperation(Operations.FullExit memory _fullExit, uint64 _priorityRequestId) internal view {
Operations.OpType priorReqType = priorityRequests[_priorityRequestId].opType;
require(priorReqType == Operations.OpType.FullExit, "J"); // incorrect priority op type
bytes20 hashedPubdata = priorityRequests[_priorityRequestId].hashedPubData;
require(Operations.checkFullExitInPriorityQueue(_fullExit, hashedPubdata), "K");
}
/// @notice Checks that current state not is exodus mode
function requireActive() internal view {
require(!exodusMode, "L"); // exodus mode activated
}
// Priority queue
/// @notice Saves priority request in storage
/// @dev Calculates expiration block for request, store this request and emit NewPriorityRequest event
/// @param _opType Rollup operation type
/// @param _pubData Operation pubdata
function addPriorityRequest(Operations.OpType _opType, bytes memory _pubData) internal {
// Expiration block is: current block number + priority expiration delta
uint64 expirationBlock = uint64(block.number + PRIORITY_EXPIRATION);
uint64 nextPriorityRequestId = firstPriorityRequestId + totalOpenPriorityRequests;
bytes20 hashedPubData = Utils.hashBytesToBytes20(_pubData);
priorityRequests[nextPriorityRequestId] = PriorityOperation({
hashedPubData: hashedPubData,
expirationBlock: expirationBlock,
opType: _opType
});
emit NewPriorityRequest(msg.sender, nextPriorityRequestId, _opType, _pubData, uint256(expirationBlock));
totalOpenPriorityRequests++;
}
/// @notice Deletes processed priority requests
/// @param _number The number of requests
function deleteRequests(uint64 _number) internal {
require(_number <= totalOpenPriorityRequests, "M"); // number is higher than total priority requests number
uint64 numberOfRequestsToClear = Utils.minU64(_number, MAX_PRIORITY_REQUESTS_TO_DELETE_IN_VERIFY);
uint64 startIndex = firstPriorityRequestId;
for (uint64 i = startIndex; i < startIndex + numberOfRequestsToClear; i++) {
delete priorityRequests[i];
}
totalOpenPriorityRequests -= _number;
firstPriorityRequestId += _number;
totalCommittedPriorityRequests -= _number;
}
function increaseBalanceToWithdraw(bytes22 _packedBalanceKey, uint128 _amount) internal {
uint128 balance = pendingBalances[_packedBalanceKey].balanceToWithdraw;
pendingBalances[_packedBalanceKey] = PendingBalance(balance.add(_amount), FILLED_GAS_RESERVE_VALUE);
}
/// @notice Sends ETH
/// @param _to Address of recipient
/// @param _amount Amount of tokens to transfer
/// @return bool flag indicating that transfer is successful
function sendETHNoRevert(address payable _to, uint256 _amount) internal returns (bool) {
(bool callSuccess, ) = _to.call{gas: WITHDRAWAL_GAS_LIMIT, value: _amount}("");
return callSuccess;
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*
* _Since v2.5.0:_ this module is now much more gas efficient, given net gas
* metering changes introduced in the Istanbul hardfork.
*/
contract ReentrancyGuard {
/// @dev Address of lock flag variable.
/// @dev Flag is placed at random memory location to not interfere with Storage contract.
uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1;
function initializeReentrancyGuard() internal {
// Storing an initial non-zero value makes deployment a bit more
// expensive, but in exchange the refund on every call to nonReentrant
// will be lower in amount. Since refunds are capped to a percetange of
// the total transaction's gas, it is best to keep them low in cases
// like this one, to increase the likelihood of the full refund coming
// into effect.
assembly {
sstore(LOCK_FLAG_ADDRESS, 1)
}
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
bool notEntered;
assembly {
notEntered := sload(LOCK_FLAG_ADDRESS)
}
// On the first call to nonReentrant, _notEntered will be true
require(notEntered, "1b");
// Any calls to nonReentrant after this point will fail
assembly {
sstore(LOCK_FLAG_ADDRESS, 0)
}
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
assembly {
sstore(LOCK_FLAG_ADDRESS, 1)
}
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "14");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "v");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "15");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "x");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "y");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMathUInt128 {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint128 a, uint128 b) internal pure returns (uint128) {
uint128 c = a + b;
require(c >= a, "12");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint128 a, uint128 b) internal pure returns (uint128) {
return sub(a, b, "aa");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(
uint128 a,
uint128 b,
string memory errorMessage
) internal pure returns (uint128) {
require(b <= a, errorMessage);
uint128 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint128 a, uint128 b) internal pure returns (uint128) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint128 c = a * b;
require(c / a == b, "13");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint128 a, uint128 b) internal pure returns (uint128) {
return div(a, b, "ac");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(
uint128 a,
uint128 b,
string memory errorMessage
) internal pure returns (uint128) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint128 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint128 a, uint128 b) internal pure returns (uint128) {
return mod(a, b, "ad");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(
uint128 a,
uint128 b,
string memory errorMessage
) internal pure returns (uint128) {
require(b != 0, errorMessage);
return a % b;
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/**
* @dev Wrappers over Solidity's uintXX casting operators with added overflow
* checks.
*
* Downcasting from uint256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} to extend it to smaller types, by performing
* all math on `uint256` and then downcasting.
*
* _Available since v2.5.0._
*/
library SafeCast {
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value < 2**128, "16");
return uint128(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value < 2**64, "17");
return uint64(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value < 2**32, "18");
return uint32(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value < 2**16, "19");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value < 2**8, "1a");
return uint8(value);
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./IERC20.sol";
import "./Bytes.sol";
library Utils {
/// @notice Returns lesser of two values
function minU32(uint32 a, uint32 b) internal pure returns (uint32) {
return a < b ? a : b;
}
/// @notice Returns lesser of two values
function minU64(uint64 a, uint64 b) internal pure returns (uint64) {
return a < b ? a : b;
}
/// @notice Sends tokens
/// @dev NOTE: this function handles tokens that have transfer function not strictly compatible with ERC20 standard
/// @dev NOTE: call `transfer` to this token may return (bool) or nothing
/// @param _token Token address
/// @param _to Address of recipient
/// @param _amount Amount of tokens to transfer
/// @return bool flag indicating that transfer is successful
function sendERC20(
IERC20 _token,
address _to,
uint256 _amount
) internal returns (bool) {
(bool callSuccess, bytes memory callReturnValueEncoded) =
address(_token).call(abi.encodeWithSignature("transfer(address,uint256)", _to, _amount));
// `transfer` method may return (bool) or nothing.
bool returnedSuccess = callReturnValueEncoded.length == 0 || abi.decode(callReturnValueEncoded, (bool));
return callSuccess && returnedSuccess;
}
/// @notice Transfers token from one address to another
/// @dev NOTE: this function handles tokens that have transfer function not strictly compatible with ERC20 standard
/// @dev NOTE: call `transferFrom` to this token may return (bool) or nothing
/// @param _token Token address
/// @param _from Address of sender
/// @param _to Address of recipient
/// @param _amount Amount of tokens to transfer
/// @return bool flag indicating that transfer is successful
function transferFromERC20(
IERC20 _token,
address _from,
address _to,
uint256 _amount
) internal returns (bool) {
(bool callSuccess, bytes memory callReturnValueEncoded) =
address(_token).call(abi.encodeWithSignature("transferFrom(address,address,uint256)", _from, _to, _amount));
// `transferFrom` method may return (bool) or nothing.
bool returnedSuccess = callReturnValueEncoded.length == 0 || abi.decode(callReturnValueEncoded, (bool));
return callSuccess && returnedSuccess;
}
/// @notice Recovers signer's address from ethereum signature for given message
/// @param _signature 65 bytes concatenated. R (32) + S (32) + V (1)
/// @param _messageHash signed message hash.
/// @return address of the signer
function recoverAddressFromEthSignature(bytes memory _signature, bytes32 _messageHash)
internal
pure
returns (address)
{
require(_signature.length == 65, "P"); // incorrect signature length
bytes32 signR;
bytes32 signS;
uint8 signV;
assembly {
signR := mload(add(_signature, 32))
signS := mload(add(_signature, 64))
signV := byte(0, mload(add(_signature, 96)))
}
return ecrecover(_messageHash, signV, signR, signS);
}
/// @notice Returns new_hash = hash(old_hash + bytes)
function concatHash(bytes32 _hash, bytes memory _bytes) internal pure returns (bytes32) {
bytes32 result;
assembly {
let bytesLen := add(mload(_bytes), 32)
mstore(_bytes, _hash)
result := keccak256(_bytes, bytesLen)
}
return result;
}
function hashBytesToBytes20(bytes memory _bytes) internal pure returns (bytes20) {
return bytes20(uint160(uint256(keccak256(_bytes))));
}
}
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./IERC20.sol";
import "./Governance.sol";
import "./Verifier.sol";
import "./Operations.sol";
/// @title zkSync storage contract
/// @author Matter Labs
contract Storage {
/// @dev Flag indicates that upgrade preparation status is active
/// @dev Will store false in case of not active upgrade mode
bool internal upgradePreparationActive;
/// @dev Upgrade preparation activation timestamp (as seconds since unix epoch)
/// @dev Will be equal to zero in case of not active upgrade mode
uint256 internal upgradePreparationActivationTime;
/// @dev Verifier contract. Used to verify block proof and exit proof
Verifier public verifier;
/// @dev Governance contract. Contains the governor (the owner) of whole system, validators list, possible tokens list
Governance public governance;
uint8 internal constant FILLED_GAS_RESERVE_VALUE = 0xff; // we use it to set gas revert value so slot will not be emptied with 0 balance
struct PendingBalance {
uint128 balanceToWithdraw;
uint8 gasReserveValue; // gives user opportunity to fill storage slot with nonzero value
}
/// @dev Root-chain balances (per owner and token id, see packAddressAndTokenId) to withdraw
mapping(bytes22 => PendingBalance) internal pendingBalances;
// @dev Pending withdrawals are not used in this version
struct PendingWithdrawal_DEPRECATED {
address to;
uint16 tokenId;
}
mapping(uint32 => PendingWithdrawal_DEPRECATED) internal pendingWithdrawals_DEPRECATED;
uint32 internal firstPendingWithdrawalIndex_DEPRECATED;
uint32 internal numberOfPendingWithdrawals_DEPRECATED;
/// @notice Total number of executed blocks i.e. blocks[totalBlocksExecuted] points at the latest executed block (block 0 is genesis)
uint32 public totalBlocksExecuted;
/// @notice Total number of committed blocks i.e. blocks[totalBlocksCommitted] points at the latest committed block
uint32 public totalBlocksCommitted;
/// @Old rollup block stored data - not used in current version
/// @member validator Block producer
/// @member committedAtBlock ETH block number at which this block was committed
/// @member cumulativeOnchainOperations Total number of operations in this and all previous blocks
/// @member priorityOperations Total number of priority operations for this block
/// @member commitment Hash of the block circuit commitment
/// @member stateRoot New tree root hash
///
/// Consider memory alignment when changing field order: https://solidity.readthedocs.io/en/v0.4.21/miscellaneous.html
struct Block_DEPRECATED {
uint32 committedAtBlock;
uint64 priorityOperations;
uint32 chunks;
bytes32 withdrawalsDataHash; // can be restricted to 16 bytes to reduce number of required storage slots
bytes32 commitment;
bytes32 stateRoot;
}
mapping(uint32 => Block_DEPRECATED) internal blocks_DEPRECATED;
/// @notice Flag indicates that a user has exited in the exodus mode certain token balance (per account id and tokenId)
mapping(uint32 => mapping(uint16 => bool)) public performedExodus;
/// @notice Flag indicates that exodus (mass exit) mode is triggered
/// @notice Once it was raised, it can not be cleared again, and all users must exit
bool public exodusMode;
/// @notice User authenticated fact hashes for some nonce.
mapping(address => mapping(uint32 => bytes32)) public authFacts;
/// @notice Old Priority Operation container
/// @member opType Priority operation type
/// @member pubData Priority operation public data
/// @member expirationBlock Expiration block number (ETH block) for this request (must be satisfied before)
struct PriorityOperation_DEPRECATED {
Operations.OpType opType;
bytes pubData;
uint256 expirationBlock;
}
/// @dev Priority Requests mapping (request id - operation)
/// @dev Contains op type, pubdata and expiration block of unsatisfied requests.
/// @dev Numbers are in order of requests receiving
mapping(uint64 => PriorityOperation_DEPRECATED) internal priorityRequests_DEPRECATED;
/// @notice First open priority request id
uint64 public firstPriorityRequestId;
/// @notice Total number of requests
uint64 public totalOpenPriorityRequests;
/// @notice Total number of committed requests.
/// @dev Used in checks: if the request matches the operation on Rollup contract and if provided number of requests is not too big
uint64 public totalCommittedPriorityRequests;
/// @notice Packs address and token id into single word to use as a key in balances mapping
function packAddressAndTokenId(address _address, uint16 _tokenId) internal pure returns (bytes22) {
return bytes22((uint176(_address) | (uint176(_tokenId) << 160)));
}
/// @Rollup block stored data
/// @member blockNumber Rollup block number
/// @member priorityOperations Number of priority operations processed
/// @member pendingOnchainOperationsHash Hash of all operations that must be processed after verify
/// @member timestamp Rollup block timestamp, have the same format as Ethereum block constant
/// @member stateHash Root hash of the rollup state
/// @member commitment Verified input for the zkSync circuit
struct StoredBlockInfo {
uint32 blockNumber;
uint64 priorityOperations;
bytes32 pendingOnchainOperationsHash;
uint256 timestamp;
bytes32 stateHash;
bytes32 commitment;
}
/// @notice Returns the keccak hash of the ABI-encoded StoredBlockInfo
function hashStoredBlockInfo(StoredBlockInfo memory _storedBlockInfo) internal pure returns (bytes32) {
return keccak256(abi.encode(_storedBlockInfo));
}
/// @dev Stored hashed StoredBlockInfo for some block number
mapping(uint32 => bytes32) internal storedBlockHashes;
/// @notice Total blocks proven.
uint32 public totalBlocksProven;
/// @notice Priority Operation container
/// @member hashedPubData Hashed priority operation public data
/// @member expirationBlock Expiration block number (ETH block) for this request (must be satisfied before)
/// @member opType Priority operation type
struct PriorityOperation {
bytes20 hashedPubData;
uint64 expirationBlock;
Operations.OpType opType;
}
/// @dev Priority Requests mapping (request id - operation)
/// @dev Contains op type, pubdata and expiration block of unsatisfied requests.
/// @dev Numbers are in order of requests receiving
mapping(uint64 => PriorityOperation) internal priorityRequests;
/// @dev Timer for authFacts entry reset (address, nonce -> timer).
/// @dev Used when user wants to reset `authFacts` for some nonce.
mapping(address => mapping(uint32 => uint256)) internal authFactsResetTimer;
}
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 constant WITHDRAWAL_GAS_LIMIT = 100000;
/// @dev Bytes in one chunk
uint8 constant CHUNK_BYTES = 9;
/// @dev zkSync address length
uint8 constant ADDRESS_BYTES = 20;
uint8 constant PUBKEY_HASH_BYTES = 20;
/// @dev Public key bytes length
uint8 constant PUBKEY_BYTES = 32;
/// @dev Ethereum signature r/s bytes length
uint8 constant ETH_SIGN_RS_BYTES = 32;
/// @dev Success flag bytes length
uint8 constant SUCCESS_FLAG_BYTES = 1;
/// @dev Max amount of tokens registered in the network (excluding ETH, which is hardcoded as tokenId = 0)
uint16 constant MAX_AMOUNT_OF_REGISTERED_TOKENS = 127;
/// @dev Max account id that could be registered in the network
uint32 constant MAX_ACCOUNT_ID = (2**24) - 1;
/// @dev Expected average period of block creation
uint256 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 constant EXPECT_VERIFICATION_IN = 0 hours / BLOCK_PERIOD;
uint256 constant NOOP_BYTES = 1 * CHUNK_BYTES;
uint256 constant DEPOSIT_BYTES = 6 * CHUNK_BYTES;
uint256 constant TRANSFER_TO_NEW_BYTES = 6 * CHUNK_BYTES;
uint256 constant PARTIAL_EXIT_BYTES = 6 * CHUNK_BYTES;
uint256 constant TRANSFER_BYTES = 2 * CHUNK_BYTES;
uint256 constant FORCED_EXIT_BYTES = 6 * CHUNK_BYTES;
/// @dev Full exit operation length
uint256 constant FULL_EXIT_BYTES = 6 * CHUNK_BYTES;
/// @dev ChangePubKey operation length
uint256 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 constant PRIORITY_EXPIRATION_PERIOD = 3 days;
/// @dev Expiration delta for priority request to be satisfied (in ETH blocks)
uint256 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 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 constant MASS_FULL_EXIT_PERIOD = 9 days;
/// @dev Reserved time for users to withdraw funds from full exit priority operation in case of an upgrade (in seconds)
uint256 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 constant UPGRADE_NOTICE_PERIOD =
MASS_FULL_EXIT_PERIOD+PRIORITY_EXPIRATION_PERIOD+TIME_TO_WITHDRAW_FUNDS_FROM_FULL_EXIT;
/// @dev Timestamp - seconds since unix epoch
uint256 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 constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 15 minutes;
/// @dev Bit mask to apply for verifier public input before verifying.
uint256 constant INPUT_MASK = 14474011154664524427946373126085988481658748083205070504932198000989141204991;
/// @dev Auth fact reset timelock
uint256 constant AUTH_FACT_RESET_TIMELOCK = 1 days;
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Upgradeable.sol";
import "./Operations.sol";
/// @title zkSync events
/// @author Matter Labs
interface Events {
/// @notice Event emitted when a block is committed
event BlockCommit(uint32 indexed blockNumber);
/// @notice Event emitted when a block is verified
event BlockVerification(uint32 indexed blockNumber);
/// @notice Event emitted when user funds are withdrawn from the zkSync contract
event Withdrawal(uint16 indexed tokenId, uint128 amount);
/// @notice Event emitted when user funds are deposited to the zkSync contract
event Deposit(uint16 indexed tokenId, uint128 amount);
/// @notice Event emitted when user sends a authentication fact (e.g. pub-key hash)
event FactAuth(address indexed sender, uint32 nonce, bytes fact);
/// @notice Event emitted when blocks are reverted
event BlocksRevert(uint32 totalBlocksVerified, uint32 totalBlocksCommitted);
/// @notice Exodus mode entered event
event ExodusMode();
/// @notice New priority request event. Emitted when a request is placed into mapping
event NewPriorityRequest(
address sender,
uint64 serialId,
Operations.OpType opType,
bytes pubData,
uint256 expirationBlock
);
/// @notice Deposit committed event.
event DepositCommit(
uint32 indexed zkSyncBlockId,
uint32 indexed accountId,
address owner,
uint16 indexed tokenId,
uint128 amount
);
/// @notice Full exit committed event.
event FullExitCommit(
uint32 indexed zkSyncBlockId,
uint32 indexed accountId,
address owner,
uint16 indexed tokenId,
uint128 amount
);
}
/// @title Upgrade events
/// @author Matter Labs
interface UpgradeEvents {
/// @notice Event emitted when new upgradeable contract is added to upgrade gatekeeper's list of managed contracts
event NewUpgradable(uint256 indexed versionId, address indexed upgradeable);
/// @notice Upgrade mode enter event
event NoticePeriodStart(
uint256 indexed versionId,
address[] newTargets,
uint256 noticePeriod // notice period (in seconds)
);
/// @notice Upgrade mode cancel event
event UpgradeCancel(uint256 indexed versionId);
/// @notice Upgrade mode preparation status event
event PreparationStart(uint256 indexed versionId);
/// @notice Upgrade mode complete event
event UpgradeComplete(uint256 indexed versionId, address[] newTargets);
}
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Bytes.sol";
import "./Utils.sol";
/// @title zkSync operations tools
library Operations {
// Circuit ops and their pubdata (chunks * bytes)
/// @notice zkSync circuit operation type
enum OpType {
Noop,
Deposit,
TransferToNew,
PartialExit,
_CloseAccount, // used for correct op id offset
Transfer,
FullExit,
ChangePubKey,
ForcedExit,
TransferFrom
}
// Byte lengths
uint8 constant OP_TYPE_BYTES = 1;
uint8 constant TOKEN_BYTES = 2;
uint8 constant PUBKEY_BYTES = 32;
uint8 constant NONCE_BYTES = 4;
uint8 constant PUBKEY_HASH_BYTES = 20;
uint8 constant ADDRESS_BYTES = 20;
/// @dev Packed fee bytes lengths
uint8 constant FEE_BYTES = 2;
/// @dev zkSync account id bytes lengths
uint8 constant ACCOUNT_ID_BYTES = 4;
uint8 constant AMOUNT_BYTES = 16;
/// @dev Signature (for example full exit signature) bytes length
uint8 constant SIGNATURE_BYTES = 64;
// Deposit pubdata
struct Deposit {
// uint8 opType
uint32 accountId;
uint16 tokenId;
uint128 amount;
address owner;
}
uint256 public constant PACKED_DEPOSIT_PUBDATA_BYTES =
OP_TYPE_BYTES + ACCOUNT_ID_BYTES + TOKEN_BYTES + AMOUNT_BYTES + ADDRESS_BYTES;
/// Deserialize deposit pubdata
function readDepositPubdata(bytes memory _data) internal pure returns (Deposit memory parsed) {
// NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
uint256 offset = OP_TYPE_BYTES;
(offset, parsed.accountId) = Bytes.readUInt32(_data, offset); // accountId
(offset, parsed.tokenId) = Bytes.readUInt16(_data, offset); // tokenId
(offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
(offset, parsed.owner) = Bytes.readAddress(_data, offset); // owner
require(offset == PACKED_DEPOSIT_PUBDATA_BYTES, "N"); // reading invalid deposit pubdata size
}
/// Serialize deposit pubdata
function writeDepositPubdataForPriorityQueue(Deposit memory op) internal pure returns (bytes memory buf) {
buf = abi.encodePacked(
uint8(OpType.Deposit),
bytes4(0), // accountId (ignored) (update when ACCOUNT_ID_BYTES is changed)
op.tokenId, // tokenId
op.amount, // amount
op.owner // owner
);
}
/// @notice Write deposit pubdata for priority queue check.
function checkDepositInPriorityQueue(Deposit memory op, bytes20 hashedPubdata) internal pure returns (bool) {
return Utils.hashBytesToBytes20(writeDepositPubdataForPriorityQueue(op)) == hashedPubdata;
}
// FullExit pubdata
struct FullExit {
// uint8 opType
uint32 accountId;
address owner;
uint16 tokenId;
uint128 amount;
}
uint256 public constant PACKED_FULL_EXIT_PUBDATA_BYTES =
OP_TYPE_BYTES + ACCOUNT_ID_BYTES + ADDRESS_BYTES + TOKEN_BYTES + AMOUNT_BYTES;
function readFullExitPubdata(bytes memory _data) internal pure returns (FullExit memory parsed) {
// NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
uint256 offset = OP_TYPE_BYTES;
(offset, parsed.accountId) = Bytes.readUInt32(_data, offset); // accountId
(offset, parsed.owner) = Bytes.readAddress(_data, offset); // owner
(offset, parsed.tokenId) = Bytes.readUInt16(_data, offset); // tokenId
(offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
require(offset == PACKED_FULL_EXIT_PUBDATA_BYTES, "O"); // reading invalid full exit pubdata size
}
function writeFullExitPubdataForPriorityQueue(FullExit memory op) internal pure returns (bytes memory buf) {
buf = abi.encodePacked(
uint8(OpType.FullExit),
op.accountId, // accountId
op.owner, // owner
op.tokenId, // tokenId
uint128(0) // amount -- ignored
);
}
function checkFullExitInPriorityQueue(FullExit memory op, bytes20 hashedPubdata) internal pure returns (bool) {
return Utils.hashBytesToBytes20(writeFullExitPubdataForPriorityQueue(op)) == hashedPubdata;
}
// PartialExit pubdata
struct PartialExit {
//uint8 opType; -- present in pubdata, ignored at serialization
//uint32 accountId; -- present in pubdata, ignored at serialization
uint16 tokenId;
uint128 amount;
//uint16 fee; -- present in pubdata, ignored at serialization
address owner;
}
function readPartialExitPubdata(bytes memory _data) internal pure returns (PartialExit memory parsed) {
// NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
uint256 offset = OP_TYPE_BYTES + ACCOUNT_ID_BYTES; // opType + accountId (ignored)
(offset, parsed.tokenId) = Bytes.readUInt16(_data, offset); // tokenId
(offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
offset += FEE_BYTES; // fee (ignored)
(offset, parsed.owner) = Bytes.readAddress(_data, offset); // owner
}
// ForcedExit pubdata
struct ForcedExit {
//uint8 opType; -- present in pubdata, ignored at serialization
//uint32 initiatorAccountId; -- present in pubdata, ignored at serialization
//uint32 targetAccountId; -- present in pubdata, ignored at serialization
uint16 tokenId;
uint128 amount;
//uint16 fee; -- present in pubdata, ignored at serialization
address target;
}
function readForcedExitPubdata(bytes memory _data) internal pure returns (ForcedExit memory parsed) {
// NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
uint256 offset = OP_TYPE_BYTES + ACCOUNT_ID_BYTES * 2; // opType + initiatorAccountId + targetAccountId (ignored)
(offset, parsed.tokenId) = Bytes.readUInt16(_data, offset); // tokenId
(offset, parsed.amount) = Bytes.readUInt128(_data, offset); // amount
offset += FEE_BYTES; // fee (ignored)
(offset, parsed.target) = Bytes.readAddress(_data, offset); // target
}
// ChangePubKey
enum ChangePubkeyType {ECRECOVER, CREATE2}
struct ChangePubKey {
// uint8 opType; -- present in pubdata, ignored at serialization
uint32 accountId;
bytes20 pubKeyHash;
address owner;
uint32 nonce;
//uint16 tokenId; -- present in pubdata, ignored at serialization
//uint16 fee; -- present in pubdata, ignored at serialization
}
function readChangePubKeyPubdata(bytes memory _data) internal pure returns (ChangePubKey memory parsed) {
uint256 offset = OP_TYPE_BYTES;
(offset, parsed.accountId) = Bytes.readUInt32(_data, offset); // accountId
(offset, parsed.pubKeyHash) = Bytes.readBytes20(_data, offset); // pubKeyHash
(offset, parsed.owner) = Bytes.readAddress(_data, offset); // owner
(offset, parsed.nonce) = Bytes.readUInt32(_data, offset); // nonce
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/// @title Interface of the upgradeable master contract (defines notice period duration and allows finish upgrade during preparation of it)
/// @author Matter Labs
interface UpgradeableMaster {
/// @notice Notice period before activation preparation status of upgrade mode
function getNoticePeriod() external returns (uint256);
/// @notice Notifies contract that notice period started
function upgradeNoticePeriodStarted() external;
/// @notice Notifies contract that upgrade preparation status is activated
function upgradePreparationStarted() external;
/// @notice Notifies contract that upgrade canceled
function upgradeCanceled() external;
/// @notice Notifies contract that upgrade finishes
function upgradeFinishes() external;
/// @notice Checks that contract is ready for upgrade
/// @return bool flag indicating that contract is ready for upgrade
function isReadyForUpgrade() external returns (bool);
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Config.sol";
/// @title Governance Contract
/// @author Matter Labs
contract Governance is Config {
/// @notice Token added to Franklin net
event NewToken(address indexed token, uint16 indexed tokenId);
/// @notice Governor changed
event NewGovernor(address newGovernor);
/// @notice Validator's status changed
event ValidatorStatusUpdate(address indexed validatorAddress, bool isActive);
event TokenPausedUpdate(address indexed token, bool paused);
/// @notice Address which will exercise governance over the network i.e. add tokens, change validator set, conduct upgrades
address public networkGovernor;
/// @notice Total number of ERC20 tokens registered in the network (excluding ETH, which is hardcoded as tokenId = 0)
uint16 public totalTokens;
/// @notice List of registered tokens by tokenId
mapping(uint16 => address) public tokenAddresses;
/// @notice List of registered tokens by address
mapping(address => uint16) public tokenIds;
/// @notice List of permitted validators
mapping(address => bool) public validators;
/// @notice Paused tokens list, deposits are impossible to create for paused tokens
mapping(uint16 => bool) public pausedTokens;
/// @notice Governance contract initialization. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param initializationParameters Encoded representation of initialization parameters:
/// _networkGovernor The address of network governor
function initialize(bytes calldata initializationParameters) external {
address _networkGovernor = abi.decode(initializationParameters, (address));
networkGovernor = _networkGovernor;
}
/// @notice Governance contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param upgradeParameters Encoded representation of upgrade parameters
function upgrade(bytes calldata upgradeParameters) external {}
/// @notice Change current governor
/// @param _newGovernor Address of the new governor
function changeGovernor(address _newGovernor) external {
requireGovernor(msg.sender);
if (networkGovernor != _newGovernor) {
networkGovernor = _newGovernor;
emit NewGovernor(_newGovernor);
}
}
/// @notice Add token to the list of networks tokens
/// @param _token Token address
function addToken(address _token) external {
requireGovernor(msg.sender);
require(tokenIds[_token] == 0, "1e"); // token exists
require(totalTokens < MAX_AMOUNT_OF_REGISTERED_TOKENS, "1f"); // no free identifiers for tokens
totalTokens++;
uint16 newTokenId = totalTokens; // it is not `totalTokens - 1` because tokenId = 0 is reserved for eth
tokenAddresses[newTokenId] = _token;
tokenIds[_token] = newTokenId;
emit NewToken(_token, newTokenId);
}
/// @notice Pause token deposits for the given token
/// @param _tokenAddr Token address
/// @param _tokenPaused Token paused status
function setTokenPaused(address _tokenAddr, bool _tokenPaused) external {
requireGovernor(msg.sender);
uint16 tokenId = this.validateTokenAddress(_tokenAddr);
if (pausedTokens[tokenId] != _tokenPaused) {
pausedTokens[tokenId] = _tokenPaused;
emit TokenPausedUpdate(_tokenAddr, _tokenPaused);
}
}
/// @notice Change validator status (active or not active)
/// @param _validator Validator address
/// @param _active Active flag
function setValidator(address _validator, bool _active) external {
requireGovernor(msg.sender);
if (validators[_validator] != _active) {
validators[_validator] = _active;
emit ValidatorStatusUpdate(_validator, _active);
}
}
/// @notice Check if specified address is is governor
/// @param _address Address to check
function requireGovernor(address _address) public view {
require(_address == networkGovernor, "1g"); // only by governor
}
/// @notice Checks if validator is active
/// @param _address Validator address
function requireActiveValidator(address _address) external view {
require(validators[_address], "1h"); // validator is not active
}
/// @notice Validate token id (must be less than or equal to total tokens amount)
/// @param _tokenId Token id
/// @return bool flag that indicates if token id is less than or equal to total tokens amount
function isValidTokenId(uint16 _tokenId) external view returns (bool) {
return _tokenId <= totalTokens;
}
/// @notice Validate token address
/// @param _tokenAddr Token address
/// @return tokens id
function validateTokenAddress(address _tokenAddr) external view returns (uint16) {
uint16 tokenId = tokenIds[_tokenAddr];
require(tokenId != 0, "1i"); // 0 is not a valid token
return tokenId;
}
}
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 {
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
function upgrade(bytes calldata upgradeParameters) external {}
function verifyAggregatedBlockProof(
uint256[] memory _recursiveInput,
uint256[] memory _proof,
uint8[] memory _vkIndexes,
uint256[] memory _individual_vks_inputs,
uint256[16] memory _subproofs_limbs
) external view returns (bool) {
for (uint256 i = 0; i < _individual_vks_inputs.length; ++i) {
uint256 commitment = _individual_vks_inputs[i];
_individual_vks_inputs[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,
_individual_vks_inputs,
_subproofs_limbs,
vk
);
}
function verifyExitProof(
bytes32 _rootHash,
uint32 _accountId,
address _owner,
uint16 _tokenId,
uint128 _amount,
uint256[] calldata _proof
) external view returns (bool) {
bytes32 commitment =
sha256(abi.encodePacked(uint256(_rootHash) & INPUT_MASK, _accountId, _owner, _tokenId, _amount));
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);
return verify_old(proof, vk);
}
}
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 = 0x1a31a259c1161ef96ebd7c9bb4c1e4201227ffe39f22d5dd2ca1dbca1d9087f0;
uint8 constant VK_MAX_INDEX = 5;
function getVkAggregated(uint32 _proofs) internal pure returns (VerificationKey memory vk) {
if (_proofs == uint32(1)) { return getVkAggregated1(); }
else if (_proofs == uint32(5)) { return getVkAggregated5(); }
else if (_proofs == uint32(10)) { return getVkAggregated10(); }
else if (_proofs == uint32(20)) { return getVkAggregated20(); }
}
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(
0x1b2d28f346ba6302090869b58c0ccf45994c8aaee54101d489e4605b9b9d69a5,
0x05b254b5537aede870276a46ae3046ae4cb36a5e41b1a1208355a4b2de0fc3c4
);
vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
0x0e111faf12e663d8e6aa9b7c434376e13fb4ae52bb597bcc23f2044710daa60a,
0x16505d91104cdf110698ebe99f0abd162630e4b108356640d1abd8596c4680d2
);
vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
0x0e6aaf4f2ceb4d0b781ccbcb8c6b235d6c74df0079e8db8eefc9539b6ca2d920,
0x0779a9706bd1a8315662914928188f51a2081d1bbeb863a1f6945ab6e1752513
);
vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
0x12f8cc0d6eaa884fa1fa6ec2c23cd21892dff4298c67451f6c234293a85d977b,
0x165d8106e03536fcf8c66391ee31e97b00664932d63d61a008108d68f8da2dcd
);
vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
0x282ab78735c94c7d4fe2b134e7cee6bf967921c744b2df5b1ac7980ca39a6ef4,
0x0f627a1b42661cca9fa1e2de44d78413a1817b0ea44506de524f3aeb43b00c69
);
vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
0x0f1abdaaea6fc0c841cbdbb84315392c7de7270704d2bd990f3205f06f3c2e72,
0x18e32227065587b5814b4d1f8d7f78689af94f711d0521575c2ad723706403ac
);
vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
0x2e43a380b145f473c7b76c29110fa2a54d29e39e4c3e7a0667656f5d7c6fa783,
0x0c56e0e6679b4b71113d073ad16a405c62f1154a37202dcefce83ab2aa2bfd99
);
vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
0x287f80f33b27cac8c1d7ea38e3f38b9547fc64241f369332ced9f13255f02a11,
0x0019b4dfa8d1fa5172b3609a3ee75532a8fcdd946df313edb466502baec90916
);
vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
0x262c679d64425eba4718852094935ed36c916c8e58970723ab56a6edfec8ee53,
0x11512b535dcd41a87ff8fe16b944b0fc33a13b6ab82bed1e1fef9f887fb8bd17
);
vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
0x06e470b8f5828b55b7c2a1c25879f07c2e60ff3936de7c7a9a1d0cf11c7154cb,
0x0183d6431267f015d722e1e47fae0d8f6a66b1b75c271f6f2f7a19fd9bde0deb
);
vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
0x2c42b01e3e994120ebbc941def201a6242ceca9d24a5b0c21c1e00267126eb03,
0x2b3ee88ed3e1550605d061cb8db20ff97560e735f23e3234b32b875b2b0af854
);
vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
0x20f62698b7f1defcc8da79330979c7d176d2c9b72d031dac96e1db91c7596f22,
0x0ff81068a3a7706205893199514f4bbf06aa644ba08591b2b5cf315136fbbe89
);
vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
0x1645e6c282336dfd4ec70d4ebb71050390f70927a887dcfd6527070659f3a7e7,
0x1c93ca29a27a931a34482db88bed589951aa7d406b5583da235bf618fb4d048e
);
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 getVkAggregated5() 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(
0x20798e765493cd6c469f4ce0fb7b28da9a5f7953c8cec9a5735f06f389cafde5,
0x17251e248d9e9bcfdd48c67b819e975ad3dafb103f4970fe2c5c7c09c7a5c01d
);
vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
0x2fc13898392af94dde4dc8522b3d40059ed91645d3d67dc828310e742bb367ff,
0x1901ae3711afcac1852051b9a0b2b849fe421d823f1abcb21dc951c88773d5ec
);
vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
0x044f772c23604be394e1552d017e90e8ce76107b47436df67e6cf8af217df127,
0x0b2c6cfb5740376c4ad4bdf448e23ca636e3d63fd0e21509e19bfd2f17e4f9db
);
vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
0x2d6e15e221beaa35378a2dc4eec988cad1a9bdaabb7a94939747506a7c11fb07,
0x07f0555dd28e1849d2dfb67d09b6cd0a044d8e1598b7f2dabac47d02afbde104
);
vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
0x0f75e4f497f3ab4badccb48d638b32a1c72d725683cb579587c56345c7b5c5ca,
0x0cfcffa462dd360860a637ec6c85aca924738472a1890904ffb3b12035cd2a95
);
vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
0x1b452c4ed0d39f1a0c3509e6c0e0716206daf34ac56d7b392381b77b831d04b8,
0x2ec5fc94a3d9fa7f7e8d8f4c976634a4c581469202cdf0962c6bbc1cfe1e2854
);
vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
0x1d76ec2121fd568d20f6a3225bd9f605502941e2fa885972084509bd7b792f83,
0x10e264487cf80e39e94079e84f9a7805a9c5f50a9f8f9c18490927dec45c015a
);
vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
0x1b4c31be5bb7126ffee8a38dbc07b0b1f134144d6ae9a7cc4012408caeefd287,
0x2bcc49e52073baa3db469867cc6e69ba5c058a70e53c37d7cfc1a4930265f506
);
vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
0x1284551f36faeb407cc7032eaea6f3c1ce0709a7a5487d5b6cad65e93cc9f5ad,
0x2ab751e5e2e598d6bff15eff38df0042bd609b0c9cae2a276153883cdf0db65f
);
vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
0x2461419a8b3fa4852edd1962d47f839a7d6d0aa03d7854c5e5fa9f616082720d,
0x2a33adabc8e1348789e0f6fe74d3a1627d2971067a6f564fe836add79cd4fb8e
);
vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
0x26c5de18a55d3e05389089e4f87c22e4a65fdfb3c8ceebe94fad480762005d8b,
0x12cae9a5899d7582e5f95fd9fab2b9907b9f72728630e816de77154b759f757a
);
vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
0x2ac7330d3ca44d25eac57b4882bddf9ac755354701b0ad3f59c79b5af701bc50,
0x1b295eee1b5472020b65ae792c4b1857457a32f56b93b0258f1ad6a6cbd9121e
);
vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
0x0587644c2baa2878f893a96eccaa3393e213496aefbcbba257faf04aaa22bff5,
0x00e27f098241d232356ce65b1b2eabc53e6920f407667d9834450ad6cba32cee
);
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 getVkAggregated10() internal pure returns(VerificationKey memory vk) {
vk.domain_size = 33554432;
vk.num_inputs = 1;
vk.omega = PairingsBn254.new_fr(0x0d94d63997367c97a8ed16c17adaae39262b9af83acb9e003f94c217303dd160);
vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
0x195ddaa40f4b6e9b30d12fa31b569a464bc6bfed3ee6ff93d9e682f2feead9be,
0x2ff44a308801b2e8c089531d6aef1c36934f397d61bcd4b0d40a374bf9df9436
);
vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
0x0d3fa3f06569639bf5bbaf07f4938f56590e0ef1fc40bf3b1a38c833c520e06d,
0x03f5fac1bb71bb9f9995989addf5dc96077149859b9e04f0a1c3155daf209dda
);
vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
0x2fd417c4292d8d98209fbf5e4f2d9db2ccac53347a5a6a47ec844afedc7bcc1c,
0x2c283b236436a7263d2e67971df7cd534a0c7afd5a6709540cb9e42b464a1306
);
vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
0x0992d873a69f092ad514b93050c8a380a29638aece0ee6119558bc441cfcb0ab,
0x2773318f90e7ac7591f681caae174e2bc81d85406a0224d0d6709311293f1ad6
);
vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
0x287a21ccbbb448f17ea093bf72e5ffa6408ab0c42b5a0a2ba6cfa54dfa81ca8e,
0x30276a7e2e21e76cb5e6d11dfd8c9fdf21ef1d39ce8b6322198ff83e0d2abf2f
);
vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
0x2928f5217dc1375eac8c7b4d695d6fafacf012f98262defe9c5b09a851921176,
0x0e0c579f9a0bfa1d6ce0e510814a16f24298db9545d1dabc2bb303e329c91716
);
vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
0x06546546c2d7526784dcfc955ae3f714514efdf93f1672c3ae89227c237552f6,
0x0bf25d7526789defc06fcd53246265f050341964806b46c615696ab4e6482abf
);
vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
0x22b4669582b192b994fd4fece3a71194e117258c25118d8bc62be88e394862ab,
0x0368275fa2196f8f73814e96cf0a650568969b6e0e65c66049519dc01250ab3b
);
vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
0x13164c9232db87769529343c77e90d4e5d178b0695d5acc44b9e3af5e138d3cd,
0x2b95e0779238d9324dda354e700d8747856b5885f42de8ac8f119e690bc6b4ce
);
vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
0x09b9984565afa4a789d6ba629d81bc7a8f191f609a415dd071d68068f1ece1a9,
0x0c7ade93f30c15025e00ec419a8234e23c75b8b41a6d262a0567e1494a63a089
);
vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
0x050ab6d4715d929e6a03d246665bdb3ae3fb330cb1624b9dd80a16915f919097,
0x05080bc8892e8cfa5173c161655d0d9604de4246ce93ee0f39aecc44643c8338
);
vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
0x144272056d3bfd3a20817bd9e83db9255bf75d1087ea026ed265f350558bdbdb,
0x1291171a46ae520cfeb48306f75bd9b6bcc682c25f5491bbae05967032226db7
);
vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
0x135e1e48ff5f743ef65611bd6c035d609898a3aa0e1e7ac73ff84aa1591a0ff0,
0x29e8199b33b3c240d61b5bdd63758876283dc51abb963c8a3ed6d7c39f9f61d4
);
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 getVkAggregated20() internal pure returns(VerificationKey memory vk) {
vk.domain_size = 67108864;
vk.num_inputs = 1;
vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97);
vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
0x27310730dd3548df041a98ca4440019aac2ff23896dc0b311131e3bd258ed585,
0x2afba6db2001ec0fb46a7aa815df9d284f93be7735f4603183df22810a8d34eb
);
vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
0x2b6e6a32d4e82fd7c982ec92a970e99ca6fece8a7426f0f03c82674e285f3abc,
0x0b1cd760a8d7c2956f620733013b687515d1a8667453eb6ebddef095d9c82c6e
);
vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
0x01382f6465a7bed9283aeb0d42bfec55233220397fd54c5c31b3b0358d97840d,
0x098ceef2375246c73b544e0e6d59942d555997f58d71fe650b3c56cb7b0c1e1f
);
vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
0x0d05c024a2e0990fb0dd6831d5768ad0e5ea2cab4e56f70c02b5d20eccd59242,
0x1caef11577704020cb0bc332a06cfcbdc91688826ecfce4d7fa95b1d356e33c6
);
vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
0x150f1c2a2da8b30067f44c774dc7c913ce5b503794489eae66117ab28d7f0d56,
0x1315bfe5e9c07a370e62ac4ee82677259abd15b698b5c679bb9cf97112406602
);
vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
0x16ee6c88117311a18268d276280d151ef746b429d4e3bebde63432220fcc43af,
0x12af9840baa1f43fe449b3aba6fbcd9a96dfd50bfb670227c5299191722b676b
);
vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
0x0a4ff4dbdd532c7848559ca80b18d0e026aa05481e41bf0c7f3c3d2033c504c1,
0x03b1b5f40650541a02900534cf4b941f3c3abf54d0dce8753fbf41905067fba2
);
vk.gate_selector_commitments[0] = PairingsBn254.new_g1(
0x1aeed9f925d1a9021bf63cace33e5e329a2fc2bc3ff2f15d44bd64b2612f776c,
0x161e4b64f846f86f1a86cb57a179d18deb4d5485e90384c343c42827062717b2
);
vk.gate_selector_commitments[1] = PairingsBn254.new_g1(
0x08bb3a04dfc8a05064f3cca8ef52d8eeb2f3a0b2d3b3c243dc2d2c37ac63b09d,
0x0f9cd3f78f222982e949d97119c5197553bce77ecc808d9360b5d98c9323dada
);
vk.copy_permutation_commitments[0] = PairingsBn254.new_g1(
0x2d6933d1ead2cc32e1743d39f9230f34b2ba7cbd6f0ce4a495e2a6c06f56527c,
0x2537cf3a2fbd9f49da269c2ac8f980347e7d6c3063bb4fbaf079976e86880849
);
vk.copy_permutation_commitments[1] = PairingsBn254.new_g1(
0x174beb42335087164d215735b4ed67200735d97b35deb24e946388b851b54ebc,
0x0ed913c69e882565aa8fcda65f9029b0c8e388790af40e802ea7c3ad6f114246
);
vk.copy_permutation_commitments[2] = PairingsBn254.new_g1(
0x01088efa71f09d3067482a7383fe52b37417cf7fe85410b67dcb2e28139efbca,
0x270b2a49ecbf9852a418726bdbe9df4d453eb88e39927089d5a0d27338798d75
);
vk.copy_permutation_commitments[3] = PairingsBn254.new_g1(
0x2fcc3bec8b60cca2c3d9b8ef77883b04eb896124157b8356cef0436177fb6ab8,
0x24eb48d7484ae4bb0163d49e165a27c6375b9969c3bad591a1e0a1355e2e128f
);
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 = 262144;
vk.num_inputs = 1;
vk.omega = PairingsBn254.new_fr(0x0f60c8fe0414cb9379b2d39267945f6bd60d06a05216231b26a9fcf88ddbfebe);
vk.selector_commitments[0] = PairingsBn254.new_g1(
0x117ebe939b7336d17b69b05d5530e30326af39da45a989b078bb3d607707bf3e,
0x18b16095a1c814fe2980170ff34490f1fd454e874caa87df2f739fb9c8d2e902
);
vk.selector_commitments[1] = PairingsBn254.new_g1(
0x05ac70a10fc569cc8358bfb708c184446966c6b6a3e0d7c25183ded97f9e7933,
0x0f6152282854e153588d45e784d216a423a624522a687741492ee0b807348e71
);
vk.selector_commitments[2] = PairingsBn254.new_g1(
0x03cfa9d8f9b40e565435bee3c5b0e855c8612c5a89623557cc30f4588617d7bd,
0x2292bb95c2cc2da55833b403a387e250a9575e32e4ce7d6caa954f12e6ce592a
);
vk.selector_commitments[3] = PairingsBn254.new_g1(
0x04d04f495c69127b6cc6ecbfd23f77f178e7f4e2d2de3eab3e583a4997744cd9,
0x09dcf5b3db29af5c5eef2759da26d3b6959cb8d80ada9f9b086f7cc39246ad2b
);
vk.selector_commitments[4] = PairingsBn254.new_g1(
0x01ebab991522d407cfd4e8a1740b64617f0dfca50479bba2707c2ec4159039fc,
0x2c8bd00a44c6120bbf8e57877013f2b5ee36b53eef4ea3b6748fd03568005946
);
vk.selector_commitments[5] = PairingsBn254.new_g1(
0x07a7124d1fece66bd5428fcce25c22a4a9d5ceaa1e632565d9a062c39f005b5e,
0x2044ae5306f0e114c48142b9b97001d94e3f2280db1b01a1e47ac1cf6bd5f99e
);
// 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(
0x1dd1549a639f052c4fbc95b7b7a40acf39928cad715580ba2b38baa116dacd9c,
0x0f8e712990da1ce5195faaf80185ef0d5e430fdec9045a20af758cc8ecdac2e5
);
vk.permutation_commitments[0] = PairingsBn254.new_g1(
0x0026b64066e39a22739be37fed73308ace0a5f38a0e2292dcc2309c818e8c89c,
0x285101acca358974c2c7c9a8a3936e08fbd86779b877b416d9480c91518cb35b
);
vk.permutation_commitments[1] = PairingsBn254.new_g1(
0x2159265ac6fcd4d0257673c3a85c17f4cf3ea13a3c9fb51e404037b13778d56f,
0x25bf73e568ba3406ace2137195bb2176d9de87a48ae42520281aaef2ac2ef937
);
vk.permutation_commitments[2] = PairingsBn254.new_g1(
0x068f29af99fc8bbf8c00659d34b6d34e4757af6edc10fc7647476cbd0ea9be63,
0x2ef759b20cabf3da83d7f578d9e11ed60f7015440e77359db94475ddb303144d
);
vk.permutation_commitments[3] = PairingsBn254.new_g1(
0x22793db6e98b9e37a1c5d78fcec67a2d8c527d34c5e9c8c1ff15007d30a4c133,
0x1b683d60fd0750b3a45cdee5cbc4057204a02bd428e8071c92fe6694a40a5c1f
);
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]
);
}
}
pragma solidity >=0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
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]);
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/// @title Interface of the upgradeable contract
/// @author Matter Labs
interface Upgradeable {
/// @notice Upgrades target of upgradeable contract
/// @param newTarget New target
/// @param newTargetInitializationParameters New target initialization parameters
function upgradeTarget(address newTarget, bytes calldata newTargetInitializationParameters) external;
}
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: UNLICENSED
import "../ZkSync.sol";
contract ZkSyncWithdrawalUnitTest is ZkSync {
function setBalanceToWithdraw(
address _owner,
uint16 _token,
uint128 _amount
) external {
pendingBalances[packAddressAndTokenId(_owner, _token)].balanceToWithdraw = _amount;
}
function receiveETH() external payable {}
function withdrawOrStoreExternal(
uint16 _tokenId,
address _recipient,
uint128 _amount
) external {
return withdrawOrStore(_tokenId, _recipient, _amount);
}
}
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: UNLICENSED
import "../ZkSync.sol";
contract ZKSyncSignatureUnitTest is ZkSync {
function changePubkeySignatureCheckECRECOVER(Operations.ChangePubKey memory _changePk, bytes memory _witness)
external
pure
returns (bool)
{
return verifyChangePubkeyECRECOVER(_witness, _changePk);
}
function changePubkeySignatureCheckCREATE2(Operations.ChangePubKey memory _changePk, bytes memory _witness)
external
pure
returns (bool)
{
return verifyChangePubkeyCREATE2(_witness, _changePk);
}
function testRecoverAddressFromEthSignature(bytes memory _signature, bytes32 _messageHash)
external
pure
returns (address)
{
return Utils.recoverAddressFromEthSignature(_signature, _messageHash);
}
}
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: UNLICENSED
import "../ZkSync.sol";
contract ZkSyncProcessOpUnitTest is ZkSync {
function collectOnchainOpsExternal(
CommitBlockInfo memory _newBlockData,
bytes32 processableOperationsHash,
uint64 priorityOperationsProcessed,
bytes memory offsetsCommitment
) external view {
(bytes32 resOpHash, uint64 resPriorOps, bytes memory resOffsetsCommitment) = collectOnchainOps(_newBlockData);
require(resOpHash == processableOperationsHash, "h");
require(resPriorOps == priorityOperationsProcessed, "p");
require(keccak256(resOffsetsCommitment) == keccak256(offsetsCommitment), "o");
}
function commitPriorityRequests() external {
totalCommittedPriorityRequests = totalOpenPriorityRequests;
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
import "../Upgradeable.sol";
import "../UpgradeableMaster.sol";
interface DummyTarget {
function get_DUMMY_INDEX() external pure returns (uint256);
function initialize(bytes calldata initializationParameters) external;
function upgrade(bytes calldata upgradeParameters) external;
function verifyPriorityOperation() external;
}
contract DummyFirst is UpgradeableMaster, DummyTarget {
uint256 constant UPGRADE_NOTICE_PERIOD = 4;
function get_UPGRADE_NOTICE_PERIOD() external pure returns (uint256) {
return UPGRADE_NOTICE_PERIOD;
}
function getNoticePeriod() external pure override returns (uint256) {
return UPGRADE_NOTICE_PERIOD;
}
function upgradeNoticePeriodStarted() external override {}
function upgradePreparationStarted() external override {}
function upgradeCanceled() external override {}
function upgradeFinishes() external override {}
function isReadyForUpgrade() external view override returns (bool) {
return totalVerifiedPriorityOperations() >= totalRegisteredPriorityOperations();
}
uint256 private constant DUMMY_INDEX = 1;
function get_DUMMY_INDEX() external pure override returns (uint256) {
return DUMMY_INDEX;
}
uint64 _verifiedPriorityOperations;
function initialize(bytes calldata initializationParameters) external override {
bytes32 byte_0 = bytes32(uint256(uint8(initializationParameters[0])));
bytes32 byte_1 = bytes32(uint256(uint8(initializationParameters[1])));
assembly {
sstore(1, byte_0)
sstore(2, byte_1)
}
}
function upgrade(bytes calldata upgradeParameters) external override {}
function totalVerifiedPriorityOperations() internal view returns (uint64) {
return _verifiedPriorityOperations;
}
function totalRegisteredPriorityOperations() internal pure returns (uint64) {
return 1;
}
function verifyPriorityOperation() external override {
_verifiedPriorityOperations++;
}
}
contract DummySecond is UpgradeableMaster, DummyTarget {
uint256 constant UPGRADE_NOTICE_PERIOD = 4;
function get_UPGRADE_NOTICE_PERIOD() external pure returns (uint256) {
return UPGRADE_NOTICE_PERIOD;
}
function getNoticePeriod() external pure override returns (uint256) {
return UPGRADE_NOTICE_PERIOD;
}
function upgradeNoticePeriodStarted() external override {}
function upgradePreparationStarted() external override {}
function upgradeCanceled() external override {}
function upgradeFinishes() external override {}
function isReadyForUpgrade() external view override returns (bool) {
return totalVerifiedPriorityOperations() >= totalRegisteredPriorityOperations();
}
uint256 private constant DUMMY_INDEX = 2;
function get_DUMMY_INDEX() external pure override returns (uint256) {
return DUMMY_INDEX;
}
uint64 _verifiedPriorityOperations;
function initialize(bytes calldata) external pure override {
revert("dsini");
}
function upgrade(bytes calldata upgradeParameters) external override {
bytes32 byte_0 = bytes32(uint256(uint8(upgradeParameters[0])));
bytes32 byte_1 = bytes32(uint256(uint8(upgradeParameters[1])));
assembly {
sstore(2, byte_0)
sstore(3, byte_1)
}
}
function totalVerifiedPriorityOperations() internal view returns (uint64) {
return _verifiedPriorityOperations;
}
function totalRegisteredPriorityOperations() internal pure returns (uint64) {
return 0;
}
function verifyPriorityOperation() external override {
_verifiedPriorityOperations++;
}
}
pragma solidity ^0.7.0;
pragma abicoder v2;
// SPDX-License-Identifier: UNLICENSED
import "../Operations.sol";
contract OperationsTest {
function testDepositPubdata(Operations.Deposit memory _example, bytes memory _pubdata) external pure {
Operations.Deposit memory parsed = Operations.readDepositPubdata(_pubdata);
require(_example.tokenId == parsed.tokenId, "tok");
require(_example.amount == parsed.amount, "amn");
require(_example.owner == parsed.owner, "own");
}
function testDepositPriorityQueue(Operations.Deposit memory _example, bytes memory _priorityQueueData)
external
pure
{
bytes memory result = Operations.writeDepositPubdataForPriorityQueue(_example);
require(keccak256(result) == keccak256(_priorityQueueData), "pqd");
}
function testFullExitPubdata(Operations.FullExit memory _example, bytes memory _pubdata) external pure {
Operations.FullExit memory parsed = Operations.readFullExitPubdata(_pubdata);
require(_example.accountId == parsed.accountId, "acc");
require(_example.owner == parsed.owner, "own");
require(_example.tokenId == parsed.tokenId, "tok");
require(_example.amount == parsed.amount, "amn");
}
function testFullExitPriorityQueue(Operations.FullExit memory _example, bytes memory _priorityQueueData)
external
pure
{
bytes memory result = Operations.writeFullExitPubdataForPriorityQueue(_example);
require(keccak256(result) == keccak256(_priorityQueueData), "pqd");
}
function testWithdrawPubdata(Operations.PartialExit memory _example, bytes memory _pubdata) external pure {
Operations.PartialExit memory parsed = Operations.readPartialExitPubdata(_pubdata);
require(_example.owner == parsed.owner, "own");
require(_example.tokenId == parsed.tokenId, "tok");
require(_example.amount == parsed.amount, "amn");
}
function testForcedExitPubdata(Operations.ForcedExit memory _example, bytes memory _pubdata) external pure {
Operations.ForcedExit memory parsed = Operations.readForcedExitPubdata(_pubdata);
require(_example.target == parsed.target, "trg");
require(_example.tokenId == parsed.tokenId, "tok");
require(_example.amount == parsed.amount, "amn");
}
function testChangePubkeyPubdata(Operations.ChangePubKey memory _example, bytes memory _pubdata) external pure {
Operations.ChangePubKey memory parsed = Operations.readChangePubKeyPubdata(_pubdata);
require(_example.accountId == parsed.accountId, "acc");
require(_example.pubKeyHash == parsed.pubKeyHash, "pkh");
require(_example.owner == parsed.owner, "own");
require(_example.nonce == parsed.nonce, "nnc");
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Governance.sol";
import "./Proxy.sol";
import "./UpgradeGatekeeper.sol";
import "./ZkSync.sol";
import "./Verifier.sol";
import "./TokenInit.sol";
contract DeployFactory is TokenDeployInit {
// Why do we deploy contracts in the constructor?
//
// If we want to deploy Proxy and UpgradeGatekeeper (using new) we have to deploy their contract code with this contract
// in total deployment of this contract would cost us around 2.5kk of gas and calling final transaction
// deployProxyContracts would cost around 3.5kk of gas(which is equivalent but slightly cheaper then doing deploy old way by sending
// transactions one by one) but doing this in one method gives us simplicity and atomicity of our deployment.
//
// If we use selfdesctruction in the constructor then it removes overhead of deploying Proxy and UpgradeGatekeeper
// with DeployFactory and in total this constructor would cost us around 3.5kk, so we got simplicity and atomicity of
// deploy without overhead.
//
// `_feeAccountAddress` argument is not used by the constructor itself, but it's important to have this
// information as a part of a transaction, since this transaction can be used for restoring the tree
// state. By including this address to the list of arguments, we're making ourselves able to restore
// genesis state, as the very first account in tree is a fee account, and we need its address before
// we're able to start recovering the data from the Ethereum blockchain.
constructor(
Governance _govTarget,
Verifier _verifierTarget,
ZkSync _zkSyncTarget,
bytes32 _genesisRoot,
address _firstValidator,
address _governor,
address _feeAccountAddress
) {
require(_firstValidator != address(0));
require(_governor != address(0));
require(_feeAccountAddress != address(0));
deployProxyContracts(_govTarget, _verifierTarget, _zkSyncTarget, _genesisRoot, _firstValidator, _governor);
selfdestruct(msg.sender);
}
event Addresses(address governance, address zksync, address verifier, address gatekeeper);
function deployProxyContracts(
Governance _governanceTarget,
Verifier _verifierTarget,
ZkSync _zksyncTarget,
bytes32 _genesisRoot,
address _validator,
address _governor
) internal {
Proxy governance = new Proxy(address(_governanceTarget), abi.encode(this));
// set this contract as governor
Proxy verifier = new Proxy(address(_verifierTarget), abi.encode());
Proxy zkSync =
new Proxy(address(_zksyncTarget), abi.encode(address(governance), address(verifier), _genesisRoot));
UpgradeGatekeeper upgradeGatekeeper = new UpgradeGatekeeper(zkSync);
governance.transferMastership(address(upgradeGatekeeper));
upgradeGatekeeper.addUpgradeable(address(governance));
verifier.transferMastership(address(upgradeGatekeeper));
upgradeGatekeeper.addUpgradeable(address(verifier));
zkSync.transferMastership(address(upgradeGatekeeper));
upgradeGatekeeper.addUpgradeable(address(zkSync));
upgradeGatekeeper.transferMastership(_governor);
emit Addresses(address(governance), address(zkSync), address(verifier), address(upgradeGatekeeper));
finalizeGovernance(Governance(address(governance)), _validator, _governor);
}
function finalizeGovernance(
Governance _governance,
address _validator,
address _finalGovernor
) internal {
address[] memory tokens = getTokens();
for (uint256 i = 0; i < tokens.length; ++i) {
_governance.addToken(tokens[i]);
}
_governance.setValidator(_validator, true);
_governance.changeGovernor(_finalGovernor);
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./Ownable.sol";
import "./Upgradeable.sol";
import "./UpgradeableMaster.sol";
/// @title Proxy Contract
/// @dev NOTICE: Proxy must implement UpgradeableMaster interface to prevent calling some function of it not by master of proxy
/// @author Matter Labs
contract Proxy is Upgradeable, UpgradeableMaster, Ownable {
/// @dev Storage position of "target" (actual implementation address: keccak256('eip1967.proxy.implementation') - 1)
bytes32 private constant targetPosition = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice Contract constructor
/// @dev Calls Ownable contract constructor and initialize target
/// @param target Initial implementation address
/// @param targetInitializationParameters Target initialization parameters
constructor(address target, bytes memory targetInitializationParameters) Ownable(msg.sender) {
setTarget(target);
(bool initializationSuccess, ) =
getTarget().delegatecall(abi.encodeWithSignature("initialize(bytes)", targetInitializationParameters));
require(initializationSuccess, "uin11"); // uin11 - target initialization failed
}
/// @notice Intercepts initialization calls
function initialize(bytes calldata) external pure {
revert("ini11"); // ini11 - interception of initialization call
}
/// @notice Intercepts upgrade calls
function upgrade(bytes calldata) external pure {
revert("upg11"); // upg11 - interception of upgrade call
}
/// @notice Returns target of contract
/// @return target Actual implementation address
function getTarget() public view returns (address target) {
bytes32 position = targetPosition;
assembly {
target := sload(position)
}
}
/// @notice Sets new target of contract
/// @param _newTarget New actual implementation address
function setTarget(address _newTarget) internal {
bytes32 position = targetPosition;
assembly {
sstore(position, _newTarget)
}
}
/// @notice Upgrades target
/// @param newTarget New target
/// @param newTargetUpgradeParameters New target upgrade parameters
function upgradeTarget(address newTarget, bytes calldata newTargetUpgradeParameters) external override {
requireMaster(msg.sender);
setTarget(newTarget);
(bool upgradeSuccess, ) =
getTarget().delegatecall(abi.encodeWithSignature("upgrade(bytes)", newTargetUpgradeParameters));
require(upgradeSuccess, "ufu11"); // ufu11 - target upgrade failed
}
/// @notice Performs a delegatecall to the contract implementation
/// @dev Fallback function allowing to perform a delegatecall to the given implementation
/// This function will return whatever the implementation call returns
function _fallback() internal {
address _target = getTarget();
assembly {
// The pointer to the free memory slot
let ptr := mload(0x40)
// Copy function signature and arguments from calldata at zero position into memory at pointer position
calldatacopy(ptr, 0x0, calldatasize())
// Delegatecall method of the implementation contract, returns 0 on error
let result := delegatecall(gas(), _target, ptr, calldatasize(), 0x0, 0)
// Get the size of the last return data
let size := returndatasize()
// Copy the size length of bytes from return data at zero position to pointer position
returndatacopy(ptr, 0x0, size)
// Depending on result value
switch result
case 0 {
// End execution and revert state changes
revert(ptr, size)
}
default {
// Return data with length of size at pointers position
return(ptr, size)
}
}
}
/// @notice Will run when no functions matches call data
fallback() external payable {
_fallback();
}
/// @notice Same as fallback but called when calldata is empty
receive() external payable {
_fallback();
}
/// UpgradeableMaster functions
/// @notice Notice period before activation preparation status of upgrade mode
function getNoticePeriod() external override returns (uint256) {
(bool success, bytes memory result) = getTarget().delegatecall(abi.encodeWithSignature("getNoticePeriod()"));
require(success, "unp11"); // unp11 - upgradeNoticePeriod delegatecall failed
return abi.decode(result, (uint256));
}
/// @notice Notifies proxy contract that notice period started
function upgradeNoticePeriodStarted() external override {
requireMaster(msg.sender);
(bool success, ) = getTarget().delegatecall(abi.encodeWithSignature("upgradeNoticePeriodStarted()"));
require(success, "nps11"); // nps11 - upgradeNoticePeriodStarted delegatecall failed
}
/// @notice Notifies proxy contract that upgrade preparation status is activated
function upgradePreparationStarted() external override {
requireMaster(msg.sender);
(bool success, ) = getTarget().delegatecall(abi.encodeWithSignature("upgradePreparationStarted()"));
require(success, "ups11"); // ups11 - upgradePreparationStarted delegatecall failed
}
/// @notice Notifies proxy contract that upgrade canceled
function upgradeCanceled() external override {
requireMaster(msg.sender);
(bool success, ) = getTarget().delegatecall(abi.encodeWithSignature("upgradeCanceled()"));
require(success, "puc11"); // puc11 - upgradeCanceled delegatecall failed
}
/// @notice Notifies proxy contract that upgrade finishes
function upgradeFinishes() external override {
requireMaster(msg.sender);
(bool success, ) = getTarget().delegatecall(abi.encodeWithSignature("upgradeFinishes()"));
require(success, "puf11"); // puf11 - upgradeFinishes delegatecall failed
}
/// @notice Checks that contract is ready for upgrade
/// @return bool flag indicating that contract is ready for upgrade
function isReadyForUpgrade() external override returns (bool) {
(bool success, bytes memory result) = getTarget().delegatecall(abi.encodeWithSignature("isReadyForUpgrade()"));
require(success, "rfu11"); // rfu11 - readyForUpgrade delegatecall failed
return abi.decode(result, (bool));
}
}
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT OR Apache-2.0
import "./SafeMath.sol";
import "./Events.sol";
import "./Ownable.sol";
import "./Upgradeable.sol";
import "./UpgradeableMaster.sol";
/// @title Upgrade Gatekeeper Contract
/// @author Matter Labs
contract UpgradeGatekeeper is UpgradeEvents, Ownable {
using SafeMath for uint256;
/// @notice Array of addresses of upgradeable contracts managed by the gatekeeper
Upgradeable[] public managedContracts;
/// @notice Upgrade mode statuses
enum UpgradeStatus {Idle, NoticePeriod, Preparation}
UpgradeStatus public upgradeStatus;
/// @notice Notice period finish timestamp (as seconds since unix epoch)
/// @dev Will be equal to zero in case of not active upgrade mode
uint256 public noticePeriodFinishTimestamp;
/// @notice Addresses of the next versions of the contracts to be upgraded (if element of this array is equal to zero address it means that appropriate upgradeable contract wouldn't be upgraded this time)
/// @dev Will be empty in case of not active upgrade mode
address[] public nextTargets;
/// @notice Version id of contracts
uint256 public versionId;
/// @notice Contract which defines notice period duration and allows finish upgrade during preparation of it
UpgradeableMaster public mainContract;
/// @notice Contract constructor
/// @param _mainContract Contract which defines notice period duration and allows finish upgrade during preparation of it
/// @dev Calls Ownable contract constructor
constructor(UpgradeableMaster _mainContract) Ownable(msg.sender) {
mainContract = _mainContract;
versionId = 0;
}
/// @notice Adds a new upgradeable contract to the list of contracts managed by the gatekeeper
/// @param addr Address of upgradeable contract to add
function addUpgradeable(address addr) external {
requireMaster(msg.sender);
require(upgradeStatus == UpgradeStatus.Idle, "apc11"); /// apc11 - upgradeable contract can't be added during upgrade
managedContracts.push(Upgradeable(addr));
emit NewUpgradable(versionId, addr);
}
/// @notice Starts upgrade (activates notice period)
/// @param newTargets New managed contracts targets (if element of this array is equal to zero address it means that appropriate upgradeable contract wouldn't be upgraded this time)
function startUpgrade(address[] calldata newTargets) external {
requireMaster(msg.sender);
require(upgradeStatus == UpgradeStatus.Idle, "spu11"); // spu11 - unable to activate active upgrade mode
require(newTargets.length == managedContracts.length, "spu12"); // spu12 - number of new targets must be equal to the number of managed contracts
uint256 noticePeriod = mainContract.getNoticePeriod();
mainContract.upgradeNoticePeriodStarted();
upgradeStatus = UpgradeStatus.NoticePeriod;
noticePeriodFinishTimestamp = block.timestamp.add(noticePeriod);
nextTargets = newTargets;
emit NoticePeriodStart(versionId, newTargets, noticePeriod);
}
/// @notice Cancels upgrade
function cancelUpgrade() external {
requireMaster(msg.sender);
require(upgradeStatus != UpgradeStatus.Idle, "cpu11"); // cpu11 - unable to cancel not active upgrade mode
mainContract.upgradeCanceled();
upgradeStatus = UpgradeStatus.Idle;
noticePeriodFinishTimestamp = 0;
delete nextTargets;
emit UpgradeCancel(versionId);
}
/// @notice Activates preparation status
/// @return Bool flag indicating that preparation status has been successfully activated
function startPreparation() external returns (bool) {
requireMaster(msg.sender);
require(upgradeStatus == UpgradeStatus.NoticePeriod, "ugp11"); // ugp11 - unable to activate preparation status in case of not active notice period status
if (block.timestamp >= noticePeriodFinishTimestamp) {
upgradeStatus = UpgradeStatus.Preparation;
mainContract.upgradePreparationStarted();
emit PreparationStart(versionId);
return true;
} else {
return false;
}
}
/// @notice Finishes upgrade
/// @param targetsUpgradeParameters New targets upgrade parameters per each upgradeable contract
function finishUpgrade(bytes[] calldata targetsUpgradeParameters) external {
requireMaster(msg.sender);
require(upgradeStatus == UpgradeStatus.Preparation, "fpu11"); // fpu11 - unable to finish upgrade without preparation status active
require(targetsUpgradeParameters.length == managedContracts.length, "fpu12"); // fpu12 - number of new targets upgrade parameters must be equal to the number of managed contracts
require(mainContract.isReadyForUpgrade(), "fpu13"); // fpu13 - main contract is not ready for upgrade
mainContract.upgradeFinishes();
for (uint64 i = 0; i < managedContracts.length; i++) {
address newTarget = nextTargets[i];
if (newTarget != address(0)) {
managedContracts[i].upgradeTarget(newTarget, targetsUpgradeParameters[i]);
}
}
versionId++;
emit UpgradeComplete(versionId, nextTargets);
upgradeStatus = UpgradeStatus.Idle;
noticePeriodFinishTimestamp = 0;
delete nextTargets;
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
contract TokenDeployInit {
function getTokens() internal pure returns (address[] memory) {
address[] memory tokens = new address[](0);
return tokens;
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: MIT OR Apache-2.0
/// @title Ownable Contract
/// @author Matter Labs
contract Ownable {
/// @dev Storage position of the masters address (keccak256('eip1967.proxy.admin') - 1)
bytes32 private constant masterPosition = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice Contract constructor
/// @dev Sets msg sender address as masters address
/// @param masterAddress Master address
constructor(address masterAddress) {
setMaster(masterAddress);
}
/// @notice Check if specified address is master
/// @param _address Address to check
function requireMaster(address _address) internal view {
require(_address == getMaster(), "1c"); // oro11 - only by master
}
/// @notice Returns contract masters address
/// @return master Master's address
function getMaster() public view returns (address master) {
bytes32 position = masterPosition;
assembly {
master := sload(position)
}
}
/// @dev Sets new masters address
/// @param _newMaster New master's address
function setMaster(address _newMaster) internal {
bytes32 position = masterPosition;
assembly {
sstore(position, _newMaster)
}
}
/// @notice Transfer mastership of the contract to new master
/// @param _newMaster New masters address
function transferMastership(address _newMaster) external {
requireMaster(msg.sender);
require(_newMaster != address(0), "1d"); // otp11 - new masters address can't be zero address
setMaster(_newMaster);
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
import "./ContextTest.sol";
import "./MintableIERC20NoTransferReturnValueTest.sol";
import "../../SafeMath.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract MintableERC20NoTransferReturnValueTest is ContextTest, MintableIERC20NoTransferReturnValueTest {
using SafeMath for uint256;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
function mint(address to, uint256 amount) external override {
_mint(to, amount);
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public override {
_transfer(_msgSender(), recipient, amount);
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public override {
_transfer(sender, recipient, amount);
_approve(
sender,
_msgSender(),
_allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")
);
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")
);
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(
account,
_msgSender(),
_allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")
);
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextTest {
function _msgSender() internal view returns (address payable) {
return msg.sender;
}
function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface MintableIERC20NoTransferReturnValueTest {
function mint(address to, uint256 amount) external;
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external;
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external;
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
import "./ContextTest.sol";
import "./MintableIERC20Test.sol";
import "../../SafeMath.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract MintableERC20FeeAndDividendsTest is ContextTest, MintableIERC20Test {
using SafeMath for uint256;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
function mint(address to, uint256 amount) external override {
_mint(to, amount);
}
bool _shouldBeFeeTransfers;
bool _senderUnintuitiveProcess;
uint256 public FEE_AMOUNT_AS_VALUE = 15;
uint256 public DIVIDEND_AMOUNT_AS_VALUE = 7;
/// shouldBeFeeTransfers - true if there is should be taken fee, false if there should be dividends
/// senderUnintuitiveProcess - true if there is should be taken fee from sender (or dividends for him), false if this process works with recipient
constructor(bool shouldBeFeeTransfers, bool senderUnintuitiveProcess) {
_shouldBeFeeTransfers = shouldBeFeeTransfers;
_senderUnintuitiveProcess = senderUnintuitiveProcess;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public override returns (bool) {
_transfer(sender, recipient, amount);
_approve(
sender,
_msgSender(),
_allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")
);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")
);
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
if (_shouldBeFeeTransfers) {
require(FEE_AMOUNT_AS_VALUE <= amount, "tet10"); // tet10 - fee is bigger than transfer amount
if (_senderUnintuitiveProcess) {
_burn(sender, FEE_AMOUNT_AS_VALUE);
} else {
_burn(recipient, FEE_AMOUNT_AS_VALUE);
}
} else {
if (_senderUnintuitiveProcess) {
_mint(sender, DIVIDEND_AMOUNT_AS_VALUE);
} else {
_mint(recipient, DIVIDEND_AMOUNT_AS_VALUE);
}
}
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(
account,
_msgSender(),
_allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")
);
}
}
pragma solidity ^0.7.0;
// SPDX-License-Identifier: UNLICENSED
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface MintableIERC20Test {
function mint(address to, uint256 amount) external;
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}