Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Neighbor root histories #15

Merged
merged 22 commits into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 38 additions & 19 deletions contracts/anchors/bridged/AnchorPoseidon2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,20 @@ abstract contract AnchorPoseidon2 is MerkleTreePoseidon, ReentrancyGuard {
mapping(uint256 => bool) public edgeExistsForChain;
Edge[] public edgeList;

// map to store chainID => (rootIndex => root) to track neighbor histories
mapping(uint256 => mapping(uint32 => bytes32)) public neighborRoots;
// map to store the current historical root index for a chainID
mapping(uint256 => uint32) public currentNeighborRootIndex;

// map to store used nullifier hashes
mapping(bytes32 => bool) public nullifierHashes;
// map to store all commitments to prevent accidental deposits with the same commitment
mapping(bytes32 => bool) public commitments;

// map to store the history of root updates
mapping(uint => bytes32[]) public rootHistory;
// pruning length for root history (i.e. the # of history items to persist)
uint pruningLength;
// the latest history index that represents the next index to store history at % pruningLength

// the latest history index that represents the next index to store history
uint latestHistoryIndex;

// currency events
Expand Down Expand Up @@ -77,8 +81,6 @@ abstract contract AnchorPoseidon2 is MerkleTreePoseidon, ReentrancyGuard {
verifier = _verifier;
denomination = _denomination;
chainID = _chainID;
// TODO: Handle pruning length in function signature
pruningLength = 100;
latestHistoryIndex = 0;
// TODO: Parameterize max roots (length of array should be max roots)
rootHistory[latestHistoryIndex] = new bytes32[](1);
Expand Down Expand Up @@ -111,37 +113,34 @@ abstract contract AnchorPoseidon2 is MerkleTreePoseidon, ReentrancyGuard {
*/
function withdraw(
bytes calldata _proof,
bytes32 _root,
bytes calldata _roots,
bytes32 _nullifierHash,
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) external payable nonReentrant {
bytes32[2] memory roots = abi.decode(_roots, (bytes32[2]));
require(_fee <= denomination, "Fee exceeds transfer value");
require(!nullifierHashes[_nullifierHash], "The note has been already spent");
require(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
require(isKnownRoot(roots[0]), "Cannot find your merkle root");
require(roots.length >= edgeList.length + 1, "Incorrect root array length");
for (uint i = 0; i < edgeList.length; i++) {
Edge memory _edge = edgeList[i];
require(isKnownNeighborRoot(_edge.chainID, roots[i+1]), "Neighbor root not found");
}
address rec = address(_recipient);
address rel = address(_relayer);
bytes32[1] memory neighbors = getLatestNeighborRoots();
// console.log(uint256(_nullifierHash));
// console.log(uint256(uint160(rec)));
// console.log(uint256(uint160(rel)));
// console.log(_fee);
// console.log(_refund);
// console.log(uint256(chainID));
// console.log(uint256(_root));
// console.log(uint256(neighbors[0]));
// console.logBytes(_proof);

uint256[8] memory inputs;
inputs[0] = uint256(_nullifierHash);
inputs[1] = uint256(uint160(rec));
inputs[2] = uint256(uint160(rel));
inputs[3] = uint256(_fee);
inputs[4] = uint256(_refund);
inputs[5] = uint256(chainID);
inputs[6] = uint256(_root);
inputs[7] = uint256(neighbors[0]);
inputs[6] = uint256(roots[0]);
inputs[7] = uint256(roots[1]);
bytes memory encodedInputs = abi.encodePacked(inputs);

require(verify(_proof, encodedInputs), "Invalid withdraw proof");
Expand Down Expand Up @@ -213,6 +212,7 @@ abstract contract AnchorPoseidon2 is MerkleTreePoseidon, ReentrancyGuard {
}
}
}

/** @dev */
function getLatestNeighborRoots() public view returns (bytes32[1] memory roots) {
for (uint256 i = 0; i < 1; i++) {
Expand All @@ -224,6 +224,25 @@ abstract contract AnchorPoseidon2 is MerkleTreePoseidon, ReentrancyGuard {
}
}

/** @dev */
function isKnownNeighborRoot(uint256 neighborChainID, bytes32 _root) public view returns (bool) {
kmadk marked this conversation as resolved.
Show resolved Hide resolved
if (_root == 0) {
return false;
}
uint32 _currentRootIndex = currentNeighborRootIndex[neighborChainID];
uint32 i = _currentRootIndex;
do {
if (_root == neighborRoots[neighborChainID][i]) {
return true;
}
if (i == 0) {
i = ROOT_HISTORY_SIZE;
}
i--;
} while (i != _currentRootIndex);
return false;
}

modifier onlyAdmin() {
require(msg.sender == admin, 'sender is not the admin');
_;
Expand Down
17 changes: 7 additions & 10 deletions contracts/anchors/bridged/LinkableAnchorPoseidon2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,6 @@ abstract contract LinkableAnchorPoseidon2 is AnchorPoseidon2, ILinkableAnchor {
return edgeExistsForChain[_chainID];
}

function recordHistory() override external {
// add a new historical record by snapshotting the Anchor's current neighbors
bytes32[1] memory history = getLatestNeighborRoots();
rootHistory[latestHistoryIndex] = history;
// set the next history index modulo pruning length
latestHistoryIndex = latestHistoryIndex % pruningLength;
kmadk marked this conversation as resolved.
Show resolved Hide resolved
emit RootHistoryRecorded(block.timestamp, history);
}

function addEdge(
uint256 sourceChainID,
bytes32 root,
Expand All @@ -59,11 +50,13 @@ abstract contract LinkableAnchorPoseidon2 is AnchorPoseidon2, ILinkableAnchor {
});
edgeList.push(edge);
edgeIndex[sourceChainID] = index;
// add to root histories
uint32 neighborRootIndex = 0;
neighborRoots[sourceChainID][neighborRootIndex] = root;
emit EdgeAddition(sourceChainID, height, root);
// emit update event
bytes32[1] memory neighbors = getLatestNeighborRoots();
emit RootHistoryUpdate(block.timestamp, neighbors);

}

function updateEdge(
Expand All @@ -80,6 +73,10 @@ abstract contract LinkableAnchorPoseidon2 is AnchorPoseidon2, ILinkableAnchor {
root: root,
height: height
});
// add to root histories
uint32 neighborRootIndex = (currentNeighborRootIndex[sourceChainID] + 1) % ROOT_HISTORY_SIZE;
currentNeighborRootIndex[sourceChainID] = neighborRootIndex;
neighborRoots[sourceChainID][neighborRootIndex] = root;
kmadk marked this conversation as resolved.
Show resolved Hide resolved
emit EdgeUpdate(sourceChainID, height, root);
// emit update event
bytes32[1] memory neighbors = getLatestNeighborRoots();
Expand Down
3 changes: 1 addition & 2 deletions contracts/interfaces/ILinkableAnchor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ pragma solidity ^0.8.0;
interface ILinkableAnchor {
function setHandler(address _handler) external;
function setBridge(address _bridge) external;
function recordHistory() external;
function hasEdge(uint256 _chainID) external view returns (bool);
function addEdge(
uint256 sourceChainID,
Expand All @@ -20,4 +19,4 @@ interface ILinkableAnchor {
bytes32 root,
uint256 height
) external payable;
}
}
Loading