Skip to content

Commit

Permalink
batch rollupnc#1 proven integrity on L2
Browse files Browse the repository at this point in the history
  • Loading branch information
jp4g committed Jul 18, 2022
1 parent 7a21032 commit 82aa354
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 222 deletions.
16 changes: 9 additions & 7 deletions contracts/RollupNC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ contract RollupNC {

event RegisteredToken(uint256 tokenType, address tokenContract);
event RequestDeposit(uint256[2] pubkey, uint256 amount, uint256 tokenType);
event ConfirmDeposit(uint256 oldRoot, uint256 newRoot, uint8 numAdded);
event UpdatedState(uint256 currentRoot, uint256 oldRoot, uint256 txRoot);
event Withdraw(uint256[9] accountInfo, address recipient);

Expand All @@ -60,7 +61,7 @@ contract RollupNC {
uint256 _zero,
uint256[] memory _zeroCache
) {
require(_depth[0] == _zeroCache.length, "Param size mismatch");
require(_depth[0] + 1 == _zeroCache.length, "Param size mismatch");
// assign contract references
usv = IVerifier(_addresses[0]);
wsv = IVerifier(_addresses[1]);
Expand All @@ -71,7 +72,7 @@ contract RollupNC {
txDepth = _depth[1];
ZERO = _zero;
zeroCache = _zeroCache;
currentRoot = zeroCache[balDepth - 1];
currentRoot = zeroCache[balDepth];
coordinator = msg.sender;
}

Expand Down Expand Up @@ -103,7 +104,6 @@ contract RollupNC {
uint256 depositHash = PoseidonT6.poseidon(
[pubkey[0], pubkey[1], amount, uint256(0), tokenType]
);

pendingDeposits[depositQueueEnd] = depositHash;
depositQueueEnd++;
depositQueueSize++;
Expand Down Expand Up @@ -144,13 +144,16 @@ contract RollupNC {
"specified subtree is not empty"
);
// insert multiple leafs (insert subtree) by computing new root
uint256 oldRoot = currentRoot;
currentRoot = getRootFromProof(
pendingDeposits[depositQueueStart],
subtreePosition,
subtreeProof
);
removeDeposit(true);
depositQueueSize -= uint8(2**depositSubtreeHeight);
uint8 numAdded = uint8(2**depositSubtreeHeight);
depositQueueSize -= numAdded;
emit ConfirmDeposit(oldRoot, currentRoot, numAdded);
return currentRoot;
}

Expand Down Expand Up @@ -291,8 +294,7 @@ contract RollupNC {
// 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;
if ((depositQueueSize & (uint256(1) << i)) > 0) _heights[_i++] = i;
}
// store leaves
for (uint256 i = 0; i < num; i++)
Expand All @@ -312,7 +314,7 @@ contract RollupNC {
uint256 _leaf,
uint256[] memory _position,
uint256[] memory _proof
) public pure returns (uint256) {
) public view returns (uint256) {
uint256 hash = _leaf;
for (uint8 i = 0; i < _proof.length; i++) {
if (_position[i] == 0)
Expand Down
142 changes: 142 additions & 0 deletions test/0.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
const { deployments, ethers } = require('hardhat')
const { solidity } = require("ethereum-waffle");
const { buildEddsa, buildPoseidon } = require('circomlibjs')
const chai = require("chai").use(solidity)
const { initializeContracts, generateAccounts, L2Account } = require('./utils')
const { IncrementalMerkleTree } = require('@zk-kit/incremental-merkle-tree');
const { expect } = require('chai');

describe("Test rollup deposits", async () => {
let eddsa, poseidon, F;
let signers, accounts;
let rollup;
let zeroCache;
before(async () => {

// initial
signers = await ethers.getSigners();
poseidon = await buildPoseidon();
eddsa = await buildEddsa();
F = poseidon.F;

// generate zero cache
const depths = [4, 2];
zeroCache = [BigInt(0)];
for (let i = 1; i <= depths[0]; i++) {
const root = zeroCache[i - 1];
const internalNode = poseidon([root, root])
zeroCache.push(F.toObject(internalNode));
}
rollup = await initializeContracts(zeroCache);
// set accounts
accounts = await generateAccounts(poseidon, eddsa);
})
describe('Deposits', async () => {
describe('Batch #1', async () => {
it('Deposit #0 (0 ADDRESS)', async () => {
// check deposit fn execution logic
const tx = rollup.deposit([0, 0], 0, 0, { from: accounts.coordinator.L1.address });
await expect(tx).to.emit(rollup, 'RequestDeposit').withArgs([0, 0], 0, 0);
// check deposit queue
const expectedRoot = L2Account.emptyRoot(poseidon);
const depositRoot = F.toObject((await rollup.describeDeposits())._leaves[0]);
expect(expectedRoot).to.be.equal(depositRoot);
})
it('Deposit #1 (COORDINATOR ADDRESS)', async () => {
// check deposit fn execution logic
const l2Pubkey = accounts.coordinator.L2.pubkey.map(point => F.toObject(point));
const tx = rollup.deposit(l2Pubkey, 0, 0, { from: accounts.coordinator.L1.address });
await expect(tx).to.emit(rollup, 'RequestDeposit').withArgs(l2Pubkey, 0, 0);
// check deposit queue
const data = [...l2Pubkey, 0, 0, 0];
const leafRoot = F.toObject(poseidon(data));
const sibling = L2Account.emptyRoot(poseidon);
const expectedRoot = F.toObject(poseidon([sibling, leafRoot]));
const depositRoot = F.toObject((await rollup.describeDeposits())._leaves[0]);
expect(expectedRoot).to.be.equal(depositRoot);
})
it('Deposit #2 (Alice)', async () => {
// check deposit fn execution logic
const l2Pubkey = accounts.alice.L2.pubkey.map(point => F.toObject(point));
const tx = rollup.connect(accounts.alice.L1).deposit(l2Pubkey, 20, 1, { value: 20 });
await expect(tx).to.emit(rollup, 'RequestDeposit').withArgs(l2Pubkey, 20, 1);
accounts.alice.L2.credit(BigInt(20));
// check deposit queue
const expectedRoot = accounts.alice.L2.root;
const depositRoot = F.toObject((await rollup.describeDeposits())._leaves[1]);
expect(expectedRoot).to.be.equal(depositRoot);
})
it('Deposit #3 (Bob)', async () => {
// check deposit fn execution logic
const l2Pubkey = accounts.bob.L2.pubkey.map(point => F.toObject(point));
const tx = rollup.connect(accounts.bob.L1).deposit(l2Pubkey, 15, 1, { value: 15 });
await expect(tx).to.emit(rollup, 'RequestDeposit').withArgs(l2Pubkey, 15, 1);
accounts.bob.L2.credit(BigInt(15));
// check deposit queue
const coordinatorPubkey = accounts.coordinator.L2.pubkey.map(point => F.toObject(point));
const coordinatorLeaf = F.toObject(poseidon([...coordinatorPubkey, 0, 0, 0]));
const sibling = F.toObject(poseidon([L2Account.emptyRoot(poseidon), coordinatorLeaf]))
const current = F.toObject(poseidon([accounts.alice.L2.root, accounts.bob.L2.root]));
const expectedRoot = F.toObject(poseidon([sibling, current]));
const depositRoot = F.toObject((await rollup.describeDeposits())._leaves[0]);
expect(expectedRoot).to.be.equal(depositRoot);
})
it('Process Batch #1 (4 new balance leaves)', async () => {
// construct expected values
const emptyLeaf = L2Account.emptyRoot(poseidon)
const coordinatorPubkey = accounts.coordinator.L2.pubkey.map(point => F.toObject(point));
const coordinatorLeaf = F.toObject(poseidon([...coordinatorPubkey, 0, 0, 0]));
const expectTree = new IncrementalMerkleTree(poseidon, 4, 0);
expectTree.insert(emptyLeaf);
expectTree.insert(coordinatorLeaf);
expectTree.insert(accounts.alice.L2.root);
expectTree.insert(accounts.bob.L2.root);
const expected = {
oldRoot: zeroCache[zeroCache.length - 1],
newRoot: F.toObject(expectTree.root)
}
// construct transaction
const position = [0, 0];
const proof = [zeroCache[2], zeroCache[3]];
const tx = rollup.connect(accounts.coordinator.L1).processDeposits(2, position, proof);
// verify execution integrity
await expect(tx).to.emit(rollup, "ConfirmDeposit").withArgs(
expected.oldRoot,
expected.newRoot,
4
);
})

})
describe('Batch #2', async () => {
xit('Deposit #4 (Charlie)', async () => {
// check deposit fn execution logic
const l2Pubkey = accounts.charlie.L2.pubkey.map(point => F.toObject(point));
const tx = rollup.connect(accounts.charlie.L1).deposit(l2Pubkey, 500, 1, { value: 500 });
await expect(tx).to.emit(rollup, 'RequestDeposit').withArgs(l2Pubkey, 500, 1);
accounts.charlie.L2.credit(BigInt(500));
// check deposit queue
const expectedRoot = accounts.charlie.L2.root;
const depositRoot = F.toObject((await rollup.describeDeposits())._leaves[0]);
expect(expectedRoot).to.be.equal(depositRoot);
})
xit('Deposit #5 (David)', async () => {
// check deposit fn execution logic
const l2Pubkey = accounts.david.L2.pubkey.map(point => F.toObject(point));
const tx = rollup.connect(accounts.david.L1).deposit(l2Pubkey, 499, 1, { value: 500 });
await expect(tx).to.emit(rollup, 'RequestDeposit').withArgs(l2Pubkey, 499, 1);
accounts.david.L2.credit(BigInt(499));
// check deposit queue
const expectedRoot = F.toObject(poseidon[
accounts.charlie.L2.root,
accounts.david.L2.root
])
const depositRoot = F.toObject((await rollup.describeDeposits())._leaves[0]);
expect(expectedRoot).to.be.equal(depositRoot);
})
xit('Process Batch #2 (2 new balance leaves', async () => {

})
})
})
})
Loading

0 comments on commit 82aa354

Please sign in to comment.