Skip to content

Instantly share code, notes, and snippets.

@dvush
Last active June 3, 2020 15:04
Show Gist options
  • Save dvush/baa8ae945365681464412e78a14c9262 to your computer and use it in GitHub Desktop.
Save dvush/baa8ae945365681464412e78a14c9262 to your computer and use it in GitHub Desktop.
diff --git a/contracts/contracts/Bytes.sol b/contracts/contracts/Bytes.sol
index 8fe0b3c1..d9813152 100644
--- a/contracts/contracts/Bytes.sol
+++ b/contracts/contracts/Bytes.sol
@@ -38,13 +38,16 @@ library Bytes {
bts = toBytesFromUIntTruncated(uint(self), 20);
}
+ // NOTE: theoretically possible overflow of (_start + 20)
function bytesToAddress(bytes memory self, uint256 _start) internal pure returns (address addr) {
- require(self.length >= (_start + 20), "bta11");
+ uint256 newOffset = _start + 20;
+ require(self.length >= newOffset, "bta11");
assembly {
- addr := mload(add(add(self, 20), _start))
+ addr := mload(add(self, newOffset))
}
}
+ // 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 {
@@ -53,51 +56,64 @@ library Bytes {
}
}
+ // NOTE: theoretically possible overflow of (_start + 0x2)
function bytesToUInt16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 r) {
- require(_bytes.length >= (_start + 2), "btu02");
+ uint256 newOffset = _start + 0x2;
+ require(_bytes.length >= newOffset, "btu02");
assembly {
- r := mload(add(add(_bytes, 0x2), _start))
+ r := mload(add(_bytes, newOffset))
}
}
+ // NOTE: theoretically possible overflow of (_start + 0x3)
function bytesToUInt24(bytes memory _bytes, uint256 _start) internal pure returns (uint24 r) {
- require(_bytes.length >= (_start + 3), "btu03");
+ uint256 newOffset = _start + 0x3;
+ require(_bytes.length >= newOffset, "btu03");
assembly {
- r := mload(add(add(_bytes, 0x3), _start))
+ r := mload(add(_bytes, newOffset))
}
}
+ // NOTE: theoretically possible overflow of (_start + 0x4)
function bytesToUInt32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 r) {
- require(_bytes.length >= (_start + 4), "btu04");
+ uint256 newOffset = _start + 0x4;
+ require(_bytes.length >= newOffset, "btu04");
assembly {
- r := mload(add(add(_bytes, 0x4), _start))
+ r := mload(add(_bytes, newOffset))
}
}
+ // NOTE: theoretically possible overflow of (_start + 0x10)
function bytesToUInt128(bytes memory _bytes, uint256 _start) internal pure returns (uint128 r) {
- require(_bytes.length >= (_start + 16), "btu16");
+ uint256 newOffset = _start + 0x10;
+ require(_bytes.length >= newOffset, "btu16");
assembly {
- r := mload(add(add(_bytes, 0x10), _start))
+ r := mload(add(_bytes, newOffset))
}
}
+ // NOTE: theoretically possible overflow of (_start + 0x14)
function bytesToUInt160(bytes memory _bytes, uint256 _start) internal pure returns (uint160 r) {
- require(_bytes.length >= (_start + 20), "btu20");
+ uint256 newOffset = _start + 0x14;
+ require(_bytes.length >= newOffset, "btu20");
assembly {
- r := mload(add(add(_bytes, 0x14), _start))
+ r := mload(add(_bytes, newOffset))
}
}
+ // NOTE: theoretically possible overflow of (_start + 0x20)
function bytesToBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 r) {
- require(_bytes.length >= 0x20, "btb32");
+ uint256 newOffset = _start + 0x20;
+ require(_bytes.length >= newOffset, "btb32");
assembly {
- r := mload(add(add(_bytes, 0x20), _start))
+ r := mload(add(_bytes, newOffset))
}
}
// 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,
uint _start,
@@ -118,8 +134,6 @@ library Bytes {
let slice_end := add(slice_curr, _length)
for {
- // The multiplication in the next line has the same exact purpose
- // as the one above.
let array_current := add(_bytes, add(_start, 0x20))
} lt(slice_curr, slice_end) {
slice_curr := add(slice_curr, 0x20)
@@ -136,56 +150,67 @@ library Bytes {
/// 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, uint _offset, uint _length) internal pure returns (uint 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, uint _offset) internal pure returns (uint 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, uint _offset) internal pure returns (uint new_offset, uint8 r) {
new_offset = _offset + 1;
r = uint8(_data[_offset]);
}
+ // NOTE: theoretically possible overflow of (_offset + 2)
function readUInt16(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint16 r) {
new_offset = _offset + 2;
r = bytesToUInt16(_data, _offset);
}
+ // NOTE: theoretically possible overflow of (_offset + 3)
function readUInt24(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint24 r) {
new_offset = _offset + 3;
r = bytesToUInt24(_data, _offset);
}
+ // NOTE: theoretically possible overflow of (_offset + 4)
function readUInt32(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint32 r) {
new_offset = _offset + 4;
r = bytesToUInt32(_data, _offset);
}
+ // NOTE: theoretically possible overflow of (_offset + 16)
function readUInt128(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint128 r) {
new_offset = _offset + 16;
r = bytesToUInt128(_data, _offset);
}
+ // NOTE: theoretically possible overflow of (_offset + 20)
function readUInt160(bytes memory _data, uint _offset) internal pure returns (uint new_offset, uint160 r) {
new_offset = _offset + 20;
r = bytesToUInt160(_data, _offset);
}
+ // NOTE: theoretically possible overflow of (_offset + 20)
function readAddress(bytes memory _data, uint _offset) internal pure returns (uint new_offset, address r) {
new_offset = _offset + 20;
r = bytesToAddress(_data, _offset);
}
+ // NOTE: theoretically possible overflow of (_offset + 20)
function readBytes20(bytes memory _data, uint _offset) internal pure returns (uint new_offset, bytes20 r) {
new_offset = _offset + 20;
r = bytesToBytes20(_data, _offset);
}
+ // NOTE: theoretically possible overflow of (_offset + 32)
function readBytes32(bytes memory _data, uint _offset) internal pure returns (uint new_offset, bytes32 r) {
new_offset = _offset + 32;
r = bytesToBytes32(_data, _offset);
@@ -193,7 +218,7 @@ library Bytes {
// Helper function for hex conversion.
function halfByteToHex(byte _byte) internal pure returns (byte _hexByte) {
- require(uint8(_byte) | 0xf == 0xf, "hbh11"); // half byte's value is out of 0..15 range.
+ require(uint8(_byte) < 0x10, "hbh11"); // half byte's value is out of 0..15 range.
// "FEDCBA9876543210" ASCII-encoded, shifted and automatically truncated.
return byte (uint8 (0x66656463626139383736353433323130 >> (uint8 (_byte) * 8)));
diff --git a/contracts/contracts/Config.sol b/contracts/contracts/Config.sol
index d83aa242..e60ddeae 100644
--- a/contracts/contracts/Config.sol
+++ b/contracts/contracts/Config.sol
@@ -6,7 +6,7 @@ pragma solidity ^0.5.0;
contract Config {
/// @notice Notice period before activation preparation status of upgrade mode (in seconds)
- uint constant UPGRADE_NOTICE_PERIOD = 1 weeks;
+ uint constant UPGRADE_NOTICE_PERIOD = 1 days;
/// @notice Period after the start of preparation upgrade when contract wouldn't register new priority operations (in seconds)
uint constant UPGRADE_PREPARATION_LOCK_PERIOD = 1 days;
@@ -17,10 +17,12 @@ contract Config {
/// @notice ETH token withdrawal gas limit, used only for complete withdrawals
uint256 constant ETH_WITHDRAWAL_GAS_LIMIT = 10000;
+ /// @notice Bytes in one chunk
+ uint8 constant CHUNK_BYTES = 9;
+
/// @notice zkSync address length
uint8 constant ADDRESS_BYTES = 20;
- // TODO: check everywhere!
uint8 constant PUBKEY_HASH_BYTES = 20;
/// @notice Public key bytes length
@@ -41,20 +43,20 @@ contract Config {
/// @notice ETH blocks verification expectation
uint256 constant EXPECT_VERIFICATION_IN = 2 days / BLOCK_PERIOD;
- uint256 constant NOOP_BYTES = 1 * 8;
- uint256 constant DEPOSIT_BYTES = 6 * 8;
- uint256 constant TRANSFER_TO_NEW_BYTES = 5 * 8;
- uint256 constant PARTIAL_EXIT_BYTES = 6 * 8;
- uint256 constant TRANSFER_BYTES = 2 * 8;
+ 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;
/// @notice Full exit operation length
- uint256 constant FULL_EXIT_BYTES = 6 * 8;
+ uint256 constant FULL_EXIT_BYTES = 6 * CHUNK_BYTES;
/// @notice OnchainWithdrawal data length
uint256 constant ONCHAIN_WITHDRAWAL_BYTES = 1 + 20 + 2 + 16; // (uint8 addToPendingWithdrawalsQueue, address _to, uint16 _tokenId, uint128 _amount)
/// @notice ChangePubKey operation length
- uint256 constant CHANGE_PUBKEY_BYTES = 6 * 8;
+ uint256 constant CHANGE_PUBKEY_BYTES = 6 * CHUNK_BYTES;
/// @notice Expiration delta for priority request to be satisfied (in ETH blocks)
/// NOTE: Priority expiration should be > EXPECT_VERIFICATION_IN, otherwise incorrect block with priority op could not be reverted.
diff --git a/contracts/contracts/Events.sol b/contracts/contracts/Events.sol
index 9710c2a8..43dd954a 100644
--- a/contracts/contracts/Events.sol
+++ b/contracts/contracts/Events.sol
@@ -6,7 +6,7 @@ import "./Operations.sol";
/// @title zkSync events
/// @author Matter Labs
-contract Events {
+interface Events {
/// @notice Event emitted when a block is committed
event BlockCommit(uint32 indexed blockNumber);
@@ -17,14 +17,14 @@ contract Events {
/// @notice Event emitted when user send a transaction to withdraw her funds from onchain balance
event OnchainWithdrawal(
address indexed owner,
- uint16 tokenId,
+ uint16 indexed tokenId,
uint128 amount
);
/// @notice Event emitted when user send a transaction to deposit her funds
event OnchainDeposit(
- address sender,
- uint16 tokenId,
+ address indexed sender,
+ uint16 indexed tokenId,
uint128 amount,
address indexed owner
);
@@ -55,46 +55,53 @@ contract Events {
);
event DepositCommit(
- uint32 franklinBlockId,
- uint24 accountId,
+ uint32 indexed franklinBlockId,
+ uint32 indexed accountId,
address owner,
- uint16 tokenId,
+ uint16 indexed tokenId,
uint128 amount
);
event FullExitCommit(
- uint32 franklinBlockId,
- uint24 accountId,
+ uint32 indexed franklinBlockId,
+ uint32 indexed accountId,
address owner,
- uint16 tokenId,
+ uint16 indexed tokenId,
uint128 amount
);
}
/// @title Upgrade events
/// @author Matter Labs
-contract UpgradeEvents {
+interface UpgradeEvents {
/// @notice Event emitted when new upgradeable contract is added to upgrade gatekeeper's list of managed contracts
- event UpgradeableAdd(
- Upgradeable upgradeable
+ event NewUpgradable(
+ uint versionId,
+ address upgradeable
);
/// @notice Upgrade mode enter event
event NoticePeriodStart(
- address[] newTargets
+ uint versionId,
+ address[] newTargets,
+ uint noticePeriod // notice period (in seconds)
);
/// @notice Upgrade mode cancel event
- event UpgradeCancel();
+ event UpgradeCancel(
+ uint versionId
+ );
/// @notice Upgrade mode preparation status event
- event PreparationStart();
+ event PreparationStart(
+ uint versionId
+ );
/// @notice Upgrade mode complete event
event UpgradeComplete(
- Upgradeable upgradeable,
- address newTargetAddress
+ uint versionId,
+ address[] newTargets
);
}
diff --git a/contracts/contracts/Governance.sol b/contracts/contracts/Governance.sol
index 70f5e22c..b6d3d83d 100644
--- a/contracts/contracts/Governance.sol
+++ b/contracts/contracts/Governance.sol
@@ -43,20 +43,27 @@ contract Governance is Config {
/// @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
+ /// _networkGovernor The address of network governor
function initialize(bytes calldata initializationParameters) external {
address _networkGovernor = abi.decode(initializationParameters, (address));
networkGovernor = _networkGovernor;
- validators[_networkGovernor] = true;
+ }
+
+ /// @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 {
+ revert("upggv"); // it is the first version, upgrade is not supported, use initialize
}
/// @notice Change current governor
/// @param _newGovernor Address of the new governor
function changeGovernor(address _newGovernor) external {
requireGovernor(msg.sender);
- networkGovernor = _newGovernor;
- emit NewGovernor(_newGovernor);
+ if (networkGovernor != _newGovernor) {
+ networkGovernor = _newGovernor;
+ emit NewGovernor(_newGovernor);
+ }
}
/// @notice Add token to the list of networks tokens
@@ -79,8 +86,10 @@ contract Governance is Config {
/// @param _active Active flag
function setValidator(address _validator, bool _active) external {
requireGovernor(msg.sender);
- validators[_validator] = _active;
- emit ValidatorStatusUpdate(_validator, _active);
+ if (validators[_validator] != _active) {
+ validators[_validator] = _active;
+ emit ValidatorStatusUpdate(_validator, _active);
+ }
}
/// @notice Check if specified address is is governor
diff --git a/contracts/contracts/Operations.sol b/contracts/contracts/Operations.sol
index e61e39ed..55a09755 100644
--- a/contracts/contracts/Operations.sol
+++ b/contracts/contracts/Operations.sol
@@ -36,7 +36,7 @@ library Operations {
uint8 constant FEE_BYTES = 2;
/// @notice zkSync account id bytes lengths
- uint8 constant ACCOUNT_ID_BYTES = 3;
+ uint8 constant ACCOUNT_ID_BYTES = 4;
uint8 constant AMOUNT_BYTES = 16;
@@ -44,11 +44,10 @@ library Operations {
uint8 constant SIGNATURE_BYTES = 64;
// Deposit pubdata
-
struct Deposit {
- uint24 accountId;
+ uint32 accountId;
uint16 tokenId;
- uint128 amount;
+ uint128 amount;
address owner;
}
@@ -61,7 +60,7 @@ library Operations {
{
// NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
uint offset = 0;
- (offset, parsed.accountId) = Bytes.readUInt24(_data, offset); // accountId
+ (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
@@ -72,10 +71,10 @@ library Operations {
/// Serialize deposit pubdata
function writeDepositPubdata(Deposit memory op) internal pure returns (bytes memory buf) {
buf = abi.encodePacked(
- new bytes(ACCOUNT_ID_BYTES), // accountId (ignored)
- Bytes.toBytesFromUInt16(op.tokenId), // tokenId
- Bytes.toBytesFromUInt128(op.amount), // amount
- Bytes.toBytesFromAddress(op.owner) // owner
+ bytes4(0), // accountId (ignored) (update when ACCOUNT_ID_BYTES is changed)
+ op.tokenId, // tokenId
+ op.amount, // amount
+ op.owner // owner
);
}
@@ -90,7 +89,7 @@ library Operations {
// FullExit pubdata
struct FullExit {
- uint24 accountId;
+ uint32 accountId;
address owner;
uint16 tokenId;
uint128 amount;
@@ -104,7 +103,7 @@ library Operations {
{
// NOTE: there is no check that variable sizes are same as constants (i.e. TOKEN_BYTES), fix if possible.
uint offset = 0;
- (offset, parsed.accountId) = Bytes.readUInt24(_data, offset); // accountId
+ (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
@@ -114,10 +113,10 @@ library Operations {
function writeFullExitPubdata(FullExit memory op) internal pure returns (bytes memory buf) {
buf = abi.encodePacked(
- Bytes.toBytesFromUInt24(op.accountId), // accountId
- Bytes.toBytesFromAddress(op.owner), // owner
- Bytes.toBytesFromUInt16(op.tokenId), // tokenId
- Bytes.toBytesFromUInt128(op.amount) // amount
+ op.accountId, // accountId
+ op.owner, // owner
+ op.tokenId, // tokenId
+ op.amount // amount
);
}
@@ -132,7 +131,7 @@ library Operations {
// PartialExit pubdata
struct PartialExit {
- //uint24 accountId; -- 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
@@ -152,18 +151,18 @@ library Operations {
function writePartialExitPubdata(PartialExit memory op) internal pure returns (bytes memory buf) {
buf = abi.encodePacked(
- new bytes(ACCOUNT_ID_BYTES), // accountId (ignored)
- Bytes.toBytesFromUInt16(op.tokenId), // tokenId
- Bytes.toBytesFromUInt128(op.amount), // amount
- new bytes(FEE_BYTES), // fee (ignored)
- Bytes.toBytesFromAddress(op.owner) // owner
+ bytes4(0), // accountId (ignored) (update when ACCOUNT_ID_BYTES is changed)
+ op.tokenId, // tokenId
+ op.amount, // amount
+ bytes2(0), // fee (ignored) (update when FEE_BYTES is changed)
+ op.owner // owner
);
}
// ChangePubKey
struct ChangePubKey {
- uint24 accountId;
+ uint32 accountId;
bytes20 pubKeyHash;
address owner;
uint32 nonce;
@@ -172,10 +171,8 @@ library Operations {
function readChangePubKeyPubdata(bytes memory _data, uint _offset) internal pure
returns (ChangePubKey memory parsed)
{
- require(PUBKEY_HASH_BYTES == 20, "rcp11"); // expected PUBKEY_HASH_BYTES to be 20
-
uint offset = _offset;
- (offset, parsed.accountId) = Bytes.readUInt24(_data, offset); // accountId
+ (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
diff --git a/contracts/contracts/Ownable.sol b/contracts/contracts/Ownable.sol
index a9042a22..d0d6d5da 100644
--- a/contracts/contracts/Ownable.sol
+++ b/contracts/contracts/Ownable.sol
@@ -4,8 +4,8 @@ pragma solidity ^0.5.0;
/// @author Matter Labs
contract Ownable {
- /// @notice Storage position of the masters address
- bytes32 private constant masterPosition = keccak256("master");
+ /// @notice Storage position of the masters address (keccak256('eip1967.proxy.admin') - 1)
+ bytes32 private constant masterPosition = bytes32(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103);
/// @notice Contract constructor
/// @dev Sets msg sender address as masters address
diff --git a/contracts/contracts/PlonkCore.sol b/contracts/contracts/PlonkCore.sol
index fe34e554..7e4a618b 100644
--- a/contracts/contracts/PlonkCore.sol
+++ b/contracts/contracts/PlonkCore.sol
@@ -3,46 +3,47 @@ pragma solidity >=0.5.0 <0.7.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) {
- assert(fr.value != 0);
+ 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;
@@ -53,7 +54,7 @@ library PairingsBn254 {
require(success);
return Fr({value: result[0]});
}
-
+
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint[2] X;
@@ -63,15 +64,34 @@ library PairingsBn254 {
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;
@@ -79,39 +99,41 @@ library PairingsBn254 {
function P2() internal pure returns (G2Point memory) {
// for some reason ethereum expects to have c1*v + c0 form
-
+
return G2Point(
[0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
- 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed],
+ 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed],
[0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
- 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa]
+ 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa]
);
}
function negate(G1Point memory self) internal pure {
// The prime q in the base field F_q for G1
- if (self.X == 0 && self.Y == 0)
+ 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)
+ 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
+ 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
+ internal view
{
- uint256[4] memory input;
if (p2.X == 0 && p2.Y == 0) {
// we add zero, nothing happens
dest.X = p1.X;
@@ -123,28 +145,30 @@ library PairingsBn254 {
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);
}
- 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
+ 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
+ internal view
{
- uint256[4] memory input;
if (p2.X == 0 && p2.Y == 0) {
// we subtracted zero, nothing happens
dest.X = p1.X;
@@ -156,34 +180,36 @@ library PairingsBn254 {
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);
}
- 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)
+ 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
+ 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
+ internal view
{
uint[3] memory input;
input[0] = p.X;
@@ -195,9 +221,9 @@ library PairingsBn254 {
}
require(success);
}
-
+
function pairing(G1Point[] memory p1, G2Point[] memory p2)
- internal view returns (bool)
+ internal view returns (bool)
{
require(p1.length == p2.length);
uint elements = p1.length;
@@ -223,7 +249,7 @@ library PairingsBn254 {
/// 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)
+ internal view returns (bool)
{
G1Point[] memory p1 = new G1Point[](2);
G2Point[] memory p2 = new G2Point[](2);
@@ -242,7 +268,7 @@ library TranscriptLibrary {
uint32 constant DST_0 = 0;
uint32 constant DST_1 = 1;
uint32 constant DST_CHALLENGE = 2;
-
+
struct Transcript {
bytes32 state_0;
bytes32 state_1;
@@ -260,16 +286,16 @@ library TranscriptLibrary {
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;
@@ -281,38 +307,39 @@ contract Plonk4VerifierWithAccessToDNext {
using PairingsBn254 for PairingsBn254.G1Point;
using PairingsBn254 for PairingsBn254.G2Point;
using PairingsBn254 for PairingsBn254.Fr;
-
+
using TranscriptLibrary for TranscriptLibrary.Transcript;
uint256 constant STATE_WIDTH = 4;
-
+ uint256 constant ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP = 1;
+
struct VerificationKey {
uint256 domain_size;
uint256 num_inputs;
PairingsBn254.Fr omega;
PairingsBn254.G1Point[STATE_WIDTH+2] selector_commitments; // STATE_WIDTH for witness + multiplication + constant
- PairingsBn254.G1Point[1] next_step_selector_commitments;
+ PairingsBn254.G1Point[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP] next_step_selector_commitments;
PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments;
PairingsBn254.Fr[STATE_WIDTH-1] permutation_non_residues;
PairingsBn254.G2Point g2_x;
}
-
+
struct Proof {
uint256[] input_values;
PairingsBn254.G1Point[STATE_WIDTH] wire_commitments;
PairingsBn254.G1Point grand_product_commitment;
PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_commitments;
PairingsBn254.Fr[STATE_WIDTH] wire_values_at_z;
- PairingsBn254.Fr[1] wire_values_at_z_omega;
+ PairingsBn254.Fr[ACCESSIBLE_STATE_POLYS_ON_NEXT_STEP] 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-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;
@@ -322,11 +349,11 @@ contract Plonk4VerifierWithAccessToDNext {
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,
+ 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);
@@ -334,22 +361,22 @@ contract Plonk4VerifierWithAccessToDNext {
PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
res = at.pow(domain_size);
res.sub_assign(one);
- assert(res.value != 0); // Vanishing polynomial can not be zero at point `at`
+ 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,
+ 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);
@@ -358,7 +385,7 @@ contract Plonk4VerifierWithAccessToDNext {
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
- assert(vanishing_at_z.value != 0);
+ 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)
@@ -367,56 +394,56 @@ contract Plonk4VerifierWithAccessToDNext {
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].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 (uint 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 (uint 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
+ dens[i].mul_assign(partial_products[i]); // clear lowest terms
tmp_2.mul_assign(dens[i]);
}
-
+
for (uint i = 0; i < nums.length; i++) {
nums[i].mul_assign(dens[i]);
}
return nums;
}
-
+
function evaluate_vanishing(
- uint256 domain_size,
+ 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,
+ Proof memory proof,
VerificationKey memory vk
) internal view returns (bool) {
PairingsBn254.Fr memory lhs = evaluate_vanishing(vk.domain_size, state.z);
- assert(lhs.value != 0); // we can not check a polynomial relationship if point `z` is in the domain
+ 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++) {
@@ -424,80 +451,80 @@ contract Plonk4VerifierWithAccessToDNext {
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 - 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(
PartialVerifierState memory state,
- Proof memory proof,
+ 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
+ // 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))
+ // 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))
+ // + 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 + STATE_WIDTH - 1;
res = PairingsBn254.copy_g1(vk.selector_commitments[STATE_WIDTH + 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; 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].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);
@@ -509,58 +536,58 @@ contract Plonk4VerifierWithAccessToDNext {
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 - 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(
PartialVerifierState memory state,
- Proof memory proof,
+ Proof memory proof,
VerificationKey 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 (uint i = 1; i < proof.quotient_poly_commitments.length; i++) {
@@ -571,19 +598,19 @@ contract Plonk4VerifierWithAccessToDNext {
aggregation_challenge.mul_assign(state.v);
commitment_aggregation.point_add_assign(d);
-
+
for (uint 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 (uint 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);
@@ -592,26 +619,26 @@ contract Plonk4VerifierWithAccessToDNext {
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 (uint 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 (uint i = 0; i < proof.permutation_polynomials_at_z.length; i++) {
aggregation_challenge.mul_assign(state.v);
@@ -619,41 +646,41 @@ contract Plonk4VerifierWithAccessToDNext {
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(
- PartialVerifierState memory state,
- Proof memory proof,
+ PartialVerifierState memory state,
+ Proof memory proof,
VerificationKey memory vk
) internal view returns (bool) {
require(proof.input_values.length == vk.num_inputs);
@@ -662,31 +689,31 @@ contract Plonk4VerifierWithAccessToDNext {
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(
lagrange_poly_numbers,
- vk.domain_size,
+ vk.domain_size,
vk.omega, state.z
);
@@ -695,73 +722,74 @@ contract Plonk4VerifierWithAccessToDNext {
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);
-
+
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_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
-
+ // q_d_next(X) "peeks" into the next row of the trace, so it takes
+ // the same d(X) polynomial, but shifted
+
function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) {
PartialVerifierState memory state;
-
+
bool valid = verify_initial(state, proof, vk);
-
+
if (valid == false) {
return false;
}
-
+
valid = verify_commitments(state, proof, vk);
-
+
return valid;
}
}
contract VerifierWithDeserialize is Plonk4VerifierWithAccessToDNext {
+ uint256 constant SERIALIZED_PROOF_LENGTH = 33;
+
function deserialize_proof(
- uint256 expected_inputs,
uint256[] memory public_inputs,
uint256[] memory serialized_proof
) internal pure returns(Proof memory proof) {
- assert(expected_inputs == public_inputs.length);
- proof.input_values = new uint256[](expected_inputs);
- for (uint256 i = 0; i < expected_inputs; i++) {
+ 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(
+ proof.wire_commitments[i] = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j+1]
);
@@ -769,14 +797,14 @@ contract VerifierWithDeserialize is Plonk4VerifierWithAccessToDNext {
j += 2;
}
- proof.grand_product_commitment = PairingsBn254.new_g1(
+ proof.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(
+ proof.quotient_poly_commitments[i] = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j+1]
);
@@ -826,18 +854,15 @@ contract VerifierWithDeserialize is Plonk4VerifierWithAccessToDNext {
j += 1;
}
- proof.opening_at_z_proof = PairingsBn254.new_g1(
+ 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(
+ proof.opening_at_z_omega_proof = PairingsBn254.new_g1_checked(
serialized_proof[j],
serialized_proof[j+1]
);
-
- j += 2;
- assert(j == serialized_proof.length);
}
}
diff --git a/contracts/contracts/Proxy.sol b/contracts/contracts/Proxy.sol
index 2136e183..81aaf985 100644
--- a/contracts/contracts/Proxy.sol
+++ b/contracts/contracts/Proxy.sol
@@ -2,6 +2,7 @@ pragma solidity ^0.5.0;
import "./Ownable.sol";
import "./Upgradeable.sol";
+import "./UpgradeableMaster.sol";
/// @title Proxy Contract
@@ -9,8 +10,8 @@ import "./Upgradeable.sol";
/// @author Matter Labs
contract Proxy is Upgradeable, UpgradeableMaster, Ownable {
- /// @notice Storage position of "target" (actual implementation address)
- bytes32 private constant targetPosition = keccak256("target");
+ /// @notice Storage position of "target" (actual implementation address: keccak256('eip1967.proxy.implementation') - 1)
+ bytes32 private constant targetPosition = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
/// @notice Contract constructor
/// @dev Calls Ownable contract constructor and initialize target
@@ -29,6 +30,11 @@ contract Proxy is Upgradeable, UpgradeableMaster, Ownable {
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 Actual implementation address
function getTarget() public view returns (address target) {
@@ -49,23 +55,21 @@ contract Proxy is Upgradeable, UpgradeableMaster, Ownable {
/// @notice Upgrades target
/// @param newTarget New target
- /// @param newTargetInitializationParameters New target initialization parameters
- function upgradeTarget(address newTarget, bytes calldata newTargetInitializationParameters) external {
+ /// @param newTargetUpgradeParameters New target upgrade parameters
+ function upgradeTarget(address newTarget, bytes calldata newTargetUpgradeParameters) external {
requireMaster(msg.sender);
setTarget(newTarget);
- (bool initializationSuccess, ) = getTarget().delegatecall(
- abi.encodeWithSignature("initialize(bytes)", newTargetInitializationParameters)
+ (bool upgradeSuccess, ) = getTarget().delegatecall(
+ abi.encodeWithSignature("upgrade(bytes)", newTargetUpgradeParameters)
);
- require(initializationSuccess, "ufu11"); // ufu11 - target initialization failed
+ 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() external payable {
- require(msg.data.length > 0, "pfb11"); // pfb11 - calldata must not be empty
-
address _target = getTarget();
assembly {
// The pointer to the free memory slot
@@ -101,8 +105,8 @@ contract Proxy is Upgradeable, UpgradeableMaster, Ownable {
/// UpgradeableMaster functions
/// @notice Notice period before activation preparation status of upgrade mode
- function upgradeNoticePeriod() external returns (uint) {
- (bool success, bytes memory result) = getTarget().delegatecall(abi.encodeWithSignature("upgradeNoticePeriod()"));
+ function getNoticePeriod() external returns (uint) {
+ (bool success, bytes memory result) = getTarget().delegatecall(abi.encodeWithSignature("getNoticePeriod()"));
require(success, "unp11"); // unp11 - upgradeNoticePeriod delegatecall failed
return abi.decode(result, (uint));
}
@@ -137,8 +141,8 @@ contract Proxy is Upgradeable, UpgradeableMaster, Ownable {
/// @notice Checks that contract is ready for upgrade
/// @return bool flag indicating that contract is ready for upgrade
- function readyForUpgrade() external returns (bool) {
- (bool success, bytes memory result) = getTarget().delegatecall(abi.encodeWithSignature("readyForUpgrade()"));
+ function isReadyForUpgrade() external returns (bool) {
+ (bool success, bytes memory result) = getTarget().delegatecall(abi.encodeWithSignature("isReadyForUpgrade()"));
require(success, "rfu11"); // rfu11 - readyForUpgrade delegatecall failed
return abi.decode(result, (bool));
}
diff --git a/contracts/contracts/ReentrancyGuard.sol b/contracts/contracts/ReentrancyGuard.sol
index 48faa87e..892f6ba6 100644
--- a/contracts/contracts/ReentrancyGuard.sol
+++ b/contracts/contracts/ReentrancyGuard.sol
@@ -20,7 +20,9 @@ pragma solidity ^0.5.0;
* metering changes introduced in the Istanbul hardfork.
*/
contract ReentrancyGuard {
- bool private _notEntered;
+ /// 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;
function initializeReentrancyGuard () internal {
// Storing an initial non-zero value makes deployment a bit more
@@ -29,7 +31,7 @@ contract ReentrancyGuard {
// 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.
- _notEntered = true;
+ assembly { sstore(LOCK_FLAG_ADDRESS, 1) }
}
/**
@@ -40,16 +42,19 @@ contract ReentrancyGuard {
* `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, "ReentrancyGuard: reentrant call");
+ require(notEntered, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
- _notEntered = false;
+ 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)
- _notEntered = true;
+ assembly { sstore(LOCK_FLAG_ADDRESS, 1) }
}
}
diff --git a/contracts/contracts/Storage.sol b/contracts/contracts/Storage.sol
index 8cf88666..fcb49492 100644
--- a/contracts/contracts/Storage.sol
+++ b/contracts/contracts/Storage.sol
@@ -39,7 +39,7 @@ contract Storage {
uint16 tokenId;
}
- /// @notice Verified but not executed withdrawals for addresses stored in here (key is pendingWithdrawal's index)
+ /// @notice Verified but not executed withdrawals for addresses stored in here (key is pendingWithdrawal's index in pending withdrawals queue)
mapping(uint32 => PendingWithdrawal) public pendingWithdrawals;
uint32 public firstPendingWithdrawalIndex;
uint32 public numberOfPendingWithdrawals;
@@ -81,7 +81,7 @@ contract Storage {
}
/// @notice Flag indicates that a user has exited certain token balance (per account id and tokenId)
- mapping(uint24 => mapping(uint16 => bool)) public exited;
+ mapping(uint32 => mapping(uint16 => bool)) public exited;
/// @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
@@ -117,7 +117,7 @@ contract Storage {
/// @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(uint(_address) | (_tokenId << 160)));
+ return bytes22((uint176(_address) | (uint176(_tokenId) << 160)));
}
/// @notice Gets value from balancesToWithdraw
diff --git a/contracts/contracts/UpgradeGatekeeper.sol b/contracts/contracts/UpgradeGatekeeper.sol
index 961f24da..2838679a 100644
--- a/contracts/contracts/UpgradeGatekeeper.sol
+++ b/contracts/contracts/UpgradeGatekeeper.sol
@@ -1,14 +1,16 @@
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
+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;
@@ -22,14 +24,17 @@ contract UpgradeGatekeeper is UpgradeEvents, Ownable {
UpgradeStatus public upgradeStatus;
- /// @notice Notice period activation timestamp (as seconds since unix epoch)
+ /// @notice Notice period finish timestamp (as seconds since unix epoch)
/// @dev Will be equal to zero in case of not active upgrade mode
- uint public noticePeriodActivationTime;
+ uint 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
+ uint public versionId;
+
/// @notice Contract which defines notice period duration and allows finish upgrade during preparation of it
UpgradeableMaster public mainContract;
@@ -38,6 +43,7 @@ contract UpgradeGatekeeper is UpgradeEvents, Ownable {
/// @dev Calls Ownable contract constructor
constructor(UpgradeableMaster _mainContract) Ownable(msg.sender) public {
mainContract = _mainContract;
+ versionId = 0;
}
/// @notice Adds a new upgradeable contract to the list of contracts managed by the gatekeeper
@@ -47,7 +53,7 @@ contract UpgradeGatekeeper is UpgradeEvents, Ownable {
require(upgradeStatus == UpgradeStatus.Idle, "apc11"); /// apc11 - upgradeable contract can't be added during upgrade
managedContracts.push(Upgradeable(addr));
- emit UpgradeableAdd(Upgradeable(addr));
+ emit NewUpgradable(versionId, addr);
}
/// @notice Starts upgrade (activates notice period)
@@ -57,11 +63,12 @@ contract UpgradeGatekeeper is UpgradeEvents, Ownable {
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
+ uint noticePeriod = mainContract.getNoticePeriod();
mainContract.upgradeNoticePeriodStarted();
upgradeStatus = UpgradeStatus.NoticePeriod;
- noticePeriodActivationTime = now;
+ noticePeriodFinishTimestamp = now.add(noticePeriod);
nextTargets = newTargets;
- emit NoticePeriodStart(newTargets);
+ emit NoticePeriodStart(versionId, newTargets, noticePeriod);
}
/// @notice Cancels upgrade
@@ -71,9 +78,9 @@ contract UpgradeGatekeeper is UpgradeEvents, Ownable {
mainContract.upgradeCanceled();
upgradeStatus = UpgradeStatus.Idle;
- noticePeriodActivationTime = 0;
+ noticePeriodFinishTimestamp = 0;
delete nextTargets;
- emit UpgradeCancel();
+ emit UpgradeCancel(versionId);
}
/// @notice Activates preparation status
@@ -82,10 +89,10 @@ contract UpgradeGatekeeper is UpgradeEvents, Ownable {
requireMaster(msg.sender);
require(upgradeStatus == UpgradeStatus.NoticePeriod, "ugp11"); // ugp11 - unable to activate preparation status in case of not active notice period status
- if (now >= noticePeriodActivationTime + mainContract.upgradeNoticePeriod()) {
+ if (now >= noticePeriodFinishTimestamp) {
upgradeStatus = UpgradeStatus.Preparation;
mainContract.upgradePreparationStarted();
- emit PreparationStart();
+ emit PreparationStart(versionId);
return true;
} else {
return false;
@@ -93,24 +100,25 @@ contract UpgradeGatekeeper is UpgradeEvents, Ownable {
}
/// @notice Finishes upgrade
- /// @param targetsInitializationParameters New targets initialization parameters per each upgradeable contract
- function finishUpgrade(bytes[] calldata targetsInitializationParameters) external {
+ /// @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(targetsInitializationParameters.length == managedContracts.length, "fpu12"); // fpu12 - number of new targets initialization parameters must be equal to the number of managed contracts
- require(mainContract.readyForUpgrade(), "fpu13"); // fpu13 - main contract is not ready for upgrade
+ 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, targetsInitializationParameters[i]);
- emit UpgradeComplete(managedContracts[i], newTarget);
+ managedContracts[i].upgradeTarget(newTarget, targetsUpgradeParameters[i]);
}
}
+ versionId++;
+ emit UpgradeComplete(versionId, nextTargets);
upgradeStatus = UpgradeStatus.Idle;
- noticePeriodActivationTime = 0;
+ noticePeriodFinishTimestamp = 0;
delete nextTargets;
}
diff --git a/contracts/contracts/Upgradeable.sol b/contracts/contracts/Upgradeable.sol
index 8321b06b..0834eb37 100644
--- a/contracts/contracts/Upgradeable.sol
+++ b/contracts/contracts/Upgradeable.sol
@@ -1,31 +1,6 @@
pragma solidity ^0.5.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 upgradeNoticePeriod() external returns (uint);
-
- /// @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 readyForUpgrade() external returns (bool);
-
-}
-
/// @title Interface of the upgradeable contract
/// @author Matter Labs
interface Upgradeable {
diff --git a/contracts/contracts/UpgradeableMaster.sol b/contracts/contracts/UpgradeableMaster.sol
new file mode 100644
index 00000000..4971098c
--- /dev/null
+++ b/contracts/contracts/UpgradeableMaster.sol
@@ -0,0 +1,27 @@
+pragma solidity ^0.5.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 (uint);
+
+ /// @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);
+
+}
diff --git a/contracts/contracts/Utils.sol b/contracts/contracts/Utils.sol
new file mode 100644
index 00000000..eae48558
--- /dev/null
+++ b/contracts/contracts/Utils.sol
@@ -0,0 +1,62 @@
+pragma solidity ^0.5.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 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) {
+ // TODO: Use constant from Config
+ uint256 ETH_WITHDRAWAL_GAS_LIMIT = 10000;
+
+ (bool callSuccess, ) = _to.call.gas(ETH_WITHDRAWAL_GAS_LIMIT).value(_amount)("");
+ return callSuccess;
+ }
+
+ /// @notice Recovers signer's address from ethereum signature for given message
+ /// @param _signature 65 bytes concatenated. R (32) + S (32) + V (1)
+ /// @param _message signed message.
+ /// @return address of the signer
+ function recoverAddressFromEthSignature(bytes memory _signature, bytes memory _message) internal pure returns (address) {
+ require(_signature.length == 65, "ves10"); // incorrect signature length
+
+ bytes32 signR;
+ bytes32 signS;
+ uint offset = 0;
+
+ (offset, signR) = Bytes.readBytes32(_signature, offset);
+ (offset, signS) = Bytes.readBytes32(_signature, offset);
+ uint8 signV = uint8(_signature[offset]);
+
+ return ecrecover(keccak256(_message), signV, signR, signS);
+ }
+}
diff --git a/contracts/contracts/Verifier.sol b/contracts/contracts/Verifier.sol
index 3f567fb9..76ac51c0 100644
--- a/contracts/contracts/Verifier.sol
+++ b/contracts/contracts/Verifier.sol
@@ -1,4 +1,4 @@
-pragma solidity >=0.5.0 <0.7.0;
+pragma solidity ^0.5.0;
import "./KeysWithPlonkVerifier.sol";
@@ -7,10 +7,14 @@ contract Verifier is KeysWithPlonkVerifier {
bool constant DUMMY_VERIFIER = false;
- constructor() public {}
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 {
+ revert("upgvf"); // it is the first version, upgrade is not supported, use initialize
+ }
function isBlockSizeSupported(uint32 _size) public pure returns (bool) {
if (DUMMY_VERIFIER) {
@@ -28,7 +32,7 @@ contract Verifier is KeysWithPlonkVerifier {
if (DUMMY_VERIFIER) {
uint oldGasValue = gasleft();
uint tmp;
- while (gasleft() > oldGasValue - 470000) {
+ while (gasleft() + 470000 > oldGasValue) {
tmp += 1;
}
return true;
@@ -36,26 +40,28 @@ contract Verifier is KeysWithPlonkVerifier {
uint256[] memory inputs = new uint256[](1);
uint256 mask = (~uint256(0)) >> 3;
inputs[0] = uint256(_commitment) & mask;
- Proof memory proof = deserialize_proof(1, inputs, _proof);
+ Proof memory proof = deserialize_proof(inputs, _proof);
VerificationKey memory vk = getVkBlock(_chunks);
+ require(vk.num_inputs == inputs.length);
return verify(proof, vk);
}
function verifyExitProof(
- bytes32 _root_hash,
- uint24 _accountId,
+ bytes32 _rootHash,
+ uint32 _accountId,
address _owner,
uint16 _tokenId,
uint128 _amount,
uint256[] calldata _proof
) external view returns (bool) {
- bytes32 commitment = sha256(abi.encodePacked(_root_hash, _accountId, _owner, _tokenId, _amount));
+ bytes32 commitment = sha256(abi.encodePacked(_rootHash, _accountId, _owner, _tokenId, _amount));
uint256[] memory inputs = new uint256[](1);
uint256 mask = (~uint256(0)) >> 3;
inputs[0] = uint256(commitment) & mask;
- Proof memory proof = deserialize_proof(1, inputs, _proof);
+ Proof memory proof = deserialize_proof(inputs, _proof);
VerificationKey memory vk = getVkExit();
+ require(vk.num_inputs == inputs.length);
return verify(proof, vk);
}
}
diff --git a/contracts/contracts/Franklin.sol b/contracts/contracts/ZkSync.sol
similarity index 79%
rename from contracts/contracts/Franklin.sol
rename to contracts/contracts/ZkSync.sol
index 9d62a7ef..56b976c3 100644
--- a/contracts/contracts/Franklin.sol
+++ b/contracts/contracts/ZkSync.sol
@@ -1,10 +1,10 @@
pragma solidity ^0.5.0;
-import "./IERC20.sol";
import "./ReentrancyGuard.sol";
import "./SafeMath.sol";
import "./SafeMathUInt128.sol";
import "./SafeCast.sol";
+import "./Utils.sol";
import "./Storage.sol";
import "./Config.sol";
@@ -13,16 +13,20 @@ import "./Events.sol";
import "./Bytes.sol";
import "./Operations.sol";
+import "./UpgradeableMaster.sol";
+
/// @title zkSync main contract
/// @author Matter Labs
-contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
+contract ZkSync is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard {
using SafeMath for uint256;
using SafeMathUInt128 for uint128;
+ bytes32 public constant EMPTY_STRING_KECCAK = bytes32(0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470);
+
// Upgrade functional
/// @notice Notice period before activation preparation status of upgrade mode
- function upgradeNoticePeriod() external returns (uint) {
+ function getNoticePeriod() external returns (uint) {
return UPGRADE_NOTICE_PERIOD;
}
@@ -51,27 +55,10 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
/// @notice Checks that contract is ready for upgrade
/// @return bool flag indicating that contract is ready for upgrade
- function readyForUpgrade() external returns (bool) {
+ function isReadyForUpgrade() external returns (bool) {
return !exodusMode && totalOpenPriorityRequests == 0;
}
- // // Migration
-
- // // Address of the new version of the contract to migrate accounts to
- // // Can be proposed by network governor
- // address public migrateTo;
-
- // // Migration deadline: after this ETH block number migration may happen with the contract
- // // entering exodus mode for all users who have not opted in for migration
- // uint32 public migrateByBlock;
-
- // // Flag for the new contract to indicate that the migration has been sealed
- // bool public migrationSealed;
-
- // mapping (uint32 => bool) tokenMigrated;
-
- constructor() public {}
-
/// @notice Franklin contract initialization. Can be external because Proxy contract intercepts illegal calls of this function.
/// @param initializationParameters Encoded representation of initialization parameters:
/// _governanceAddress The address of Governance contract
@@ -84,9 +71,8 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
(
address _governanceAddress,
address _verifierAddress,
- ,
bytes32 _genesisRoot
- ) = abi.decode(initializationParameters, (address, address, address, bytes32));
+ ) = abi.decode(initializationParameters, (address, address, bytes32));
verifier = Verifier(_verifierAddress);
governance = Governance(_governanceAddress);
@@ -94,33 +80,34 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
blocks[0].stateRoot = _genesisRoot;
}
+ /// @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 {
+ revert("upgzk"); // it is the first version, upgrade is not supported, use initialize
+ }
+
/// @notice Sends tokens
+ /// @dev NOTE: will revert if transfer call fails or rollup balance difference (before and after transfer) is bigger than _maxAmount
/// @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 sendERC20NoRevert(address _token, address _to, uint256 _amount) internal returns (bool) {
- (bool callSuccess, bytes memory callReturnValueEncoded) = _token.call.gas(ERC20_WITHDRAWAL_GAS_LIMIT)(
- abi.encodeWithSignature("transfer(address,uint256)", _to, _amount)
- );
- bool callReturnValue = abi.decode(callReturnValueEncoded, (bool));
- return callSuccess && callReturnValue;
- }
+ /// @param _maxAmount Maximum possible amount of tokens to transfer to this account
+ function withdrawERC20Guarded(IERC20 _token, address _to, uint128 _amount, uint128 _maxAmount) external returns (uint128 withdrawnAmount) {
+ require(msg.sender == address(this), "wtg10"); // wtg10 - can be called only from this contract as one "external" call (to revert all this function state changes if it is needed)
- /// @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(ETH_WITHDRAWAL_GAS_LIMIT).value(_amount)("");
- return callSuccess;
+ 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
+
+ return SafeCast.toUint128(balance_before.sub(balance_after));
}
/// @notice executes pending withdrawals
/// @param _n The number of withdrawals to complete starting from oldest
function completeWithdrawals(uint32 _n) external nonReentrant {
// TODO: when switched to multi validators model we need to add incentive mechanism to call complete.
- uint32 toProcess = minU32(_n, numberOfPendingWithdrawals);
+ uint32 toProcess = Utils.minU32(_n, numberOfPendingWithdrawals);
uint32 startIndex = firstPendingWithdrawalIndex;
numberOfPendingWithdrawals -= toProcess;
if (numberOfPendingWithdrawals == 0) {
@@ -143,11 +130,13 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
bool sent = false;
if (tokenId == 0) {
address payable toPayable = address(uint160(to));
- sent = sendETHNoRevert(toPayable, amount);
+ sent = Utils.sendETHNoRevert(toPayable, amount);
} else {
address tokenAddr = governance.tokenAddresses(tokenId);
- require(tokenAddr != address(0), "cwd11"); // unknown tokenId
- sent = sendERC20NoRevert(tokenAddr, to, amount);
+ // we can just check that call not reverts because it wants to withdraw all amount
+ (sent, ) = address(this).call.gas(ERC20_WITHDRAWAL_GAS_LIMIT)(
+ abi.encodeWithSignature("withdrawERC20Guarded(address,address,uint128,uint128)", tokenAddr, to, amount, amount)
+ );
}
if (!sent) {
balancesToWithdraw[packedBalanceKey].balanceToWithdraw += amount;
@@ -156,25 +145,15 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
}
}
- function minU32(uint32 a, uint32 b) internal pure returns (uint32) {
- return a < b ? a : b;
- }
-
- function minU64(uint64 a, uint64 b) internal pure returns (uint64) {
- return a < b ? a : b;
- }
-
/// @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 _requests number of requests to process
- function cancelOutstandingDepositsForExodusMode(uint64 _requests) external nonReentrant {
+ /// @param _n number of requests to process
+ function cancelOutstandingDepositsForExodusMode(uint64 _n) external nonReentrant {
require(exodusMode, "coe01"); // exodus mode not active
- require(_requests > 0, "coe02"); // provided zero number of requests
- require(totalOpenPriorityRequests > 0, "coe03"); // no priority requests left
- uint64 toProcess = minU64(totalOpenPriorityRequests, _requests);
- for (uint64 i = 0; i < toProcess; i++) {
- uint64 id = firstPriorityRequestId + i;
+ uint64 toProcess = Utils.minU64(totalOpenPriorityRequests, _n);
+ require(toProcess > 0, "coe02"); // no deposits to process
+ for (uint64 id = firstPriorityRequestId; id < firstPriorityRequestId + toProcess; id++) {
if (priorityRequests[id].opType == Operations.OpType.Deposit) {
Operations.Deposit memory op = Operations.readDepositPubdata(priorityRequests[id].pubData);
bytes22 packedBalanceKey = packAddressAndTokenId(op.owner, op.tokenId);
@@ -196,8 +175,8 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
/// @notice Withdraw ETH to Layer 1 - register withdrawal and transfer ether to sender
/// @param _amount Ether amount to withdraw
function withdrawETH(uint128 _amount) external nonReentrant {
- registerSingleWithdrawal(0, _amount);
- (bool success,) = msg.sender.call.value(_amount)("");
+ registerWithdrawal(0, _amount, msg.sender);
+ (bool success, ) = msg.sender.call.value(_amount)("");
require(success, "fwe11"); // ETH withdraw failed
}
@@ -205,14 +184,14 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
/// @param _token Token address
/// @param _amount Token amount
/// @param _franklinAddr Receiver Layer 2 address
- function depositERC20(IERC20 _token, uint128 _amount, address _franklinAddr) external nonReentrant {
+ function depositERC20(IERC20 _token, uint104 _amount, address _franklinAddr) external nonReentrant {
requireActive();
// Get token id by its address
uint16 tokenId = governance.validateTokenAddress(address(_token));
uint256 balance_before = _token.balanceOf(address(this));
- require(_token.transferFrom(msg.sender, address(this), _amount), "fd012"); // token transfer failed deposit
+ require(_token.transferFrom(msg.sender, address(this), uint128(_amount)), "fd012"); // token transfer failed deposit
uint256 balance_after = _token.balanceOf(address(this));
uint128 deposit_amount = SafeCast.toUint128(balance_after.sub(balance_before));
@@ -224,14 +203,16 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
/// @param _amount amount to withdraw
function withdrawERC20(IERC20 _token, uint128 _amount) external nonReentrant {
uint16 tokenId = governance.validateTokenAddress(address(_token));
- registerSingleWithdrawal(tokenId, _amount);
- require(_token.transfer(msg.sender, _amount), "fw011"); // token transfer failed withdraw
+ bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, tokenId);
+ uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
+ uint128 withdrawnAmount = this.withdrawERC20Guarded(_token, msg.sender, _amount, balance);
+ registerWithdrawal(tokenId, withdrawnAmount, msg.sender);
}
/// @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 fullExit (uint24 _accountId, address _token) external nonReentrant {
+ function fullExit (uint32 _accountId, address _token) external nonReentrant {
requireActive();
uint16 tokenId;
@@ -257,101 +238,199 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
balancesToWithdraw[packedBalanceKey].gasReserveValue = 0xff;
}
+ /// @notice Commit block - collect onchain operations, create its commitment, emit BlockCommit event
+ /// @param _blockNumber Block number
+ /// @param _feeAccount Account to collect fees
+ /// @param _newBlockInfo New state of the block. (first element is the account tree root hash, rest of the array is reserved for the future)
+ /// @param _publicData Operations pubdata
+ /// @param _ethWitness Data passed to ethereum outside pubdata of the circuit.
+ /// @param _ethWitnessSizes Amount of eth witness bytes for the corresponding operation.
+ function commitBlock(
+ uint32 _blockNumber,
+ uint32 _feeAccount,
+ bytes32[] calldata _newBlockInfo,
+ bytes calldata _publicData,
+ bytes calldata _ethWitness,
+ uint32[] calldata _ethWitnessSizes
+ ) external nonReentrant {
+ 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;
+
+ bytes32 withdrawalsDataHash = collectOnchainOps(_blockNumber, publicData, _ethWitness, _ethWitnessSizes);
+
+ uint64 nPriorityRequestProcessed = totalCommittedPriorityRequests - prevTotalCommittedPriorityRequests;
+
+ createCommittedBlock(_blockNumber, _feeAccount, _newBlockInfo[0], publicData, withdrawalsDataHash, nPriorityRequestProcessed);
+ totalBlocksCommitted++;
+
+ emit BlockCommit(_blockNumber);
+ }
+ }
+
+ /// @notice Block verification.
+ /// @notice Verify proof -> process onchain withdrawals (accrue balances from withdrawals) -> remove priority requests
+ /// @param _blockNumber Block number
+ /// @param _proof Block proof
+ /// @param _withdrawalsData Block withdrawals data
+ function verifyBlock(uint32 _blockNumber, uint256[] calldata _proof, bytes calldata _withdrawalsData)
+ external nonReentrant
+ {
+ requireActive();
+ require(_blockNumber == totalBlocksVerified + 1, "fvk11"); // only verify next block
+ governance.requireActiveValidator(msg.sender);
+
+ require(verifier.verifyBlockProof(_proof, blocks[_blockNumber].commitment, blocks[_blockNumber].chunks), "fvk13"); // proof verification failed
+
+ processOnchainWithdrawals(_withdrawalsData, blocks[_blockNumber].withdrawalsDataHash);
+
+ deleteRequests(
+ blocks[_blockNumber].priorityOperations
+ );
+
+ totalBlocksVerified += 1;
+
+ emit BlockVerification(_blockNumber);
+ }
+
+
+ /// @notice Reverts unverified blocks
+ /// @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.
+
+ uint32 blocksCommited = totalBlocksCommitted;
+ uint32 blocksToRevert = Utils.minU32(_maxBlocksToRevert, blocksCommited - totalBlocksVerified);
+ uint64 revertedPriorityRequests = 0;
+
+ for (uint32 i = totalBlocksCommitted - blocksToRevert + 1; i <= blocksCommited; i++) {
+ Block memory revertedBlock = blocks[i];
+ require(revertedBlock.committedAtBlock > 0, "frk11"); // block not found
+
+ revertedPriorityRequests += revertedBlock.priorityOperations;
+
+ delete blocks[i];
+ }
+
+ blocksCommited -= blocksToRevert;
+ totalBlocksCommitted -= blocksToRevert;
+ totalCommittedPriorityRequests -= revertedPriorityRequests;
+
+ emit BlocksRevert(totalBlocksVerified, blocksCommited);
+ }
+
+ /// @notice Checks that upgrade preparation is active and it is in lock period (period when contract will not add any new priority requests)
+ function upgradePreparationLockStatus() public returns (bool) {
+ return upgradePreparationActive && now < upgradePreparationActivationTime + UPGRADE_PREPARATION_LOCK_PERIOD;
+ }
+
+ /// @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 triggerExodusIfNeeded() 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 Franklin to root chain in case of exodus mode. User must provide proof that he owns funds
+ /// @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 exit(uint32 _accountId, uint16 _tokenId, uint128 _amount, uint256[] calldata _proof) external nonReentrant {
+ bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, _tokenId);
+ require(exodusMode, "fet11"); // must be in exodus mode
+ require(!exited[_accountId][_tokenId], "fet12"); // already exited
+ require(verifier.verifyExitProof(blocks[totalBlocksVerified].stateRoot, _accountId, msg.sender, _tokenId, _amount, _proof), "fet13"); // verification failed
+
+ uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
+ balancesToWithdraw[packedBalanceKey].balanceToWithdraw = balance.add(_amount);
+ exited[_accountId][_tokenId] = true;
+ }
+
+ function setAuthPubkeyHash(bytes calldata _pubkey_hash, uint32 _nonce) external nonReentrant {
+ require(_pubkey_hash.length == PUBKEY_HASH_BYTES, "ahf10"); // PubKeyHash should be 20 bytes.
+ require(authFacts[msg.sender][_nonce] == bytes32(0), "ahf11"); // auth fact for nonce should be empty
+
+ authFacts[msg.sender][_nonce] = keccak256(_pubkey_hash);
+
+ emit FactAuth(msg.sender, _nonce, _pubkey_hash);
+ }
+
/// @notice Register deposit request - pack pubdata, add priority request and emit OnchainDeposit event
- /// @param _token Token by id
+ /// @param _tokenId Token by id
/// @param _amount Token amount
/// @param _owner Receiver
function registerDeposit(
- uint16 _token,
+ uint16 _tokenId,
uint128 _amount,
address _owner
) internal {
- require(governance.isValidTokenId(_token), "rgd11"); // invalid token id
-
// Priority Queue request
Operations.Deposit memory op = Operations.Deposit({
accountId: 0, // unknown at this point
owner: _owner,
- tokenId: _token,
+ tokenId: _tokenId,
amount: _amount
- });
+ });
bytes memory pubData = Operations.writeDepositPubdata(op);
addPriorityRequest(Operations.OpType.Deposit, pubData);
emit OnchainDeposit(
msg.sender,
- _token,
+ _tokenId,
_amount,
_owner
);
}
- /// @notice Register withdrawal - update user balances and emit OnchainWithdrawal event
+ /// @notice Register withdrawal - update user balance and emit OnchainWithdrawal event
/// @param _token - token by id
/// @param _amount - token amount
- function registerSingleWithdrawal(uint16 _token, uint128 _amount) internal {
- bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, _token);
+ /// @param _to - address to withdraw to
+ function registerWithdrawal(uint16 _token, uint128 _amount, address payable _to) internal {
+ bytes22 packedBalanceKey = packAddressAndTokenId(_to, _token);
uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
balancesToWithdraw[packedBalanceKey].balanceToWithdraw = balance.sub(_amount);
emit OnchainWithdrawal(
- msg.sender,
+ _to,
_token,
_amount
);
}
- /// @notice Commit block - collect onchain operations, create its commitment, emit BlockCommit event
- /// @param _blockNumber Block number
- /// @param _feeAccount Account to collect fees
- /// @param _newRoot New tree root
- /// @param _publicData Operations pubdata
- /// @param _ethWitness Data passed to ethereum outside pubdata of the circuit.
- /// @param _ethWitnessSizes Amount of eth witness bytes for the corresponding operation.
- ///
- /// _blockNumber is not necessary but it may help to catch server-side errors.
- function commitBlock(
- uint32 _blockNumber,
- uint24 _feeAccount,
- bytes32 _newRoot,
- bytes calldata _publicData,
- bytes calldata _ethWitness,
- uint32[] calldata _ethWitnessSizes
- ) external nonReentrant {
- bytes memory publicData = _publicData;
-
- requireActive();
- require(_blockNumber == totalBlocksCommitted + 1, "fck11"); // only commit next block
- governance.requireActiveValidator(msg.sender);
- require(!isBlockCommitmentExpired(), "fck12"); // committed blocks had expired
- if(!triggerExodusIfNeeded()) {
- // Unpack onchain operations and store them.
- // Get priority operations number for this block.
- uint64 prevTotalCommittedPriorityRequests = totalCommittedPriorityRequests;
-
- bytes32 withdrawalsDataHash = collectOnchainOps(_blockNumber, publicData, _ethWitness, _ethWitnessSizes);
-
- uint64 nPriorityRequestProcessed = totalCommittedPriorityRequests - prevTotalCommittedPriorityRequests;
-
- createCommittedBlock(_blockNumber, _feeAccount, _newRoot, publicData, withdrawalsDataHash, nPriorityRequestProcessed);
- totalBlocksCommitted++;
-
- emit BlockCommit(_blockNumber);
- }
- }
-
/// @notice Store committed block structure to the storage.
/// @param _nCommittedPriorityRequests - number of priority requests in block
function createCommittedBlock(
uint32 _blockNumber,
- uint24 _feeAccount,
+ uint32 _feeAccount,
bytes32 _newRoot,
bytes memory _publicData,
bytes32 _withdrawalDataHash,
uint64 _nCommittedPriorityRequests
) internal {
- require(_publicData.length % 8 == 0, "cbb10"); // Public data size is not multiple of 8
+ require(_publicData.length % CHUNK_BYTES == 0, "cbb10"); // Public data size is not multiple of CHUNK_BYTES
- uint32 blockChunks = uint32(_publicData.length / 8);
+ uint32 blockChunks = uint32(_publicData.length / CHUNK_BYTES);
require(verifier.isBlockSizeSupported(blockChunks), "ccb11");
// Create block commitment for verification proof
@@ -373,6 +452,14 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
);
}
+ function emitDepositCommitEvent(uint32 _blockNumber, Operations.Deposit memory depositData) internal {
+ emit DepositCommit(_blockNumber, depositData.accountId, depositData.owner, depositData.tokenId, depositData.amount);
+ }
+
+ function emitFullExitCommitEvent(uint32 _blockNumber, Operations.FullExit memory fullExitData) internal {
+ emit FullExitCommit(_blockNumber, fullExitData.accountId, fullExitData.owner, fullExitData.tokenId, fullExitData.amount);
+ }
+
/// @notice Gets operations packed in bytes array. Unpacks it and stores onchain operations.
/// @param _blockNumber Franklin block number
/// @param _publicData Operations packed in bytes array
@@ -381,53 +468,48 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
/// Priority operations must be committed in the same order as they are in the priority queue.
function collectOnchainOps(uint32 _blockNumber, bytes memory _publicData, bytes memory _ethWitness, uint32[] memory _ethWitnessSizes)
internal returns (bytes32 withdrawalsDataHash) {
- require(_publicData.length % 8 == 0, "fcs11"); // pubdata length must be a multiple of 8 because each chunk is 8 bytes
+ require(_publicData.length % CHUNK_BYTES == 0, "fcs11"); // pubdata length must be a multiple of CHUNK_BYTES
uint64 currentPriorityRequestId = firstPriorityRequestId + totalCommittedPriorityRequests;
uint256 pubDataPtr = 0;
uint256 pubDataStartPtr = 0;
uint256 pubDataEndPtr = 0;
- assembly {
- pubDataStartPtr := add(_publicData, 0x20)
- pubDataPtr := pubDataStartPtr
- pubDataEndPtr := add(pubDataStartPtr, mload(_publicData))
- }
+
+ assembly { pubDataStartPtr := add(_publicData, 0x20) }
+ pubDataPtr = pubDataStartPtr;
+ pubDataEndPtr = pubDataStartPtr + _publicData.length;
uint64 ethWitnessOffset = 0;
uint16 processedOperationsRequiringEthWitness = 0;
- withdrawalsDataHash = keccak256("");
+ withdrawalsDataHash = EMPTY_STRING_KECCAK;
- while (pubDataPtr<pubDataEndPtr) {
- uint8 opType;
+ while (pubDataPtr < pubDataEndPtr) {
+ Operations.OpType opType;
// read operation type from public data (the first byte per each operation)
assembly {
opType := shr(0xf8, mload(pubDataPtr))
}
- // cheap transfer operation processing
- if (opType == uint8(Operations.OpType.Transfer)) {
+ // cheap operations processing
+ if (opType == Operations.OpType.Transfer) {
pubDataPtr += TRANSFER_BYTES;
+ } else if (opType == Operations.OpType.Noop) {
+ pubDataPtr += NOOP_BYTES;
+ } else if (opType == Operations.OpType.TransferToNew) {
+ pubDataPtr += TRANSFER_TO_NEW_BYTES;
} else {
// other operations processing
// calculation of public data offset
- uint256 pubdataOffset;
- assembly {
- // Number of pubdata bytes processed equal to current pubData memory pointer minus pubData memory start pointer
- pubdataOffset := sub(pubDataPtr, pubDataStartPtr)
- }
+ uint256 pubdataOffset = pubDataPtr - pubDataStartPtr;
- if (opType == uint8(Operations.OpType.Noop)) {
- pubDataPtr += NOOP_BYTES;
- } else if (opType == uint8(Operations.OpType.TransferToNew)) {
- pubDataPtr += TRANSFER_TO_NEW_BYTES;
- } else if (opType == uint8(Operations.OpType.Deposit)) {
+ if (opType == Operations.OpType.Deposit) {
bytes memory pubData = Bytes.slice(_publicData, pubdataOffset + 1, DEPOSIT_BYTES - 1);
Operations.Deposit memory depositData = Operations.readDepositPubdata(pubData);
- emit DepositCommit(_blockNumber, depositData.accountId, depositData.owner, depositData.tokenId, depositData.amount);
+ emitDepositCommitEvent(_blockNumber, depositData);
OnchainOperation memory onchainOp = OnchainOperation(
Operations.OpType.Deposit,
@@ -437,18 +519,18 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
currentPriorityRequestId++;
pubDataPtr += DEPOSIT_BYTES;
- } else if (opType == uint8(Operations.OpType.PartialExit)) {
+ } else if (opType == Operations.OpType.PartialExit) {
Operations.PartialExit memory data = Operations.readPartialExitPubdata(_publicData, pubdataOffset + 1);
bool addToPendingWithdrawalsQueue = true;
withdrawalsDataHash = keccak256(abi.encode(withdrawalsDataHash, addToPendingWithdrawalsQueue, data.owner, data.tokenId, data.amount));
pubDataPtr += PARTIAL_EXIT_BYTES;
- } else if (opType == uint8(Operations.OpType.FullExit)) {
+ } else if (opType == Operations.OpType.FullExit) {
bytes memory pubData = Bytes.slice(_publicData, pubdataOffset + 1, FULL_EXIT_BYTES - 1);
Operations.FullExit memory fullExitData = Operations.readFullExitPubdata(pubData);
- emit FullExitCommit(_blockNumber, fullExitData.accountId, fullExitData.owner, fullExitData.tokenId, fullExitData.amount);
+ emitFullExitCommitEvent(_blockNumber, fullExitData);
bool addToPendingWithdrawalsQueue = false;
withdrawalsDataHash = keccak256(abi.encode(withdrawalsDataHash, addToPendingWithdrawalsQueue, fullExitData.owner, fullExitData.tokenId, fullExitData.amount));
@@ -461,7 +543,7 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
currentPriorityRequestId++;
pubDataPtr += FULL_EXIT_BYTES;
- } else if (opType == uint8(Operations.OpType.ChangePubKey)) {
+ } else if (opType == Operations.OpType.ChangePubKey) {
require(processedOperationsRequiringEthWitness < _ethWitnessSizes.length, "fcs13"); // eth witness data malformed
Operations.ChangePubKey memory op = Operations.readChangePubKeyPubdata(_publicData, pubdataOffset + 1);
@@ -492,38 +574,23 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
totalCommittedPriorityRequests = currentPriorityRequestId - firstPriorityRequestId;
}
- /// @notice Recovers signer's address from ethereum signature for given message
- /// @param _signature 65 bytes concatenated. R (32) + S (32) + V (1)
- /// @param _message signed message.
- /// @return address of the signer
- function recoverAddressFromEthSignature(bytes memory _signature, bytes memory _message) internal pure returns (address) {
- require(_signature.length == 2*ETH_SIGN_RS_BYTES + 1, "ves10"); // incorrect signature length
- require(ETH_SIGN_RS_BYTES == 32, "ves11"); // expected ETH_SIGN_RS_BYTES to be 32 bytes
-
- bytes32 signR;
- bytes32 signS;
- uint offset = 0;
-
- (offset, signR) = Bytes.readBytes32(_signature, offset);
- (offset, signS) = Bytes.readBytes32(_signature, offset);
- uint8 signV = uint8(_signature[offset]);
-
- return ecrecover(keccak256(_message), signV, signR, signS);
- }
-
- function verifyChangePubkeySignature(bytes memory _signature, bytes20 _newPkHash, uint32 _nonce, address _ethAddress, uint24 _accountId) internal pure returns (bool) {
- require(_newPkHash.length == 20, "vpk11"); // unexpected hash length
-
+ /// @notice Checks that signature is valid for pubkey change message
+ /// @param _signature Signature
+ /// @param _newPkHash New pubkey hash
+ /// @param _nonce Nonce used for message
+ /// @param _ethAddress Account's ethereum address
+ /// @param _accountId Id of zkSync account
+ function verifyChangePubkeySignature(bytes memory _signature, bytes20 _newPkHash, uint32 _nonce, address _ethAddress, uint32 _accountId) internal pure returns (bool) {
bytes memory signedMessage = abi.encodePacked(
- "\x19Ethereum Signed Message:\n150",
+ "\x19Ethereum Signed Message:\n152",
"Register zkSync pubkey:\n\n",
Bytes.bytesToHexASCIIBytes(abi.encodePacked(_newPkHash)), "\n",
"nonce: 0x", Bytes.bytesToHexASCIIBytes(Bytes.toBytesFromUInt32(_nonce)), "\n",
- "account id: 0x", Bytes.bytesToHexASCIIBytes(Bytes.toBytesFromUInt24(_accountId)),
+ "account id: 0x", Bytes.bytesToHexASCIIBytes(Bytes.toBytesFromUInt32(_accountId)),
"\n\n",
"Only sign this message for a trusted client!"
);
- address recoveredAddress = recoverAddressFromEthSignature(_signature, signedMessage);
+ address recoveredAddress = Utils.recoverAddressFromEthSignature(_signature, signedMessage);
return recoveredAddress == _ethAddress;
}
@@ -536,7 +603,7 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
/// @return block commitment
function createBlockCommitment(
uint32 _blockNumber,
- uint24 _feeAccount,
+ uint32 _feeAccount,
bytes32 _oldRoot,
bytes32 _newRoot,
bytes memory _publicData
@@ -577,7 +644,10 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
}
}
- function commitNextPriorityOperation(OnchainOperation memory _onchainOp, uint64 _priorityRequestId) internal {
+ /// @notice Checks that operation is same as operation in priority queue
+ /// @param _onchainOp The operation
+ /// @param _priorityRequestId Operation's id in priority queue
+ function commitNextPriorityOperation(OnchainOperation memory _onchainOp, uint64 _priorityRequestId) internal view {
Operations.OpType priorReqType = priorityRequests[_priorityRequestId].opType;
bytes memory priorReqPubdata = priorityRequests[_priorityRequestId].pubData;
@@ -596,14 +666,13 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
/// @dev NOTICE: must process only withdrawals which hash matches with expectedWithdrawalsDataHash.
/// @param withdrawalsData Withdrawals data
/// @param expectedWithdrawalsDataHash Expected withdrawals data hash
- function processOnchainWithdrawals(bytes memory withdrawalsData, bytes32 expectedWithdrawalsDataHash)
- internal
- {
+ function processOnchainWithdrawals(bytes memory withdrawalsData, bytes32 expectedWithdrawalsDataHash) internal {
require(withdrawalsData.length % ONCHAIN_WITHDRAWAL_BYTES == 0, "pow11"); // pow11 - withdrawalData length is not multiple of ONCHAIN_WITHDRAWAL_BYTES
- bytes32 withdrawalsDataHash = keccak256("");
+ bytes32 withdrawalsDataHash = EMPTY_STRING_KECCAK;
uint offset = 0;
+ uint32 localNumberOfPendingWithdrawals = numberOfPendingWithdrawals;
while (offset < withdrawalsData.length) {
(bool addToPendingWithdrawalsQueue, address _to, uint16 _tokenId, uint128 _amount) = Operations.readWithdrawalData(withdrawalsData, offset);
bytes22 packedBalanceKey = packAddressAndTokenId(_to, _tokenId);
@@ -612,41 +681,17 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
balancesToWithdraw[packedBalanceKey].balanceToWithdraw = balance.add(_amount);
if (addToPendingWithdrawalsQueue) {
- pendingWithdrawals[firstPendingWithdrawalIndex + numberOfPendingWithdrawals] = PendingWithdrawal(_to, _tokenId);
- numberOfPendingWithdrawals++;
+ pendingWithdrawals[firstPendingWithdrawalIndex + localNumberOfPendingWithdrawals] = PendingWithdrawal(_to, _tokenId);
+ localNumberOfPendingWithdrawals++;
}
withdrawalsDataHash = keccak256(abi.encode(withdrawalsDataHash, addToPendingWithdrawalsQueue, _to, _tokenId, _amount));
offset += ONCHAIN_WITHDRAWAL_BYTES;
}
+ numberOfPendingWithdrawals = localNumberOfPendingWithdrawals;
require(withdrawalsDataHash == expectedWithdrawalsDataHash, "pow12"); // pow12 - withdrawals data hash not matches with expected value
}
- /// @notice Block verification.
- /// @notice Verify proof -> process onchain withdrawals (accrue balances from withdrawals) -> remove priority requests
- /// @param _blockNumber Block number
- /// @param _proof Block proof
- /// @param _withdrawalsData Block withdrawals data
- function verifyBlock(uint32 _blockNumber, uint256[] calldata _proof, bytes calldata _withdrawalsData)
- external nonReentrant
- {
- requireActive();
- require(_blockNumber == totalBlocksVerified + 1, "fvk11"); // only verify next block
- governance.requireActiveValidator(msg.sender);
-
- require(verifier.verifyBlockProof(_proof, blocks[_blockNumber].commitment, blocks[_blockNumber].chunks), "fvk13"); // proof verification failed
-
- processOnchainWithdrawals(_withdrawalsData, blocks[_blockNumber].withdrawalsDataHash);
-
- deleteRequests(
- blocks[_blockNumber].priorityOperations
- );
-
- totalBlocksVerified += 1;
-
- emit BlockVerification(_blockNumber);
- }
-
/// @notice Checks whether oldest unverified block has expired
/// @return bool flag that indicates whether oldest unverified block has expired
function isBlockCommitmentExpired() internal view returns (bool) {
@@ -657,84 +702,11 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
);
}
- /// @notice Reverts unverified blocks
- /// @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.
-
- uint32 blocksCommited = totalBlocksCommitted;
- uint32 blocksToRevert = minU32(_maxBlocksToRevert, blocksCommited - totalBlocksVerified);
- uint64 revertedPriorityRequests = 0;
-
- for (uint32 i = totalBlocksCommitted - blocksToRevert + 1; i <= blocksCommited; i++) {
- Block memory revertedBlock = blocks[i];
- require(revertedBlock.committedAtBlock > 0, "frk11"); // block not found
-
- revertedPriorityRequests += revertedBlock.priorityOperations;
-
- delete blocks[i];
- }
-
- blocksCommited -= blocksToRevert;
- totalBlocksCommitted -= blocksToRevert;
- totalCommittedPriorityRequests -= revertedPriorityRequests;
-
- emit BlocksRevert(totalBlocksVerified, blocksCommited);
- }
-
- /// @notice Checks that upgrade preparation is active and it is in lock period (period when contract will not add any new priority requests)
- function upgradePreparationLockStatus() public returns (bool) {
- return upgradePreparationActive && now < upgradePreparationActivationTime + UPGRADE_PREPARATION_LOCK_PERIOD;
- }
-
/// @notice Checks that current state not is exodus mode
function requireActive() internal view {
require(!exodusMode, "fre11"); // exodus mode activated
}
- /// @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 triggerExodusIfNeeded() 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 Franklin to root chain in case of exodus mode. User must provide proof that he owns funds
- /// @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 exit(uint24 _accountId, uint16 _tokenId, uint128 _amount, uint256[] calldata _proof) external nonReentrant {
- bytes22 packedBalanceKey = packAddressAndTokenId(msg.sender, _tokenId);
- require(exodusMode, "fet11"); // must be in exodus mode
- require(!exited[_accountId][_tokenId], "fet12"); // already exited
- require(verifier.verifyExitProof(blocks[totalBlocksVerified].stateRoot, _accountId, msg.sender, _tokenId, _amount, _proof), "fet13"); // verification failed
-
- uint128 balance = balancesToWithdraw[packedBalanceKey].balanceToWithdraw;
- balancesToWithdraw[packedBalanceKey].balanceToWithdraw = balance.add(_amount);
- exited[_accountId][_tokenId] = true;
- }
-
- function setAuthPubkeyHash(bytes calldata _pubkey_hash, uint32 _nonce) external nonReentrant {
- require(_pubkey_hash.length == PUBKEY_HASH_BYTES, "ahf10"); // PubKeyHash should be 20 bytes.
- require(authFacts[msg.sender][_nonce] == bytes32(0), "ahf11"); // auth fact for nonce should be empty
-
- authFacts[msg.sender][_nonce] = keccak256(_pubkey_hash);
-
- emit FactAuth(msg.sender, _nonce, _pubkey_hash);
- }
-
// Priority queue
/// @notice Saves priority request in storage
@@ -750,7 +722,7 @@ contract Franklin is UpgradeableMaster, Storage, Config, Events, ReentrancyGuard
// Expiration block is: current block number + priority expiration delta
uint256 expirationBlock = block.number + PRIORITY_EXPIRATION;
- uint64 nextPriorityRequestId = firstPriorityRequestId + totalOpenPriorityRequests;
+ uint64 nextPriorityRequestId = firstPriorityRequestId + totalOpenPriorityRequests;
priorityRequests[nextPriorityRequestId] = PriorityOperation({
opType: _opType,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment