*Use custom json flatten algorithm to ensure deterministic hashing
This commit is contained in:
parent
29271df627
commit
5cefe245af
|
@ -15,7 +15,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hyperswarm/dht": "^6.0.1",
|
"@hyperswarm/dht": "^6.0.1",
|
||||||
"json-stable-stringify": "^1.0.1",
|
|
||||||
"libskynet": "^0.0.61",
|
"libskynet": "^0.0.61",
|
||||||
"msgpackr": "^1.6.1"
|
"msgpackr": "^1.6.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,7 @@ import { pack, unpack } from "msgpackr";
|
||||||
import { RPCRequest, RPCResponse } from "./types";
|
import { RPCRequest, RPCResponse } from "./types";
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
import { blake2b } from "libskynet";
|
import { blake2b } from "libskynet";
|
||||||
|
import { flatten } from "./util.js";
|
||||||
import jsonStringify from "json-stable-stringify";
|
|
||||||
|
|
||||||
export default class RpcQuery {
|
export default class RpcQuery {
|
||||||
private _network: RpcNetwork;
|
private _network: RpcNetwork;
|
||||||
|
@ -100,22 +99,24 @@ export default class RpcQuery {
|
||||||
|
|
||||||
private checkResponses() {
|
private checkResponses() {
|
||||||
const responseStore = this._responses;
|
const responseStore = this._responses;
|
||||||
|
|
||||||
const responseStoreData = Object.values(responseStore);
|
const responseStoreData = Object.values(responseStore);
|
||||||
|
|
||||||
type ResponseGroup = { [response: string]: number };
|
type ResponseGroup = { [response: string]: number };
|
||||||
|
|
||||||
const responseObjects = responseStoreData.reduce((output: any, item) => {
|
const responseObjects = responseStoreData.reduce((output: any, item) => {
|
||||||
|
const itemFlattened = flatten(item?.data).sort();
|
||||||
|
|
||||||
const hash = Buffer.from(
|
const hash = Buffer.from(
|
||||||
blake2b(Buffer.from(jsonStringify(item?.data)))
|
blake2b(Buffer.from(JSON.stringify(itemFlattened)))
|
||||||
).toString("hex");
|
).toString("hex");
|
||||||
output[hash] = item?.data;
|
output[hash] = item?.data;
|
||||||
return output;
|
return output;
|
||||||
}, {});
|
}, {});
|
||||||
const responses: ResponseGroup = responseStoreData.reduce(
|
const responses: ResponseGroup = responseStoreData.reduce(
|
||||||
(output: ResponseGroup, item) => {
|
(output: ResponseGroup, item) => {
|
||||||
|
const itemFlattened = flatten(item?.data).sort();
|
||||||
const hash = Buffer.from(
|
const hash = Buffer.from(
|
||||||
blake2b(Buffer.from(jsonStringify(item?.data)))
|
blake2b(Buffer.from(JSON.stringify(itemFlattened)))
|
||||||
).toString("hex");
|
).toString("hex");
|
||||||
output[hash] = output[hash] ?? 0;
|
output[hash] = output[hash] ?? 0;
|
||||||
output[hash]++;
|
output[hash]++;
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
function isBuffer(obj: any): boolean {
|
||||||
|
return (
|
||||||
|
obj &&
|
||||||
|
obj.constructor &&
|
||||||
|
typeof obj.constructor.isBuffer === "function" &&
|
||||||
|
obj.constructor.isBuffer(obj)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Forked from https://github.com/hughsk/flat
|
||||||
|
*/
|
||||||
|
export function flatten(target: any, opts: any = {}): any[] {
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
const delimiter = opts.delimiter || ".";
|
||||||
|
const maxDepth = opts.maxDepth;
|
||||||
|
const transformKey =
|
||||||
|
opts.transformKey || ((key: any) => (isNaN(parseInt(key)) ? key : ""));
|
||||||
|
const output: any[] = [];
|
||||||
|
|
||||||
|
function step(object: any, prev?: any, currentDepth?: any) {
|
||||||
|
currentDepth = currentDepth || 1;
|
||||||
|
Object.keys(object).forEach(function (key) {
|
||||||
|
const value = object[key];
|
||||||
|
const isarray = opts.safe && Array.isArray(value);
|
||||||
|
const type = Object.prototype.toString.call(value);
|
||||||
|
const isbuffer = isBuffer(value);
|
||||||
|
const isobject = type === "[object Object]" || type === "[object Array]";
|
||||||
|
|
||||||
|
const newKey = prev
|
||||||
|
? prev + delimiter + transformKey(key)
|
||||||
|
: transformKey(key);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isarray &&
|
||||||
|
!isbuffer &&
|
||||||
|
isobject &&
|
||||||
|
Object.keys(value).length &&
|
||||||
|
(!opts.maxDepth || currentDepth < maxDepth)
|
||||||
|
) {
|
||||||
|
return step(value, newKey, currentDepth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push(`${newKey}=${value}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
step(target);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
Loading…
Reference in New Issue