merkletreejs method getHexProof doesn't work? - merkle-tree

I am trying test merkle proof with merkletreejs library and I can't figure out why this works
const tree = new MerkleTree(leaves, SHA256)
const root = tree.getHexRoot()
const leaf = SHA256('a')
const proof = tree.getProof(leaf) // РАБОТАЕТ
console.log(tree.verify(proof, leaf, root)) // true
But this not ?
const tree = new MerkleTree(leaves, SHA256)
const root = tree.getHexRoot()
const leaf = SHA256('a')
const proof = tree.getHexProof(leaf)
console.log(tree.verify(proof, leaf, root)) // false

It seems it needs some extra code in order to work.
This code works:
const { MerkleTree } = require('merkletreejs')
const sha1 = require('crypto-js/sha1')
const leaves = [
'd89f84d948796605a413e196f40bce1d6294175d',
'32f04c7f572bf75a266268c6f4d8c92731dc3b7f',
'b80b52d80f5fe940ac2c987044bc439e4218ac94',
'1553c75a1d637961827f4904a0955e57915d8310'
]
const tree = new MerkleTree(leaves, sha1, {
sortLeaves: true,
sortPairs: true
})
const root = tree.getHexRoot()
const leaf = 'b80b52d80f5fe940ac2c987044bc439e4218ac94'
const proof = tree.getHexProof(leaf)
console.log(tree.verify(proof, leaf, root))

Related

What is the data structure for real-time searching for the most suitable (least diverged) text mask for given input

Given:
A constant original array of string (~5k elements)
[
"595(###)###-###",
"974-####-####",
"262-#####-####",
"40-##-###-####",
"381-##-###-####",
...
]
The #-sign means "any digit". All # signs are located only after digits part, but can contain (), space or - sign
a user-typed string that could be like one of these
[
"1 (23"
"7 950 745 88 15",
"7 (950) 745-88-15",
"7 (950) 745 88-15",
"79507458815"
]
I need to find the most suitable mask in array (1) for given string (2).
The solution must fit in O(log n) in CPU, and, O(n^2) in memory.
My idea of suitability is based on Damerau–Levenshtein distance, but it punishes for every difference with #-sign, although it shouldn't.
I tried fuzzy search and BK-tree, but it works way too slow, takes about 100 ms to compute.
The answer was to use so-called Patricia tree
the final structure (JSON) is
{
"7": {
"12": {
"$value": "712###########",
},
"13": {
"4": {
...
}
},
"95#": {
"$value": "795###########",
},
"maxPrefixLength": "3",
...
},
"maxPrefixLength": "1",
...
}
The algorithm is (JS)
const getMaskedKeys = (obj: any) =>
Object.keys(obj).filter((e) => e.includes("#"));
const findValues = (node: TrieNode) => {
let currentNode = node;
const values: string[] = [];
while (currentNode) {
values.push(currentNode.$value);
currentNode = currentNode["#"];
}
return values;
};
const getObjectKeyByPath = (obj: any, path: string[]) => {
let result = obj;
for (const p of path) {
result = result[p];
}
return result;
};
export function createMaskTree(maskRows: string[]) {
const uniqueRows = [...new Set(maskRows)];
const masks = uniqueRows.map((mask, i) => {
const key = mask.replace(/[ +()\-]/g, "");
return [key, mask];
});
masks.sort((a, b) => (a[0] > b[0] ? 1 : -1));
const trie = Trie.create({
wildcard: "#",
});
masks.forEach(([key, mask]) => {
trie.insert(key, mask);
});
optimize(trie.tree);
return trie.tree as TrieNode;
}
export function searchMask(
cleanValue: string,
root: TrieNode,
debug = false
): string[] {
let matches: string[] = [];
let path: string[] = [];
let key = cleanValue;
const q: TrieNode[] = [root];
while (q.length) {
const currentNode = q.shift()!;
if (debug) {
console.log(
"Search Iteration. Key:",
key,
"Path:",
path,
"level:",
path.length
);
}
let l = currentNode.maxPrefixLength || key.length;
// let l = key.length;
let isEmpty = true;
// Search for existing prefixes and recursively descend
while (l--) {
const prefix = key.substring(0, l + 1);
if (debug) {
console.log("prefix=", prefix, "path=", path);
}
if (currentNode[prefix]) {
isEmpty = false;
path.push(prefix);
if (debug) {
console.log(prefix, "found in tree! current path:", path);
}
q.push(currentNode[prefix]);
const suffix = key.substring(l + 1);
key = suffix;
}
}
if (isEmpty) {
let l = currentNode.maxPrefixLength || key.length;
while (l--) {
const prefix = key.substring(0, l) + "#";
if (debug) console.log(" mask prefix=", prefix);
if (currentNode[prefix] && currentNode[prefix].$value) {
matches.push(...findValues(currentNode[prefix]));
}
}
}
}
if (!matches.length) {
if (debug)
console.log(
"No matches found without mask. Trying again with mask only!"
);
const last = path.pop();
key = last + key;
let currentNode: TrieNode = root;
let isRunning = true;
while (isRunning) {
currentNode = getObjectKeyByPath(root, path);
const keys = getMaskedKeys(currentNode);
if (debug) {
console.log(
"Mask search Iteration. Key:",
key,
"Path:",
path,
"level:",
path.length,
"currentNode keys:",
keys
);
}
let l = currentNode.maxPrefixLength || key.length;
while (l--) {
const prefix = key.substring(0, l) + "#";
if (debug) console.log(" mask prefix=", prefix);
if (currentNode[prefix] && currentNode[prefix].$value) {
matches.push(...findValues(currentNode[prefix]));
}
}
let pathPart = path.pop();
key = pathPart + key;
if (currentNode === root && !path.length) {
isRunning = false;
}
}
}
return matches;
}

How to access the BufferGeometry of IFC items in web-ifc-three

I'm trying to get the geometry of an element
i.e. a BufferGeometry object corresponding to an expressId I have (not through picking).
Basically I'm asking how to traverse the IFC model and export each object as a separate OBJ.
I'll note I have reverse engineered code to achieve that for some version of the package, but it uses undocumented functionality, so naturally it broke in later versions (the code also colors the geometry according to the material's color so I don't need an mtl):
Don't copy this code it won't work
Object.values(bimModel.ifcManager.state.models[bimModel.modelID].items).forEach(type => {
Object.entries(type.geometries).forEach(([id, geometry]) => {
const properties = bimModel.getItemProperties(Number(id))
const numVertices = geometry.getAttribute('position').count
const color = type.material.color.toArray().map(x => x * 255)
const vertexColors = new Uint8Array(Array.from({ length: numVertices }, () => color).flat())
geometry.setAttribute('color', new BufferAttribute(vertexColors, 3, true))
})
})
This is exactly what we do to export models to glTF. The basic workflow is:
Decide what IFC categories you would like to export.
Get all the items of each category.
Reconstruct the mesh for each item.
Export the mesh using the Three.js exporter of your choice.
Let's see a basic example to get all the meshes from the walls. The process is not as straightforward as having each IFC item as a separate mesh, but that's the price for having the draw calls at minimum (otherwise, a browser wouldn't stand even medium-sized IFC files):
import { IFCWALLSTANDARDCASE } from 'web-ifc';
async function getAllWallMeshes() {
// Get all the IDs of the walls
const wallsIDs = manager.getAllItemsOfType(0, IFCWALL, false);
const meshes = [];
const customID = 'temp-gltf-subset';
for (const wallID of wallsIDs) {
const coordinates = [];
const expressIDs = [];
const newIndices = [];
const alreadySaved = new Map();
// Get the subset for the wall
const subset = viewer.IFC.loader.ifcManager.createSubset({
ids: [wallID],
modelID,
removePrevious: true,
customID
});
// Subsets have their own index, but share the BufferAttributes
// with the original geometry, so we need to rebuild a new
// geometry with this index
const positionAttr = subset.geometry.attributes.position;
const expressIDAttr = subset.geometry.attributes.expressID;
const newGroups = subset.geometry.groups
.filter((group) => group.count !== 0);
const newMaterials = [];
const prevMaterials = subset.material;
let newMaterialIndex = 0;
newGroups.forEach((group) => {
newMaterials.push(prevMaterials[group.materialIndex]);
group.materialIndex = newMaterialIndex++;
});
let newIndex = 0;
for (let i = 0; i < subset.geometry.index.count; i++) {
const index = subset.geometry.index.array[i];
if (!alreadySaved.has(index)) {
coordinates.push(positionAttr.array[3 * index]);
coordinates.push(positionAttr.array[3 * index + 1]);
coordinates.push(positionAttr.array[3 * index + 2]);
expressIDs.push(expressIDAttr.getX(index));
alreadySaved.set(index, newIndex++);
}
const saved = alreadySaved.get(index);
newIndices.push(saved);
}
const geometryToExport = new BufferGeometry();
const newVerticesAttr = new BufferAttribute(Float32Array.from(coordinates), 3);
const newExpressIDAttr = new BufferAttribute(Uint32Array.from(expressIDs), 1);
geometryToExport.setAttribute('position', newVerticesAttr);
geometryToExport.setAttribute('expressID', newExpressIDAttr);
geometryToExport.setIndex(newIndices);
geometryToExport.groups = newGroups;
geometryToExport.computeVertexNormals();
const mesh = new Mesh(geometryToExport, newMaterials);
meshes.push(mesh);
}
viewer.IFC.loader.ifcManager.removeSubset(modelID, undefined, customID);
return meshes;
}

How to use EnumProcesses in node-ffi

I was trying to use EnumProcesses with node-ffi. I got code below:
import ffi from 'ffi'
export const psapi = ffi.Library('psapi', {
EnumProcesses: ['bool', ['ulong', 'ulong', 'uint16*']]
})
export class Win32ProcessManager {
public async getProcessList () {
let lpidProcess = ref.alloc('ulong*')
const cb = 1024
const lpcbNeeded = ref.alloc('uint16*')
const res = psapi.EnumProcesses(lpidProcess, cb, lpcbNeeded)
const ulongSize = (ref as any).sizeof.ulong
const totalBytesReturned = lpcbNeeded.readInt16LE()
const processCount = totalBytesReturned / ulongSize
console.log(`processCount: ${processCount}`)
// ??? How to get the value from the lpidProcess?
return lpidProcess
}
}
I tried with ref.get but I encountered errors:
let processId = ref.get(array, 0, ref.types.ulong)
console.log(processId)
const pointerSize = (ref as any).sizeof.pointer
console.log(pointerSize)
let processId2 = ref.get(array, (ref as any).sizeof.pointer, ref.types.ulong)
console.log(processId2)
Errors:
RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to write outside buffer bounds
Anyone knows how to use node-ffi read the array data from dll?
Thanks #DrakeWu-MSFT, I finally got my code works, here are how they looks finally:
import ffi from 'ffi';
import ref from 'ref';
import ArrayType from "ref-array";
export const psapi = ffi.Library('psapi', {
EnumProcesses: ['bool', ['ulong*', 'ulong', 'uint16*']],
});
export class Win32ProcessManager {
public getProcessIdList (): number[] {
const processIdLength = 1024;
const ulongSize = (ref as any).sizeof.ulong;
const cb = processIdLength * ulongSize;
let processIdArray = ArrayType('ulong', processIdLength);
let lpidProcess = ref.alloc(processIdArray);
const lpcbNeeded = ref.alloc('uint16*');
const res = psapi.EnumProcesses(lpidProcess, cb, lpcbNeeded);
if (res) {
const totalBytesReturned = lpcbNeeded.readInt16LE();
const processCount = totalBytesReturned / ulongSize;
const processArray = (lpidProcess as any).deref();
let resultProcessArray: number[] = [];
for (let i = 0; i < processCount; i++) {
resultProcessArray.push(processArray[i]);
}
return resultProcessArray;
} else {
console.error(`Get process list failed with result from EnumProcess: ${res}`);
return [];
}
}
}
I was struggled with getting array data from the pointer, and that was wrong, as #DrakeWu-MSFT said in the comment, because I didn't allocate enough spaces for the buffer, no data can be write into that. With ref-array and a pointer to the array, it works like a charm.

How to set object position to be center of its mass?

Right now, when I upload my model, its center(highlighted by transformControls) is not in the model's center of mass. How can I shift it there?
I tried this:
(model) => {
box = new Box3();
box.expandByObject(model);
const centerOfBox = new Vector3();
box.getCenter(centerOfBox);
const size = new Vector3();
box.getSize(size);
const centerOfWorld = new Vector3(0, size.y/2, 0);
const offset = new Vector3();
offset.subVectors(centerOfWorld, centerOfBox);
const scale = model.scale;
model.translateX(scale.x*offset.x);
model.translateY(scale.y*offset.y);
model.translateZ(scale.z*offset.z);
}
and also this:
(model) => {
model.traverse(function(child) {
const rememberPosition = child.getWorldPosition(new Vector3());
const box = new Box3();
box.expandByObject(child);
const centerOfBox = new Vector3();
box.getCenter(centerOfBox);
const offset = new Vector3();
offset.subVectors(rememberPosition, centerOfBox);
child.traverse(function(ch) {
if (child instanceof Mesh) {
ch.geometry.translate(offset.x, offset.y, offset.z);
ch.translateX(-offset.x);
ch.translateY(-offset.y);
ch.translateZ(-offset.z);
}
});
});
model.position.copy(new Vector3());
}
In the latter version, I tried to shift the geometry to be at the object's position and then shift everything (geometry+position) back to the original place (original place = visibly original place).

RxJS several input streams. Proper way to deal with them?

I have five inputs on my app and I want to calculate the sum of each of them in a final number.
So far I did this:
const input1 = document.querySelector("#text1");
const input2 = document.querySelector("#text2");
const input3 = document.querySelector("#text3");
const input4 = document.querySelector("#text4");
const input5 = document.querySelector("#text5");
function setObservable(selector) {
return Rx.Observable
.fromEvent(selector, "input")
.map(event => parseInt(event.target.value))
.startWith(0)
}
const input$ = setObservable(input1)
const input2$ = setObservable(input2)
const input3$ = setObservable(input3)
const input4$ = setObservable(input4)
const input5$ = setObservable(input5)
const source = Rx.Observable.combineLatest(input$, input2$, input3$, input4$, input5$)
source.subscribe(value => {
const totalHTML = document.querySelector(".total");
const total = value.reduce((acc, curr) => acc + curr, 0);
totalHTML.textContent = total;
});
Which is working fine... but is there any more elegant way to get the values of each input without specifying all of them?
For sure! Let's store all your selectors in an array:
const selectors = ['#text1', '#text2', '#text3', '#text4', '#text5'];
And let's map over these to get the actual elements:
const elements = selectors.map(s => document.querySelector(s));
And now let's map over these elements and get an array of Observables:
const clickStreams = elements.map(setObservable);
And finally we can combine latest (like you did before):
const click$ = Rx.Observable.combineLatest(clickStreams);

Resources