Update example
This commit is contained in:
parent
322a00f9de
commit
39fd0d9e1d
|
@ -8,12 +8,21 @@
|
|||
body {
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
form {
|
||||
display: block;
|
||||
border: 1px dashed black;
|
||||
padding: 0.2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
}
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
}
|
||||
#output {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
@ -21,73 +30,112 @@
|
|||
</head>
|
||||
<body>
|
||||
<h1>MerleTree.js example</h1>
|
||||
<form id="form">
|
||||
<div>
|
||||
<label>Leaves <small>(input json array or newline separated)</small></label>
|
||||
<textarea id="input" rows="10">
|
||||
<div>
|
||||
<form id="form">
|
||||
<div>
|
||||
<label>Leaves <small>(input json array or newline separated)</small></label>
|
||||
<textarea id="input" rows="10">
|
||||
0xca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
|
||||
0x3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d
|
||||
0x2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6
|
||||
</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Hash function</label>
|
||||
<div>
|
||||
<input type="radio" name="hash" value="sha256" id="sha256" checked>
|
||||
<label for="sha256">SHA-256</label>
|
||||
<input type="radio" name="hash" value="keccak256" id="keccak256">
|
||||
<label for="keccak256">Keccak-256</label>
|
||||
<div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Options</label>
|
||||
<div>
|
||||
<input type="checkbox" name="option" value="hashLeaves" id="hashLeaves">
|
||||
<label for="hashLeaves">hashLeaves</label>
|
||||
<input type="checkbox" name="option" value="sortLeaves" id="sortLeaves">
|
||||
<label for="sortLeaves">sortLeaves</label>
|
||||
<input type="checkbox" name="option" value="sortPairs" id="sortPairs">
|
||||
<label for="sortPairs">sortPairs</label>
|
||||
<input type="checkbox" name="option" value="duplicateOdd" id="duplicateOdd">
|
||||
<label for="duplicateOdd">duplicateOdd</label>
|
||||
<input type="checkbox" name="option" value="isBitcoinTree" id="isBitcoinTree">
|
||||
<label for="isBitcoinTree">isBitcoinTree</label>
|
||||
<input type="checkbox" name="option" value="fillDefaultHash" id="fillDefaultHash">
|
||||
<label for="fillDefaultHash">fillDefaultHash</label>
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fillDefaultHashView" style="display:none">
|
||||
<label>Fill default hash</label>
|
||||
<small>
|
||||
<a href='#' id="addressZero">addressZero</a>
|
||||
<a href='#' id="hashAddressZero">hash(addressZero)</a>
|
||||
</small>
|
||||
<textarea id="fillDefaultHashValue" rows="1"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<button type="submit" id="submit">Compute</button>
|
||||
</div>
|
||||
<div>
|
||||
<label>Hash function</label>
|
||||
<div>
|
||||
<input type="radio" name="hash" value="sha256" id="sha256" checked>
|
||||
<label for="sha256">SHA-256</label>
|
||||
<input type="radio" name="hash" value="keccak256" id="keccak256">
|
||||
<label for="keccak256">Keccak-256</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Options</label>
|
||||
<div>
|
||||
<input type="checkbox" name="option" value="hashLeaves" id="hashLeaves">
|
||||
<label for="hashLeaves">hashLeaves</label>
|
||||
<input type="checkbox" name="option" value="sortLeaves" id="sortLeaves">
|
||||
<label for="sortLeaves">sortLeaves</label>
|
||||
<input type="checkbox" name="option" value="sortPairs" id="sortPairs">
|
||||
<label for="sortPairs">sortPairs</label>
|
||||
<input type="checkbox" name="option" value="duplicateOdd" id="duplicateOdd">
|
||||
<label for="duplicateOdd">duplicateOdd</label>
|
||||
<input type="checkbox" name="option" value="isBitcoinTree" id="isBitcoinTree">
|
||||
<label for="isBitcoinTree">isBitcoinTree</label>
|
||||
<input type="checkbox" name="option" value="fillDefaultHash" id="fillDefaultHash">
|
||||
<label for="fillDefaultHash">fillDefaultHash</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fillDefaultHashView" style="display:none">
|
||||
<label>Fill default hash</label>
|
||||
<small>
|
||||
<a href='#' id="addressZero">addressZero</a>
|
||||
<a href='#' id="hashAddressZero">hash(addressZero)</a>
|
||||
</small>
|
||||
<input type="text" id="fillDefaultHashValue" />
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">Compute</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<form id="verifyForm">
|
||||
<label>Proof</label>
|
||||
<div>
|
||||
<textarea id="verifyProof" rows="3"></textarea>
|
||||
</div>
|
||||
<label>Leaf</label>
|
||||
<div>
|
||||
<input type="text" id="verifyLeaf" />
|
||||
</div>
|
||||
<label>Root</label>
|
||||
<div>
|
||||
<input type="text" id="verifyRoot" />
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">Verify</button>
|
||||
</div>
|
||||
<label>Verified</label>
|
||||
<div>
|
||||
<pre id="verified"></pre>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<form id="proofForm">
|
||||
<div>
|
||||
<label>Proof</label>
|
||||
<div>
|
||||
<select id="leaveSelect"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<pre id="proof"></pre>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="output">
|
||||
<label>Root</label>
|
||||
<textarea id="root" rows="1"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Leaves</label>
|
||||
<textarea id="leaves" rows="10"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Layers</label>
|
||||
<textarea id="layers" rows="10"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Flat Layers</label>
|
||||
<textarea id="flatLayers" rows="10"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Tree</label>
|
||||
<pre id="tree"></pre>
|
||||
<div>
|
||||
<label>Root</label>
|
||||
<pre id="root"></pre>
|
||||
</div>
|
||||
<div>
|
||||
<label>Leaves</label>
|
||||
<pre id="leaves"></pre>
|
||||
</div>
|
||||
<div>
|
||||
<label>Layers</label>
|
||||
<pre id="layers"></pre>
|
||||
</div>
|
||||
<div>
|
||||
<label>Flat Layers</label>
|
||||
<pre id="flatLayers"></pre>
|
||||
</div>
|
||||
<div>
|
||||
<label>Tree</label>
|
||||
<pre id="tree"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/keccak256@latest/keccak256.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-sha256/0.9.0/sha256.min.js"></script>
|
||||
|
|
271
example/main.js
271
example/main.js
|
@ -1,5 +1,7 @@
|
|||
// elements
|
||||
var $form = document.getElementById('form')
|
||||
var $input = document.getElementById('input')
|
||||
var $options = document.querySelectorAll('input[name="option"]')
|
||||
var $fillDefaultHashView = document.getElementById('fillDefaultHashView')
|
||||
var $fillDefaultHash = document.getElementById('fillDefaultHashValue')
|
||||
var $addressZero = document.getElementById('addressZero')
|
||||
|
@ -9,6 +11,15 @@ var $leaves = document.getElementById('leaves')
|
|||
var $layers = document.getElementById('layers')
|
||||
var $flatLayers = document.getElementById('flatLayers')
|
||||
var $tree = document.getElementById('tree')
|
||||
var $leaveSelect = document.getElementById('leaveSelect')
|
||||
var $proof = document.getElementById('proof')
|
||||
var $verifyForm = document.getElementById('verifyForm')
|
||||
var $verifyProof = document.getElementById('verifyProof')
|
||||
var $verifyLeaf = document.getElementById('verifyLeaf')
|
||||
var $verifyRoot = document.getElementById('verifyRoot')
|
||||
var $verified = document.getElementById('verified')
|
||||
|
||||
// variables
|
||||
|
||||
const addressZero = '0x0000000000000000000000000000000000000000000000000000000000000000'
|
||||
var hashFns = {
|
||||
|
@ -16,16 +27,6 @@ var hashFns = {
|
|||
keccak256: window.keccak256
|
||||
}
|
||||
|
||||
function parseInput (value) {
|
||||
value = $input.value.trim()
|
||||
value = value.replace(/'/gi, '"')
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch (err) {
|
||||
return value.split('\n').filter(function (x) { return x })
|
||||
}
|
||||
}
|
||||
|
||||
var options = {
|
||||
hashLeaves: false,
|
||||
sortLeaves: false,
|
||||
|
@ -34,18 +35,22 @@ var options = {
|
|||
isBitcoinTree: false
|
||||
}
|
||||
|
||||
var tree
|
||||
|
||||
// functions
|
||||
|
||||
function compute () {
|
||||
const value = getInputValue()
|
||||
const leaves = parseInput(value)
|
||||
const hash = getHashFn()
|
||||
const fillDefaultHash = getDefaultFillHashValue()
|
||||
const fillDefaultHash = getDefaultFillHashInput()
|
||||
const _options = Object.assign({}, options, {
|
||||
fillDefaultHash: options.fillDefaultHash ? fillDefaultHash : undefined
|
||||
})
|
||||
console.log('input leaves:', leaves)
|
||||
console.log('hash:', getHashType())
|
||||
console.log('options:', _options)
|
||||
const tree = new window.MerkleTree(leaves, hash, _options)
|
||||
tree = new window.MerkleTree(leaves, hash, _options)
|
||||
const hexRoot = tree.getHexRoot()
|
||||
const hexLeaves = tree.getHexLeaves()
|
||||
const hexLayers = tree.getHexLayers()
|
||||
|
@ -59,13 +64,97 @@ function compute () {
|
|||
setLayersValue(JSON.stringify(hexLayers, null, 2))
|
||||
setFlatLayersValue(JSON.stringify(hexFlatLayers, null, 2))
|
||||
setTreeValue(tree.toString())
|
||||
updateLeaveOptions(hexLeaves)
|
||||
updateProof(0)
|
||||
setVerified('')
|
||||
}
|
||||
|
||||
function parseInput (value) {
|
||||
value = value.trim().replace(/'/gi, '"')
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch (err) {
|
||||
return value.split('\n')
|
||||
.map(function (line) { return line.trim() })
|
||||
.filter(function (line) { return line })
|
||||
}
|
||||
}
|
||||
|
||||
function updateLeaveOptions (leaves) {
|
||||
$leaveSelect.innerHTML = ''
|
||||
leaves.forEach(function (leaf, i) {
|
||||
const el = document.createElement('option')
|
||||
el.value = `${i}`
|
||||
el.text = `#${i} - ${leaf}`
|
||||
$leaveSelect.appendChild(el)
|
||||
})
|
||||
}
|
||||
|
||||
function updateProof (index) {
|
||||
setProof('')
|
||||
if (!tree) {
|
||||
return
|
||||
}
|
||||
const leaves = tree.getHexLeaves()
|
||||
if (!leaves.length) {
|
||||
return
|
||||
}
|
||||
const leaf = leaves[index]
|
||||
const proof = tree.getHexProof(leaf)
|
||||
console.log('proof:', proof)
|
||||
setProof(JSON.stringify(proof, null, 2))
|
||||
}
|
||||
|
||||
function setVerified (verified) {
|
||||
$verified.textContent = verified
|
||||
}
|
||||
|
||||
function verify () {
|
||||
setVerified('')
|
||||
if (!tree) {
|
||||
return
|
||||
}
|
||||
const proof = getVerifyProof()
|
||||
const leaf = getVerifyLeaf()
|
||||
const root = getVerifyRoot()
|
||||
const verified = tree.verify(proof, leaf, root)
|
||||
setVerified(`${verified}`)
|
||||
}
|
||||
|
||||
// getters
|
||||
|
||||
function getHashType () {
|
||||
const $hash = document.querySelector('input[name="hash"]:checked')
|
||||
return $hash.value
|
||||
return $hash.value.trim()
|
||||
}
|
||||
|
||||
function getHashFn () {
|
||||
const key = getHashType()
|
||||
return hashFns[key]
|
||||
}
|
||||
|
||||
function getDefaultFillHashInput () {
|
||||
return $fillDefaultHash.value.trim()
|
||||
}
|
||||
|
||||
function getInputValue (value) {
|
||||
return $input.value.trim()
|
||||
}
|
||||
|
||||
function getVerifyProof () {
|
||||
return parseInput($verifyProof.value.trim())
|
||||
}
|
||||
|
||||
function getVerifyLeaf () {
|
||||
return $verifyLeaf.value.trim()
|
||||
}
|
||||
|
||||
function getVerifyRoot () {
|
||||
return $verifyRoot.value.trim()
|
||||
}
|
||||
|
||||
// setters
|
||||
|
||||
function setHashType (value) {
|
||||
if (!value) {
|
||||
return
|
||||
|
@ -77,21 +166,10 @@ function setHashType (value) {
|
|||
$hash.checked = true
|
||||
}
|
||||
|
||||
function getHashFn () {
|
||||
const key = getHashType()
|
||||
return hashFns[key]
|
||||
}
|
||||
|
||||
function getDefaultFillHashValue () {
|
||||
return $fillDefaultHash.value
|
||||
}
|
||||
|
||||
function getInputValue (value) {
|
||||
return $input.value
|
||||
}
|
||||
|
||||
function setInputValue (value) {
|
||||
$input.value = value
|
||||
function setInputValue (value, onlySave) {
|
||||
if (!onlySave) {
|
||||
$input.value = value
|
||||
}
|
||||
try {
|
||||
localStorage.setItem('input', value)
|
||||
} catch (err) {
|
||||
|
@ -100,19 +178,19 @@ function setInputValue (value) {
|
|||
}
|
||||
|
||||
function setRootValue (value) {
|
||||
$root.value = value
|
||||
$root.textContent = value
|
||||
}
|
||||
|
||||
function setLeavesValue (value) {
|
||||
$leaves.value = value
|
||||
$leaves.textContent = value
|
||||
}
|
||||
|
||||
function setLayersValue (value) {
|
||||
$layers.value = value
|
||||
$layers.textContent = value
|
||||
}
|
||||
|
||||
function setFlatLayersValue (value) {
|
||||
$flatLayers.value = value
|
||||
$flatLayers.textContent = value
|
||||
}
|
||||
|
||||
function setTreeValue (value) {
|
||||
|
@ -127,8 +205,14 @@ function setHashValue (value) {
|
|||
}
|
||||
}
|
||||
|
||||
function setOptionValue (key, enabled) {
|
||||
function setOptionValue (key, enabled, onlySave) {
|
||||
try {
|
||||
if (!onlySave) {
|
||||
var $option = document.querySelector(`input[name="option"][id="${key}"]`)
|
||||
if ($option) {
|
||||
$option.checked = enabled
|
||||
}
|
||||
}
|
||||
options[key] = enabled
|
||||
localStorage.setItem('options', JSON.stringify(options))
|
||||
toggleFillDefaultHashView()
|
||||
|
@ -137,11 +221,10 @@ function setOptionValue (key, enabled) {
|
|||
}
|
||||
}
|
||||
|
||||
function setFillDefaultHash (value) {
|
||||
$fillDefaultHash.value = value
|
||||
}
|
||||
|
||||
function setDefaultFillHashValue (value) {
|
||||
function setFillDefaultHash (value, onlySave) {
|
||||
if (!onlySave) {
|
||||
$fillDefaultHash.value = value
|
||||
}
|
||||
try {
|
||||
localStorage.setItem('fillDefaultHash', value)
|
||||
} catch (err) {
|
||||
|
@ -149,6 +232,43 @@ function setDefaultFillHashValue (value) {
|
|||
}
|
||||
}
|
||||
|
||||
function setProof (value) {
|
||||
$proof.textContent = value
|
||||
}
|
||||
|
||||
function setVerifyProof (value, onlySave) {
|
||||
if (!onlySave) {
|
||||
$verifyProof.value = value
|
||||
}
|
||||
try {
|
||||
localStorage.setItem('verifyProof', value)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
function setVerifyLeaf (value, onlySave) {
|
||||
if (!onlySave) {
|
||||
$verifyLeaf.value = value
|
||||
}
|
||||
try {
|
||||
localStorage.setItem('verifyLeaf', value)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
function setVerifyRoot (value, onlySave) {
|
||||
if (!onlySave) {
|
||||
$verifyRoot.value = value
|
||||
}
|
||||
try {
|
||||
localStorage.setItem('verifyRoot', value)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFillDefaultHashView () {
|
||||
if (options.fillDefaultHash) {
|
||||
$fillDefaultHashView.style.display = 'block'
|
||||
|
@ -157,35 +277,41 @@ function toggleFillDefaultHashView () {
|
|||
}
|
||||
}
|
||||
|
||||
// event listeners
|
||||
|
||||
$form.addEventListener('submit', function (event) {
|
||||
event.preventDefault()
|
||||
compute()
|
||||
})
|
||||
|
||||
$verifyForm.addEventListener('submit', function (event) {
|
||||
event.preventDefault()
|
||||
verify()
|
||||
})
|
||||
|
||||
$input.addEventListener('input', function (event) {
|
||||
const value = event.target.value
|
||||
setInputValue(value)
|
||||
const value = event.target.value.trim()
|
||||
setInputValue(value, true)
|
||||
})
|
||||
|
||||
var $hashes = document.querySelectorAll('input[name="hash"]')
|
||||
$hashes.forEach(function ($hash) {
|
||||
$hash.addEventListener('change', function (event) {
|
||||
const value = event.target.value
|
||||
const value = event.target.value.trim()
|
||||
setHashValue(value)
|
||||
})
|
||||
})
|
||||
|
||||
var $options = document.querySelectorAll('input[name="option"]')
|
||||
$options.forEach(function ($option) {
|
||||
$option.addEventListener('change', function (event) {
|
||||
const value = event.target.value
|
||||
const value = event.target.value.trim()
|
||||
const checked = event.target.checked
|
||||
setOptionValue(value, checked)
|
||||
setOptionValue(value, checked, true)
|
||||
})
|
||||
})
|
||||
|
||||
$fillDefaultHash.addEventListener('input', function (event) {
|
||||
const value = event.target.value
|
||||
const value = event.target.value.trim()
|
||||
setFillDefaultHash(value)
|
||||
})
|
||||
|
||||
|
@ -200,6 +326,27 @@ $hashAddressZero.addEventListener('click', function (event) {
|
|||
setFillDefaultHash(result)
|
||||
})
|
||||
|
||||
$leaveSelect.addEventListener('change', function (event) {
|
||||
updateProof(Number(event.target.value.trim()))
|
||||
})
|
||||
|
||||
$verifyProof.addEventListener('input', function (event) {
|
||||
const value = event.target.value.trim()
|
||||
setVerifyProof(value, true)
|
||||
})
|
||||
|
||||
$verifyLeaf.addEventListener('input', function (event) {
|
||||
const value = event.target.value.trim()
|
||||
setVerifyLeaf(value, true)
|
||||
})
|
||||
|
||||
$verifyRoot.addEventListener('input', function (event) {
|
||||
const value = event.target.value.trim()
|
||||
setVerifyRoot(value, true)
|
||||
})
|
||||
|
||||
// init
|
||||
|
||||
function load () {
|
||||
try {
|
||||
const value = localStorage.getItem('input')
|
||||
|
@ -221,6 +368,9 @@ function load () {
|
|||
const value = JSON.parse(localStorage.getItem('options'))
|
||||
if (value) {
|
||||
options = value
|
||||
for (const option in options) {
|
||||
setOptionValue(option, options[option])
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
|
@ -228,14 +378,41 @@ function load () {
|
|||
try {
|
||||
const value = localStorage.getItem('fillDefaultHash')
|
||||
if (value) {
|
||||
setDefaultFillHashValue(value)
|
||||
setFillDefaultHash(value)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
try {
|
||||
const value = localStorage.getItem('verifyProof')
|
||||
if (value) {
|
||||
setVerifyProof(value)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
try {
|
||||
const value = localStorage.getItem('verifyLeaf')
|
||||
if (value) {
|
||||
setVerifyLeaf(value)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
try {
|
||||
const value = localStorage.getItem('verifyRoot')
|
||||
if (value) {
|
||||
setVerifyRoot(value)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
function main () {
|
||||
load()
|
||||
toggleFillDefaultHashView()
|
||||
compute()
|
||||
}
|
||||
|
||||
load()
|
||||
main()
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"build": "npm run clean && tsc && npm run build:browser",
|
||||
"build:browser": "browserify -t [ babelify --presets [ @babel/preset-env ] ] dist/index.js | uglifyjs > merkletree.js",
|
||||
"lint": "standardx --fix src/*.ts test/*.js",
|
||||
"lint:example": "standardx --fix example/*.js",
|
||||
"docs": "rimraf docs/ && typedoc --plugin typedoc-plugin-markdown --hideSources --theme markdown --hideGenerator --excludeExternals --excludePrivate --out docs index.ts",
|
||||
"prepare": "npm run lint && npm run build"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue