-
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The `FragmentTree` does not follow orignal code hashing. A fragment leaf consists of `value`, `slot` and `byteLength`. `byteLength` is the orignal length of any given bytes.
- Loading branch information
1 parent
8414a8a
commit 18d9bbc
Showing
4 changed files
with
107 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const assert = require('assert'); | ||
const { randomFillSync } = require('crypto'); | ||
|
||
const { FragmentTree } = require('./../../utils'); | ||
|
||
function randomString (size) { | ||
return `0x${randomFillSync(Buffer.alloc(size)).toString('hex')}`; | ||
} | ||
|
||
describe('FragmentTree', function () { | ||
for (let i = 1; i < 1024; i++) { | ||
it(`Loop #${i}`, () => { | ||
const bytecode = randomString(i); | ||
const byteLength = (bytecode.length - 2) / 2; | ||
const tree = new FragmentTree().run(bytecode); | ||
const slots = ~~((i + 31) / 32); | ||
|
||
for (let x = 0; x < slots; x++) { | ||
const leaf = tree.leaves[x]; | ||
const startOffset = (x * 64) + 2; | ||
const value = bytecode.substring(startOffset, startOffset + 64).padEnd(64, '0'); | ||
|
||
assert.equal(leaf.slot, x, 'slot should match'); | ||
assert.equal(leaf.byteLength, byteLength, 'byteLength should match'); | ||
assert.equal(leaf.value, `0x${value}`, 'value should match'); | ||
} | ||
|
||
assert.equal(tree.leaves.length, slots + slots % 2, 'number of leaves should match'); | ||
|
||
const proof = tree.calculateProof(slots - 1); | ||
assert.ok(tree.verifyProof(tree.leaves[slots - 1], proof), 'verifyProof'); | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
const ethers = require('ethers'); | ||
|
||
const AbstractMerkleTree = require('./AbstractMerkleTree'); | ||
|
||
// We should support compressed proofs in the future, | ||
// means re-using hashes if we construct a proof for more than 1 slot. | ||
module.exports = class FragmentTree extends AbstractMerkleTree { | ||
/// @dev return hash proof for `slot`, | ||
/// `slot` is position in `this.leaves`. | ||
/// @return proof - array of 32 bytes hex-string (hashes) | ||
calculateProof (slot) { | ||
const proof = []; | ||
const len = this.depth - 1; | ||
|
||
for (let i = 0; i < len; i++) { | ||
proof.push(this.tree[i][slot ^ 1].hash); | ||
slot >>= 1; | ||
} | ||
return proof; | ||
} | ||
|
||
/// @dev verify if given `proofs` and `leaf` match `this.root.hash` | ||
verifyProof (leaf, proofs) { | ||
const len = proofs.length; | ||
let hash = leaf.hash; | ||
let slot = leaf.slot; | ||
|
||
for (let i = 0; i < len; i++) { | ||
const proof = proofs[i]; | ||
|
||
if (slot % 2 === 0) { | ||
hash = this.constructor.hash(hash, proof); | ||
} else { | ||
hash = this.constructor.hash(proof, hash); | ||
} | ||
slot >>= 1; | ||
} | ||
|
||
return hash === this.root.hash; | ||
} | ||
|
||
/// @notice build the tree for the given hex-string `data`. | ||
/// @dev `data` will be splited into fragments and padded to one word (32 bytes). | ||
run (data) { | ||
data = data.replace('0x', ''); | ||
|
||
this.tree = [[]]; | ||
|
||
const leaves = this.tree[0]; | ||
const byteLength = data.length / 2; | ||
const len = data.length; | ||
let slot = 0; | ||
|
||
for (let i = 0; i < len;) { | ||
const value = '0x' + data.substring(i, i += 64).padEnd(64, '0'); | ||
const hash = ethers.utils.solidityKeccak256( | ||
['bytes32', 'uint256', 'uint256'], | ||
[value, slot, byteLength] | ||
); | ||
|
||
slot = leaves.push({ hash, value, slot, byteLength }); | ||
} | ||
|
||
this.recal(0); | ||
|
||
return this; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters