Skip to content

Instantly share code, notes, and snippets.

@dvush
Created June 15, 2020 19:30
Show Gist options
  • Save dvush/54a590b9585a00eb39696628ee00fa69 to your computer and use it in GitHub Desktop.
Save dvush/54a590b9585a00eb39696628ee00fa69 to your computer and use it in GitHub Desktop.
diff --git a/contracts/contracts/Bytes.sol b/contracts/contracts/Bytes.sol
index d9813152..756b01f0 100644
--- a/contracts/contracts/Bytes.sol
+++ b/contracts/contracts/Bytes.sol
@@ -1,5 +1,12 @@
pragma solidity ^0.5.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 {
@@ -38,75 +45,80 @@ library Bytes {
bts = toBytesFromUIntTruncated(uint(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 newOffset = _start + 20;
- require(self.length >= newOffset, "bta11");
+ uint256 offset = _start + 20;
+ require(self.length >= offset, "bta11");
assembly {
- addr := mload(add(self, newOffset))
+ 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), "btb20");
assembly {
- // Note that bytes1..32 is stored in the beginning of the word unlike other primitive types
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 newOffset = _start + 0x2;
- require(_bytes.length >= newOffset, "btu02");
+ uint256 offset = _start + 0x2;
+ require(_bytes.length >= offset, "btu02");
assembly {
- r := mload(add(_bytes, newOffset))
+ 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 newOffset = _start + 0x3;
- require(_bytes.length >= newOffset, "btu03");
+ uint256 offset = _start + 0x3;
+ require(_bytes.length >= offset, "btu03");
assembly {
- r := mload(add(_bytes, newOffset))
+ 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 newOffset = _start + 0x4;
- require(_bytes.length >= newOffset, "btu04");
+ uint256 offset = _start + 0x4;
+ require(_bytes.length >= offset, "btu04");
assembly {
- r := mload(add(_bytes, newOffset))
+ 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 newOffset = _start + 0x10;
- require(_bytes.length >= newOffset, "btu16");
+ uint256 offset = _start + 0x10;
+ require(_bytes.length >= offset, "btu16");
assembly {
- r := mload(add(_bytes, newOffset))
+ 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 newOffset = _start + 0x14;
- require(_bytes.length >= newOffset, "btu20");
+ uint256 offset = _start + 0x14;
+ require(_bytes.length >= offset, "btu20");
assembly {
- r := mload(add(_bytes, newOffset))
+ 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 newOffset = _start + 0x20;
- require(_bytes.length >= newOffset, "btb32");
+ uint256 offset = _start + 0x20;
+ require(_bytes.length >= offset, "btb32");
assembly {
- r := mload(add(_bytes, newOffset))
+ r := mload(add(_bytes, offset))
}
}
@@ -227,9 +239,30 @@ library Bytes {
// Convert bytes to ASCII hex representation
function bytesToHexASCIIBytes(bytes memory _input) internal pure returns (bytes memory _output) {
bytes memory outStringBytes = new bytes(_input.length * 2);
- for (uint i = 0; i < _input.length; ++i) {
- outStringBytes[i*2] = halfByteToHex(_input[i] >> 4);
- outStringBytes[i*2+1] = halfByteToHex(_input[i] & 0x0f);
+
+ // code in `assembly` construction is equivalent of the next code:
+ // for (uint i = 0; i < _input.length; ++i) {
+ // outStringBytes[i*2] = halfByteToHex(_input[i] >> 4);
+ // outStringBytes[i*2+1] = halfByteToHex(_input[i] & 0x0f);
+ // }
+ assembly {
+ let input_curr := add(_input, 0x20)
+ let input_end := add(input_curr, mload(_input))
+
+ for {
+ let out_curr := add(outStringBytes, 0x20)
+ } lt(input_curr, input_end) {
+ input_curr := add(input_curr, 0x01)
+ out_curr := add(out_curr, 0x02)
+ } {
+ let curr_input_byte := shr(0xf8, mload(input_curr))
+ // here outStringByte from each half of input byte calculates by the next:
+ //
+ // "FEDCBA9876543210" ASCII-encoded, shifted and automatically truncated.
+ // outStringByte = byte (uint8 (0x66656463626139383736353433323130 >> (uint8 (_byteHalf) * 8)))
+ mstore(out_curr, shl(0xf8, shr(mul(shr(0x04, curr_input_byte), 0x08), 0x66656463626139383736353433323130)))
+ mstore(add(out_curr, 0x01), shl(0xf8, shr(mul(and(0x0f, curr_input_byte), 0x08), 0x66656463626139383736353433323130)))
+ }
}
return outStringBytes;
}
diff --git a/contracts/contracts/Events.sol b/contracts/contracts/Events.sol
index 43dd954a..a5de3260 100644
--- a/contracts/contracts/Events.sol
+++ b/contracts/contracts/Events.sol
@@ -77,30 +77,30 @@ interface UpgradeEvents {
/// @notice Event emitted when new upgradeable contract is added to upgrade gatekeeper's list of managed contracts
event NewUpgradable(
- uint versionId,
- address upgradeable
+ uint indexed versionId,
+ address indexed upgradeable
);
/// @notice Upgrade mode enter event
event NoticePeriodStart(
- uint versionId,
+ uint indexed versionId,
address[] newTargets,
uint noticePeriod // notice period (in seconds)
);
/// @notice Upgrade mode cancel event
event UpgradeCancel(
- uint versionId
+ uint indexed versionId
);
/// @notice Upgrade mode preparation status event
event PreparationStart(
- uint versionId
+ uint indexed versionId
);
/// @notice Upgrade mode complete event
event UpgradeComplete(
- uint versionId,
+ uint indexed versionId,
address[] newTargets
);
diff --git a/contracts/contracts/Ownable.sol b/contracts/contracts/Ownable.sol
index d0d6d5da..cfd6a4e0 100644
--- a/contracts/contracts/Ownable.sol
+++ b/contracts/contracts/Ownable.sol
@@ -5,7 +5,7 @@ pragma solidity ^0.5.0;
contract Ownable {
/// @notice Storage position of the masters address (keccak256('eip1967.proxy.admin') - 1)
- bytes32 private constant masterPosition = bytes32(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103);
+ bytes32 private constant masterPosition = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice Contract constructor
/// @dev Sets msg sender address as masters address
diff --git a/contracts/contracts/Proxy.sol b/contracts/contracts/Proxy.sol
index 81aaf985..93dcea11 100644
--- a/contracts/contracts/Proxy.sol
+++ b/contracts/contracts/Proxy.sol
@@ -11,7 +11,7 @@ import "./UpgradeableMaster.sol";
contract Proxy is Upgradeable, UpgradeableMaster, Ownable {
/// @notice Storage position of "target" (actual implementation address: keccak256('eip1967.proxy.implementation') - 1)
- bytes32 private constant targetPosition = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
+ bytes32 private constant targetPosition = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice Contract constructor
/// @dev Calls Ownable contract constructor and initialize target
diff --git a/contracts/contracts/ReentrancyGuard.sol b/contracts/contracts/ReentrancyGuard.sol
index 892f6ba6..2d1f67d5 100644
--- a/contracts/contracts/ReentrancyGuard.sol
+++ b/contracts/contracts/ReentrancyGuard.sol
@@ -22,7 +22,7 @@ pragma solidity ^0.5.0;
contract ReentrancyGuard {
/// Address of lock flag variable.
/// Flag is placed at random memory location to not interfere with Storage contract.
- uint constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1;
+ uint constant private LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1;
function initializeReentrancyGuard () internal {
// Storing an initial non-zero value makes deployment a bit more
diff --git a/contracts/contracts/ZkSync.sol b/contracts/contracts/ZkSync.sol
index 56b976c3..4640f220 100644
--- a/contracts/contracts/ZkSync.sol
+++ b/contracts/contracts/ZkSync.sol
@@ -21,7 +21,7 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
using SafeMath for uint256;
using SafeMathUInt128 for uint128;
- bytes32 public constant EMPTY_STRING_KECCAK = bytes32(0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470);
+ bytes32 public constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// Upgrade functional
@@ -98,9 +98,10 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
uint256 balance_before = _token.balanceOf(address(this));
require(Utils.sendERC20(_token, _to, _amount), "wtg11"); // wtg11 - ERC20 transfer fails
uint256 balance_after = _token.balanceOf(address(this));
- require(balance_before.sub(balance_after) <= _maxAmount, "wtg12"); // wtg12 - rollup balance difference (before and after transfer) is bigger than _maxAmount
+ uint256 balance_diff = balance_before.sub(balance_after);
+ require(balance_diff <= _maxAmount, "wtg12"); // wtg12 - rollup balance difference (before and after transfer) is bigger than _maxAmount
- return SafeCast.toUint128(balance_before.sub(balance_after));
+ return SafeCast.toUint128(balance_diff);
}
/// @notice executes pending withdrawals
@@ -110,11 +111,7 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
uint32 toProcess = Utils.minU32(_n, numberOfPendingWithdrawals);
uint32 startIndex = firstPendingWithdrawalIndex;
numberOfPendingWithdrawals -= toProcess;
- if (numberOfPendingWithdrawals == 0) {
- firstPendingWithdrawalIndex = 0;
- } else {
- firstPendingWithdrawalIndex += toProcess;
- }
+ firstPendingWithdrawalIndex += toProcess;
for (uint32 i = startIndex; i < startIndex + toProcess; ++i) {
uint16 tokenId = pendingWithdrawals[i].tokenId;
@@ -191,7 +188,7 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
uint16 tokenId = governance.validateTokenAddress(address(_token));
uint256 balance_before = _token.balanceOf(address(this));
- require(_token.transferFrom(msg.sender, address(this), uint128(_amount)), "fd012"); // token transfer failed deposit
+ require(_token.transferFrom(msg.sender, address(this), SafeCast.toUint128(_amount)), "fd012"); // token transfer failed deposit
uint256 balance_after = _token.balanceOf(address(this));
uint128 deposit_amount = SafeCast.toUint128(balance_after.sub(balance_before));
@@ -214,6 +211,7 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
/// @param _token Token address, 0 address for ether
function fullExit (uint32 _accountId, address _token) external nonReentrant {
requireActive();
+ require(_accountId <= MAX_ACCOUNT_ID, "fee11");
uint16 tokenId;
if (_token == address(0)) {
@@ -256,25 +254,22 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
requireActive();
require(_blockNumber == totalBlocksCommitted + 1, "fck11"); // only commit next block
governance.requireActiveValidator(msg.sender);
- require(!isBlockCommitmentExpired(), "fck12"); // committed blocks had expired
require(_newBlockInfo.length == 1, "fck13"); // This version of the contract expects only account tree root hash
bytes memory publicData = _publicData;
- if(!triggerExodusIfNeeded()) {
- // Unpack onchain operations and store them.
- // Get priority operations number for this block.
- uint64 prevTotalCommittedPriorityRequests = totalCommittedPriorityRequests;
+ // Unpack onchain operations and store them.
+ // Get priority operations number for this block.
+ uint64 prevTotalCommittedPriorityRequests = totalCommittedPriorityRequests;
- bytes32 withdrawalsDataHash = collectOnchainOps(_blockNumber, publicData, _ethWitness, _ethWitnessSizes);
+ bytes32 withdrawalsDataHash = collectOnchainOps(_blockNumber, publicData, _ethWitness, _ethWitnessSizes);
- uint64 nPriorityRequestProcessed = totalCommittedPriorityRequests - prevTotalCommittedPriorityRequests;
+ uint64 nPriorityRequestProcessed = totalCommittedPriorityRequests - prevTotalCommittedPriorityRequests;
- createCommittedBlock(_blockNumber, _feeAccount, _newBlockInfo[0], publicData, withdrawalsDataHash, nPriorityRequestProcessed);
- totalBlocksCommitted++;
+ createCommittedBlock(_blockNumber, _feeAccount, _newBlockInfo[0], publicData, withdrawalsDataHash, nPriorityRequestProcessed);
+ totalBlocksCommitted++;
- emit BlockCommit(_blockNumber);
- }
+ emit BlockCommit(_blockNumber);
}
/// @notice Block verification.
@@ -307,6 +302,7 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
/// @param _maxBlocksToRevert the maximum number blocks that will be reverted (use if can't revert all blocks because of gas limit).
function revertBlocks(uint32 _maxBlocksToRevert) external nonReentrant {
require(isBlockCommitmentExpired(), "rbs11"); // trying to revert non-expired blocks.
+ governance.requireActiveValidator(msg.sender);
uint32 blocksCommited = totalBlocksCommitted;
uint32 blocksToRevert = Utils.minU32(_maxBlocksToRevert, blocksCommited - totalBlocksVerified);
@@ -337,7 +333,7 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
/// @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 triggerExodusIfNeeded() public returns (bool) {
+ function triggerExodusIfNeeded() external returns (bool) {
bool trigger = block.number >= priorityRequests[firstPriorityRequestId].expirationBlock &&
priorityRequests[firstPriorityRequestId].expirationBlock != 0;
if (trigger) {
@@ -678,7 +674,11 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
bytes22 packedBalanceKey = packAddressAndTokenId(_to, _tokenId);
uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
- balancesToWithdraw[packedBalanceKey].balanceToWithdraw = balance.add(_amount);
+ // after this all writes to this slot will cost 5k gas
+ balancesToWithdraw[packedBalanceKey] = BalanceToWithdraw({
+ balanceToWithdraw: balance.add(_amount),
+ gasReserveValue: 0xff
+ });
if (addToPendingWithdrawalsQueue) {
pendingWithdrawals[firstPendingWithdrawalIndex + localNumberOfPendingWithdrawals] = PendingWithdrawal(_to, _tokenId);
@@ -746,8 +746,9 @@ contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
function deleteRequests(uint64 _number) internal {
require(_number <= totalOpenPriorityRequests, "pcs21"); // 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 + _number; i++) {
+ for (uint64 i = startIndex; i < startIndex + numberOfRequestsToClear; i++) {
delete priorityRequests[i];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment