Skip to content

Commit

Permalink
deposits working
Browse files Browse the repository at this point in the history
  • Loading branch information
jp4g committed Jul 18, 2022
1 parent 54d483d commit 7a21032
Show file tree
Hide file tree
Showing 12 changed files with 6,010 additions and 4,127 deletions.
264 changes: 163 additions & 101 deletions contracts/Rollup.sol → contracts/RollupNC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,29 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// import './interfaces/IRollup.sol';
import "./interfaces/ITokenRegistry.sol";
import "./interfaces/IVerifier.sol";
import "./libraries/IncrementalBinaryTree.sol";
import "./libraries/PackedPairings.sol";
// import "./libraries/PackedPairings.sol";
import "./libraries/Poseidon.sol";
import "hardhat/console.sol";

/// @title implementation of Non-custodial rollup
contract RollupNC {
using IncrementalBinaryTree for IncrementalTreeData;

PoseidonT3 public poseidonT3;
PoseidonT6 public poseidonT6;
IVerifier public usv; // update state verifier
IVerifier public wsv; // withdraw signature verifier
IncrementalBinaryTree public merkleTree; // incremental binary tree of account balances
// IncrementalBinaryTree public state; // incremental binary tree of account balances
mapping(uint256 => uint256) public pendingDeposits;
ITokenRegistry public registry;

uint256 public constant DEPTH;
uint256 public constant ZERO;
uint256[] constant ZERO_CACHE;
uint256 public balDepth;
uint256 public txDepth;
uint256 public ZERO;
uint256[] public zeroCache;

uint256 public currentRoot;
address public coordinator;
uint256[] public pendingDeposits;
uint256 public queueNumber;
uint256 public depositSubtreeHeight;
uint256 public depositQueueStart;
uint256 public depositQueueEnd;
uint8 public depositQueueSize;
uint8 public depositSubtreeHeight;
uint256 public updateNumber;

// (queueNumber => [pubkey_x, pubkey_y, balance, nonce, token_type])
Expand All @@ -40,43 +39,42 @@ contract RollupNC {
event UpdatedState(uint256 currentRoot, uint256 oldRoot, uint256 txRoot);
event Withdraw(uint256[9] accountInfo, address recipient);

modifier onlyCoordinator() {
assert(msg.sender == coordinator);
_;
}

/// @dev construct a new non-custodial on-chain rollup
/// @param _addresses: array of addresses used in the rollup contract
/// [0]: poseidonT3 library contract
/// [1]: poseidonT6 library contract
/// [2]: Update State Verifier contract
/// [3]: Withdrawal Signature Verifier contract
/// [4]: incremental binary tree library contract
/// [5]: ollup token registry contract
/// @param _depth: depth of account/ transaction balance merkle tree
/// [0]: Update State Verifier contract
/// [1]: Withdrawal Signature Verifier contract
/// [2]: Rollup token registry contract
/// @param _depth: depth of trees
/// [0]: Balance tree max depth
/// [1]: Tx tree max depth
/// @param _zero: the value to use for an empty leaf in a merkle tree
/// @param _zeroCache: array of precomputed roots for zero's at different heights
constructor(
address[6] memory _addresses,
uint256 _depth,
address[3] memory _addresses,
uint256[2] memory _depth,
uint256 _zero,
uint256[_zero] memory _zeroCache
) public {
uint256[] memory _zeroCache
) {
require(_depth[0] == _zeroCache.length, "Param size mismatch");
// assign contract references
poseidonT3 = PoseidonT3(_addresses[0]);
poseidonT6 = PoseidonT6(_addresses[1]);
usv = IVerifier(_addresses[2]);
wsv = IVerifier(_addresses[3]);
merkleTree = IncrementalBinaryTree(_addresses[4]);
registry = ITokenRegistry(_addresses[5]);
usv = IVerifier(_addresses[0]);
wsv = IVerifier(_addresses[1]);
registry = ITokenRegistry(_addresses[2]);

// assign primative variables
DEPTH = _depth;
balDepth = _depth[0];
txDepth = _depth[1];
ZERO = _zero;
ZERO_CACHE = _zeroCache;
currentRoot = ZERO_CACHE[DEPTH];
zeroCache = _zeroCache;
currentRoot = zeroCache[balDepth - 1];
coordinator = msg.sender;
}

modifier onlyCoordinator() {
assert(msg.sender == coordinator);
_;
}

// function updateState(
// uint256[2] memory a,
// uint256[2][2] memory b,
Expand All @@ -99,59 +97,35 @@ contract RollupNC {
uint256 amount,
uint256 tokenType
) public payable {
// handle token types
if (tokenType == 0) {
require(
msg.sender == coordinator,
"tokenType 0 is reserved for coordinator"
);
require(
amount == 0 && msg.value == 0,
"tokenType 0 does not have real value"
);
} else if (tokenType == 1) {
require(
msg.value > 0 && msg.value >= amount,
"msg.value must at least equal stated amount in wei"
);
} else if (tokenType > 1) {
require(amount > 0, "token deposit must be greater than 0");
address tokenAddress = registry.registeredTokens(tokenType);
require(
IERC20(tokenAddress).transferFrom(
msg.sender,
address(this),
amount
),
"token transfer not approved"
);
}

uint256[] memory depositArray = new uint256[](5);
depositArray[0] = pubkey[0];
depositArray[1] = pubkey[1];
depositArray[2] = amount;
depositArray[3] = 0;
depositArray[4] = tokenType;
// Ensure token can be transferred
checkToken(amount, tokenType);
// Store deposit leaf
uint256 depositHash = PoseidonT6.poseidon(
[pubkey[0], pubkey[1], amount, uint256(0), tokenType]
);

uint256 depositHash = poseidonT6(depositArray);
pendingDeposits.push(depositHash);
emit RequestDeposit(pubkey, amount, tokenType);
queueNumber++;
uint256 tmpDepositSubtreeHeight = 0;
uint256 tmp = queueNumber;
pendingDeposits[depositQueueEnd] = depositHash;
depositQueueEnd++;
depositQueueSize++;
// Generate
uint8 tmpDepositSubtreeHeight = 0;
uint256 tmp = depositQueueSize;
while (tmp % 2 == 0) {
pendingDeposits[pendingDeposits.length - 2] = poseidonT3([
pendingDeposits[pendingDeposits.length - 2],
pendingDeposits[pendingDeposits.length - 1]
]);
// removeDeposit(pendingDeposits.length - 1);
// while leafs can be hashed into merkle tree, generate a higher order internal node
pendingDeposits[depositQueueEnd - 2] = PoseidonT3.poseidon(
[
pendingDeposits[depositQueueEnd - 2],
pendingDeposits[depositQueueEnd - 1]
]
);
removeDeposit(false);
tmp = tmp / 2;
tmpDepositSubtreeHeight++;
}
if (tmpDepositSubtreeHeight > depositSubtreeHeight) {
depositSubtreeHeight = tmpDepositSubtreeHeight;
}
emit RequestDeposit(pubkey, amount, tokenType);
}

// // coordinator adds certain number of deposits to balance tree
Expand All @@ -162,23 +136,21 @@ contract RollupNC {
uint256[] memory subtreePosition,
uint256[] memory subtreeProof
) public onlyCoordinator returns (uint256) {
uint256 emptySubtreeRoot = mimcMerkle.zeroCache(subtreeDepth); //empty subtree of height 2
// ensure subtree specified is empty
uint256 emptyRoot = zeroCache[subtreeDepth];
require(
currentRoot ==
mimcMerkle.getRootFromProof(
emptySubtreeRoot,
subtreePosition,
subtreeProof
),
getRootFromProof(emptyRoot, subtreePosition, subtreeProof),
"specified subtree is not empty"
);
currentRoot = mimcMerkle.getRootFromProof(
pendingDeposits[0],
// insert multiple leafs (insert subtree) by computing new root
currentRoot = getRootFromProof(
pendingDeposits[depositQueueStart],
subtreePosition,
subtreeProof
);
removeDeposit(0);
queueNumber = queueNumber - 2**depositSubtreeHeight;
removeDeposit(true);
depositQueueSize -= uint8(2**depositSubtreeHeight);
return currentRoot;
}

Expand Down Expand Up @@ -248,15 +220,105 @@ contract RollupNC {
emit RegisteredToken(registry.registryIndex(), _token);
}

// helper functions
function removeDeposit(uint256 index) internal returns (uint256[] memory) {
require(index < pendingDeposits.length, "index is out of bounds");
/// INTERNAL FUNCTIONS ///

/**
* Ensures a token can be deposited by the message sender
* @dev throws error if checks are failed
* @param _amount - the amount of tokens attempting to transfer
* @param _type - the token's registry index
*/
function checkToken(uint256 _amount, uint256 _type) internal {
if (_type == 0) {
require(
msg.sender == coordinator,
"tokenType 0 is reserved for coordinator"
);
require(
_amount == 0 && msg.value == 0,
"tokenType 0 does not have real value"
);
} else if (_type == 1) {
require(
msg.value > 0 && msg.value >= _amount,
"msg.value must at least equal stated amount in wei"
);
} else if (_type > 1) {
require(_amount > 0, "token deposit must be greater than 0");
address tokenAddress = registry.registry(_type);
require(
IERC20(tokenAddress).transferFrom(
msg.sender,
address(this),
_amount
),
"token transfer not approved"
);
}
}

/**
* Remove a deposit in either a FIFO or LIFO manner
* @param _fifo - if true, remove the oldest element (tallest subtree). else remove the newest element
*/
function removeDeposit(bool _fifo) internal {
if (_fifo) {
// remove tallest perfect subtree
delete pendingDeposits[depositQueueStart];
depositQueueStart += 1;
} else {
// remove last inserted entry
delete pendingDeposits[depositQueueEnd - 1];
depositQueueEnd -= 1;
}
}

/**
* Describe the subtrees for balance tree build in deposit queue
*
* @return _leaves - the node value
* @return _heights - the node height in the tree
*/
function describeDeposits()
public
view
returns (uint256[] memory _leaves, uint256[] memory _heights)
{
// create return variables
uint256 num = depositQueueEnd - depositQueueStart; // number of entries in deposit queue
_leaves = new uint256[](num);
_heights = new uint256[](num);
// compute height
uint8 _i = 0; // track insert index, should always be safe
for (uint256 i = 1; i <= depositSubtreeHeight; i++) {
if ((depositQueueSize & (uint256(1) << i)) > 0)
_heights[_i++] = i;
}
// store leaves
for (uint256 i = 0; i < num; i++)
_leaves[i] = pendingDeposits[depositQueueStart + i];
}

for (uint256 i = index; i < pendingDeposits.length - 1; i++) {
pendingDeposits[i] = pendingDeposits[i + 1];
/**
* Generate a merkle root from a given proof
* @notice uses poseidon hash function
* @dev does not prove membership - returned root must be compared to stored state
*
* @param _leaf - the item being checked for membership
* @param _position - the path of the leaf in the tree
* @param _proof - the sibling nodes at any given height in the tree
*/
function getRootFromProof(
uint256 _leaf,
uint256[] memory _position,
uint256[] memory _proof
) public pure returns (uint256) {
uint256 hash = _leaf;
for (uint8 i = 0; i < _proof.length; i++) {
if (_position[i] == 0)
hash = PoseidonT3.poseidon([hash, _proof[i]]);
else hash = PoseidonT3.poseidon([_proof[i], hash]);
}
delete pendingDeposits[pendingDeposits.length - 1];
pendingDeposits.length--;
return pendingDeposits;
return hash;
}
}
5 changes: 2 additions & 3 deletions contracts/TokenRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import "./interfaces/ITokenRegistry.sol";
contract TokenRegistry is ITokenRegistry {
/**
* Instantiate the RollupNC's ERC20 Token Registry
* @param _coordinator - the address of the permissioned off-chain roll-up sequencer
*/
constructor(address _coordinator) {
coordinator = _coordinator;
constructor() {
coordinator = msg.sender;
registryIndex = 1; // ETH
}

Expand Down
Loading

0 comments on commit 7a21032

Please sign in to comment.