-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathMerkleStatementContract.sol
122 lines (105 loc) · 4.95 KB
/
MerkleStatementContract.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
Copyright 2019-2022 StarkWare Industries Ltd.
Licensed under the Apache License, Version 2.0 (the "License").
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.starkware.co/open-source-license/
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions
and limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0.
pragma solidity ^0.6.12;
import "./components/FactRegistry.sol";
import "./MerkleVerifier.sol";
contract MerkleStatementContract is MerkleVerifier, FactRegistry {
/*
This function receives an initial Merkle queue (consists of indices of leaves in the Merkle
in addition to their values) and a Merkle view (contains the values of all the nodes
required to be able to validate the queue). In case of success it registers the Merkle fact,
which is the hash of the queue together with the resulting root.
*/
// NOLINTNEXTLINE: external-function.
function verifyMerkle(
uint256[] memory merkleView,
uint256[] memory initialMerkleQueue,
uint256 height,
uint256 expectedRoot
) public {
// Ensure 'height' is bounded from above as a sanity check
// (the bound is somewhat arbitrary).
require(height < 200, "Height must be < 200.");
require(
initialMerkleQueue.length <= MAX_N_MERKLE_VERIFIER_QUERIES * 2,
"TOO_MANY_MERKLE_QUERIES"
);
require(initialMerkleQueue.length % 2 == 0, "ODD_MERKLE_QUEUE_SIZE");
uint256 merkleQueuePtr;
uint256 channelPtr;
uint256 nQueries;
uint256 dataToHashPtr;
uint256 badInput = 0;
assembly {
// Skip 0x20 bytes length at the beginning of the merkleView.
let merkleViewPtr := add(merkleView, 0x20)
// Let channelPtr point to a free space.
channelPtr := mload(0x40) // freePtr.
// channelPtr will point to the merkleViewPtr since the 'verify' function expects
// a pointer to the proofPtr.
mstore(channelPtr, merkleViewPtr)
// Skip 0x20 bytes length at the beginning of the initialMerkleQueue.
merkleQueuePtr := add(initialMerkleQueue, 0x20)
// Get number of queries.
nQueries := div(mload(initialMerkleQueue), 0x2) //NOLINT: divide-before-multiply.
// Get a pointer to the end of initialMerkleQueue.
let initialMerkleQueueEndPtr := add(
merkleQueuePtr,
mul(nQueries, MERKLE_SLOT_SIZE_IN_BYTES)
)
// Let dataToHashPtr point to a free memory.
dataToHashPtr := add(channelPtr, 0x20) // Next freePtr.
// Copy initialMerkleQueue to dataToHashPtr and validaite the indices.
// The indices need to be in the range [2**height..2*(height+1)-1] and
// strictly incrementing.
// First index needs to be >= 2**height.
let idxLowerLimit := shl(height, 1)
for {
} lt(merkleQueuePtr, initialMerkleQueueEndPtr) {
} {
let curIdx := mload(merkleQueuePtr)
// badInput |= curIdx < IdxLowerLimit.
badInput := or(badInput, lt(curIdx, idxLowerLimit))
// The next idx must be at least curIdx + 1.
idxLowerLimit := add(curIdx, 1)
// Copy the pair (idx, hash) to the dataToHash array.
mstore(dataToHashPtr, curIdx)
mstore(add(dataToHashPtr, 0x20), mload(add(merkleQueuePtr, 0x20)))
dataToHashPtr := add(dataToHashPtr, 0x40)
merkleQueuePtr := add(merkleQueuePtr, MERKLE_SLOT_SIZE_IN_BYTES)
}
// We need to enforce that lastIdx < 2**(height+1)
// => fail if lastIdx >= 2**(height+1)
// => fail if (lastIdx + 1) > 2**(height+1)
// => fail if idxLowerLimit > 2**(height+1).
badInput := or(badInput, gt(idxLowerLimit, shl(height, 2)))
// Reset merkleQueuePtr.
merkleQueuePtr := add(initialMerkleQueue, 0x20)
// Let freePtr point to a free memory (one word after the copied queries - reserved
// for the root).
mstore(0x40, add(dataToHashPtr, 0x20))
}
require(badInput == 0, "INVALID_MERKLE_INDICES");
bytes32 resRoot = verifyMerkle(channelPtr, merkleQueuePtr, bytes32(expectedRoot), nQueries);
bytes32 factHash;
assembly {
// Append the resulted root (should be the return value of verify) to dataToHashPtr.
mstore(dataToHashPtr, resRoot)
// Reset dataToHashPtr.
dataToHashPtr := add(channelPtr, 0x20)
factHash := keccak256(dataToHashPtr, add(mul(nQueries, 0x40), 0x20))
}
registerFact(factHash);
}
}