Skip to content
Snippets Groups Projects
Commit 4b99eb3d authored by Igor Markin's avatar Igor Markin
Browse files

Add build

parent 7b6bed30
No related branches found
No related tags found
1 merge request!70[Status verification improvement] Implement new improved status verification
import { TxData, TxMerkleTreeData } from "../../types"; import { TxData, DecodedCallData } from "../../types";
import { Request } from "../../utils/requestAdapter"; import { Request } from "../../utils/requestAdapter";
declare class AeternityService { declare class AeternityService {
readonly _nodeUrls: any[]; readonly _nodeUrls: any[];
...@@ -14,7 +14,7 @@ declare class AeternityService { ...@@ -14,7 +14,7 @@ declare class AeternityService {
*/ */
requestApi: (apiUrls: string[]) => Request; requestApi: (apiUrls: string[]) => Request;
getTxDataByHash(hash: string): Promise<TxData>; getTxDataByHash(hash: string): Promise<TxData>;
decodeContractCallData(callData: string): Promise<TxMerkleTreeData>; decodeContractCallData(callData: string): Promise<DecodedCallData>;
getBlock(height: number): Promise<{ getBlock(height: number): Promise<{
height: number; height: number;
time: number; time: number;
......
...@@ -101,8 +101,8 @@ class AeternityService { ...@@ -101,8 +101,8 @@ class AeternityService {
}, },
})); }));
return { return {
merkleeTreeFileName: (_c = (_b = (_a = data.arguments[0]) === null || _a === void 0 ? void 0 : _a.value[0]) === null || _b === void 0 ? void 0 : _b.key) === null || _c === void 0 ? void 0 : _c.value, key: (_c = (_b = (_a = data.arguments[0]) === null || _a === void 0 ? void 0 : _a.value[0]) === null || _b === void 0 ? void 0 : _b.key) === null || _c === void 0 ? void 0 : _c.value,
rootNodeHash: (_f = (_e = (_d = data.arguments[0]) === null || _d === void 0 ? void 0 : _d.value[0]) === null || _e === void 0 ? void 0 : _e.val) === null || _f === void 0 ? void 0 : _f.value, value: (_f = (_e = (_d = data.arguments[0]) === null || _d === void 0 ? void 0 : _d.value[0]) === null || _e === void 0 ? void 0 : _e.val) === null || _f === void 0 ? void 0 : _f.value,
}; };
}); });
} }
......
declare const EventEmitter: any; declare const EventEmitter: any;
import { BlockData, MerkleTree, StatusData, TxData, VerificationData } from "../../types"; import { BlockData, StatusesBatchData, StatusData, TxData, VerificationData } from "../../types";
import { AxiosError } from "axios"; import { AxiosError } from "axios";
export declare const TRANSACTION_RETRIEVED = "TRANSACTION_RETRIEVED"; export declare const TRANSACTION_RETRIEVED = "TRANSACTION_RETRIEVED";
export declare const MERKLE_TREE_VERIFIED = "MERKLE_TREE_VERIFIED"; export declare const MERKLE_TREE_VERIFIED = "MERKLE_TREE_VERIFIED";
export declare const STATUS_BATCH_VERIFIED = "STATUS_BATCH_VERIFIED";
export declare const BLOCK_DATA_RETRIEVED = "BLOCK_DATA_RETRIEVED"; export declare const BLOCK_DATA_RETRIEVED = "BLOCK_DATA_RETRIEVED";
declare class VerificationService extends EventEmitter { declare class VerificationService extends EventEmitter {
private _aeternityService; private _aeternityService;
...@@ -11,8 +12,15 @@ declare class VerificationService extends EventEmitter { ...@@ -11,8 +12,15 @@ declare class VerificationService extends EventEmitter {
getTransaction(hash: string): Promise<TxData>; getTransaction(hash: string): Promise<TxData>;
getBlockData(blockHeight: number): Promise<BlockData>; getBlockData(blockHeight: number): Promise<BlockData>;
verifyStatusData(statusData: StatusData): Promise<VerificationData>; verifyStatusData(statusData: StatusData): Promise<VerificationData>;
getMerkleTreeForTransaction(txData: TxData): Promise<MerkleTree>; getStatusBatchDataForTx(txData: TxData): Promise<StatusesBatchData>;
verifyMerkleTreeNode(nodeToVerify: string, merkleTree: MerkleTree): Promise<boolean>; verifyStatusSHA256Batch(status: string, statusesBatchData: StatusesBatchData): Promise<{
verified: boolean;
statusPosition: number;
}>;
verifyStatusesMerkleTree(nodeToVerify: string, merkleTree: StatusesBatchData): Promise<{
verified: boolean;
statusPosition: number;
}>;
catchVerificationError(error: AxiosError, verificationFailureMessage: string): void; catchVerificationError(error: AxiosError, verificationFailureMessage: string): void;
} }
export default VerificationService; export default VerificationService;
...@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { ...@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.BLOCK_DATA_RETRIEVED = exports.MERKLE_TREE_VERIFIED = exports.TRANSACTION_RETRIEVED = void 0; exports.BLOCK_DATA_RETRIEVED = exports.STATUS_BATCH_VERIFIED = exports.MERKLE_TREE_VERIFIED = exports.TRANSACTION_RETRIEVED = void 0;
const index_1 = require("../../index"); const index_1 = require("../../index");
const EventEmitter = require("eventemitter2"); const EventEmitter = require("eventemitter2");
const AeternityService_1 = __importDefault(require("../AeternityService/AeternityService")); const AeternityService_1 = __importDefault(require("../AeternityService/AeternityService"));
...@@ -20,7 +20,15 @@ const CloudflareService_1 = __importDefault(require("../CloudflareService")); ...@@ -20,7 +20,15 @@ const CloudflareService_1 = __importDefault(require("../CloudflareService"));
const VerificationError_1 = __importDefault(require("./VerificationError")); const VerificationError_1 = __importDefault(require("./VerificationError"));
exports.TRANSACTION_RETRIEVED = "TRANSACTION_RETRIEVED"; exports.TRANSACTION_RETRIEVED = "TRANSACTION_RETRIEVED";
exports.MERKLE_TREE_VERIFIED = "MERKLE_TREE_VERIFIED"; exports.MERKLE_TREE_VERIFIED = "MERKLE_TREE_VERIFIED";
exports.STATUS_BATCH_VERIFIED = "STATUS_BATCH_VERIFIED";
exports.BLOCK_DATA_RETRIEVED = "BLOCK_DATA_RETRIEVED"; exports.BLOCK_DATA_RETRIEVED = "BLOCK_DATA_RETRIEVED";
/**
* Legacy verification method. Support for older statuses
*/
const VERIFICATION_METHOD_MERKLE_TREE = "VERIFICATION_METHOD_MERKLE_TREE";
const VERIFICATION_METHOD_SHA256_BATCH = "VERIFICATION_METHOD_SHA256_BATCH";
const STATUSES_BATCH_NAME_PREFIX = "batch";
const STATUSES_MERKLE_TREE_NAME_PREFIX = "bmt";
class VerificationService extends EventEmitter { class VerificationService extends EventEmitter {
constructor(cdnUrl, cdnBucket, aeternityNodeUrls, aeternityCompilerUrls, aeternityContractBytecode) { constructor(cdnUrl, cdnBucket, aeternityNodeUrls, aeternityCompilerUrls, aeternityContractBytecode) {
super(); super();
...@@ -78,20 +86,34 @@ class VerificationService extends EventEmitter { ...@@ -78,20 +86,34 @@ class VerificationService extends EventEmitter {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let verificationError; let verificationError;
let blockData; let blockData;
let merkleTreeDetails; let batchVerificationDetails;
let txData; let txData;
try { try {
txData = yield this.getTransaction(statusData.transactionHash); txData = yield this.getTransaction(statusData.transactionHash);
this.emit(exports.TRANSACTION_RETRIEVED, txData); this.emit(exports.TRANSACTION_RETRIEVED, txData);
const merkleTree = yield this.getMerkleTreeForTransaction(txData); const statusBatchData = yield this.getStatusBatchDataForTx(txData);
const statusVerified = yield this.verifyMerkleTreeNode(statusData.statusRaw, merkleTree); const { verificationMethod, items: batchItems, hash: batchHash, } = statusBatchData;
merkleTreeDetails = { let statusVerified;
let statusPosition = -1;
if (verificationMethod === VERIFICATION_METHOD_MERKLE_TREE) {
({
verified: statusVerified,
statusPosition,
} = yield this.verifyStatusesMerkleTree(statusData.statusRaw, statusBatchData));
}
else if (verificationMethod === VERIFICATION_METHOD_SHA256_BATCH) {
({
verified: statusVerified,
statusPosition,
} = yield this.verifyStatusSHA256Batch(statusData.statusRaw, statusBatchData));
}
batchVerificationDetails = {
verified: statusVerified, verified: statusVerified,
treeSize: merkleTree.nodes.length, batchSize: batchItems.length,
statusPosition: merkleTree.nodes.indexOf(statusData.statusRaw) + 1, statusPosition,
rootHash: merkleTree.rootHash, batchHash,
}; };
this.emit(exports.MERKLE_TREE_VERIFIED, merkleTreeDetails); this.emit(exports.STATUS_BATCH_VERIFIED, batchVerificationDetails);
blockData = yield this.getBlockData(txData.blockHeight); blockData = yield this.getBlockData(txData.blockHeight);
this.emit(exports.BLOCK_DATA_RETRIEVED, blockData); this.emit(exports.BLOCK_DATA_RETRIEVED, blockData);
} }
...@@ -105,38 +127,94 @@ class VerificationService extends EventEmitter { ...@@ -105,38 +127,94 @@ class VerificationService extends EventEmitter {
} }
return { return {
statusData, statusData,
merkleTreeDetails, batchVerificationDetails,
blockData, blockData,
verificationError, verificationError,
txData, txData,
}; };
}); });
} }
getMerkleTreeForTransaction(txData) { getStatusBatchDataForTx(txData) {
var _a; var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let verificationMethod;
let batchItems;
let decodedCallData;
try { try {
const txMerkleTreeData = yield this._aeternityService.decodeContractCallData((_a = txData.tx) === null || _a === void 0 ? void 0 : _a.callData); decodedCallData = yield this._aeternityService.decodeContractCallData((_a = txData.tx) === null || _a === void 0 ? void 0 : _a.callData);
const merkleTreeNodes = yield this._cloudflareService.getMerkleTree(txMerkleTreeData.merkleeTreeFileName);
return {
nodes: merkleTreeNodes,
rootHash: txMerkleTreeData.rootNodeHash,
};
} }
catch (e) { catch (e) {
this.catchVerificationError(e, `Error extracting Merkle tree from transaction.`); this.catchVerificationError(e, `Error decoding contract call data`);
}
const { key: batchFilename, value } = decodedCallData;
const batchHash = value;
const lowerCaseFilename = batchFilename.toLowerCase();
if (lowerCaseFilename.startsWith(STATUSES_MERKLE_TREE_NAME_PREFIX)) {
verificationMethod = VERIFICATION_METHOD_MERKLE_TREE;
}
else if (lowerCaseFilename.startsWith(STATUSES_BATCH_NAME_PREFIX)) {
verificationMethod = VERIFICATION_METHOD_SHA256_BATCH;
}
else {
// Unrecognized verification method
throw new VerificationError_1.default(`No corresponding verification method found for batch "${batchFilename}"`);
} }
try {
if (verificationMethod === VERIFICATION_METHOD_MERKLE_TREE) {
batchItems = yield this._cloudflareService.getMerkleTree(batchFilename);
}
else if (verificationMethod === VERIFICATION_METHOD_SHA256_BATCH) {
batchItems = (yield this._cloudflareService.fetchFile(batchFilename));
}
}
catch (e) {
this.catchVerificationError(e, `Error obtaining batch of statuses for verification.`);
}
return {
verificationMethod,
hash: batchHash,
items: batchItems,
};
});
}
verifyStatusSHA256Batch(status, statusesBatchData) {
return __awaiter(this, void 0, void 0, function* () {
const statusSha256 = index_1.arrayBufferToBase64(yield index_1.CryptoService.SHA256(status, "base64"));
let verified = false;
const statusPosition = statusesBatchData.items.indexOf(statusSha256) + 1;
if (statusPosition > 0) {
const bytesToHash = [];
let hashesTotalByteLength = 0;
for (const statusHash of statusesBatchData.items) {
const statusHashBytes = index_1.base64ToArrayBuffer(statusHash);
hashesTotalByteLength += statusHashBytes.byteLength;
bytesToHash.push(new Uint8Array(statusHashBytes));
}
const batchBytes = new Uint8Array(hashesTotalByteLength);
let bytesOffset = 0;
bytesToHash.forEach((statusHashBytes) => {
batchBytes.set(statusHashBytes, bytesOffset);
bytesOffset += statusHashBytes.byteLength;
});
const batchHash = index_1.arrayBufferToBase64(yield index_1.CryptoService.SHA256(batchBytes));
verified = batchHash === statusesBatchData.hash;
}
return {
verified,
statusPosition,
};
}); });
} }
verifyMerkleTreeNode(nodeToVerify, merkleTree) { verifyStatusesMerkleTree(nodeToVerify, merkleTree) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const leaves = yield Promise.all(merkleTree.nodes.map((x) => __awaiter(this, void 0, void 0, function* () { return Buffer.from(yield index_1.CryptoService.SHA256(x)); }))); const leaves = yield Promise.all(merkleTree.items.map((x) => __awaiter(this, void 0, void 0, function* () { return Buffer.from(yield index_1.CryptoService.SHA256(x)); })));
const rootHash = (yield index_1.getMerkleTreeRootHash(leaves)).toString("base64"); const rootHash = (yield index_1.getMerkleTreeRootHash(leaves)).toString("base64");
const verified = rootHash === merkleTree.rootHash; const verified = rootHash === merkleTree.hash;
if (!verified) { if (!verified) {
throw new VerificationError_1.default(`Merkle tree not verified.`); throw new VerificationError_1.default(`Merkle tree not verified.`);
} }
return verified; const statusPosition = merkleTree.items.indexOf(nodeToVerify) + 1;
return { verified, statusPosition };
}); });
} }
catchVerificationError(error, verificationFailureMessage) { catchVerificationError(error, verificationFailureMessage) {
......
...@@ -29,21 +29,22 @@ export interface StatusData { ...@@ -29,21 +29,22 @@ export interface StatusData {
merkleTreeId: string; merkleTreeId: string;
transactionHash: string; transactionHash: string;
} }
export interface MerkleTreeVerificationDetails { export interface BatchVerificationDetails {
statusPosition: number; statusPosition: number;
treeSize: number; batchSize: number;
verified: boolean; verified: boolean;
rootHash: string; batchHash: string;
} }
export interface MerkleTree { export interface StatusesBatchData {
rootHash: string; verificationMethod: string;
nodes: Array<string>; hash: string;
items: Array<string>;
} }
export interface VerificationData { export interface VerificationData {
statusData?: StatusData; statusData?: StatusData;
blockData?: BlockData; blockData?: BlockData;
txData?: TxData; txData?: TxData;
merkleTreeDetails?: MerkleTreeVerificationDetails; batchVerificationDetails?: BatchVerificationDetails;
verificationError?: VerificationError; verificationError?: VerificationError;
} }
export interface BlockData { export interface BlockData {
...@@ -58,9 +59,9 @@ export interface TxData { ...@@ -58,9 +59,9 @@ export interface TxData {
callData: string; callData: string;
}; };
} }
export interface TxMerkleTreeData { export interface DecodedCallData {
merkleeTreeFileName: string; key: string;
rootNodeHash: string; value: string;
} }
export interface AttachmentSignature { export interface AttachmentSignature {
value: string; value: string;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment