"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Entry = exports.MerkleArchive = exports.ArrayBufferReader = void 0;
const directoryContents_1 = require("./directoryContents");
const fileContents_1 = require("./fileContents");
const ed25519_1 = require("@noble/curves/ed25519");
const varint_1 = require("./varint");
const __1 = require("..");
const maxVarstringLen = 1024 * 1024;
class ArrayBufferReader {
    constructor(buffer) {
        this.buffer = buffer;
    }
    next(length) {
        const res = this.buffer.slice(0, length);
        this.buffer = this.buffer.slice(length);
        return res;
    }
    nextU64() {
        return new DataView(this.next(8)).getBigUint64(0);
    }
    nextVarint() {
        const p = new varint_1.VarIntProcessor();
        while (!p.finished()) {
            p.push(new Uint8Array(this.buffer.slice(0, 1))[0]);
            this.buffer = this.buffer.slice(1);
        }
        const res = p.decode();
        if (res === null) {
            throw new Error('Reached EOF');
        }
        return res;
    }
    nextVarstring() {
        const len = Math.min(this.nextVarint(), maxVarstringLen);
        return new TextDecoder().decode(this.next(len));
    }
}
exports.ArrayBufferReader = ArrayBufferReader;
class MerkleArchive {
    constructor(signer, contents) {
        this.signer = signer;
        this.contents = contents;
    }
    static async deserialize(source, context, header, commitment) {
        const pubkey = new Uint8Array(header.next(32));
        const signature = new Uint8Array(header.next(64));
        const sighash = new Uint8Array(header.next(32));
        const rootMaxSizeBytes = header.next(8);
        const maxSize = new DataView(rootMaxSizeBytes).getBigUint64(0);
        if (!ed25519_1.ed25519ph.verify(signature, new Uint8Array(await new Blob([sighash, rootMaxSizeBytes]).arrayBuffer()), pubkey, {
            context: new TextEncoder().encode(context),
            zip215: true,
        })) {
            throw new Error('signature verification failed');
        }
        if (commitment) {
            if (!(0, __1.compare)(sighash, new Uint8Array(Buffer.from(commitment.rootSighash, 'base64').buffer))) {
                throw new Error('merkle root mismatch');
            }
            if (maxSize > commitment.rootMaxsize) {
                throw new Error('root directory max size too large');
            }
        }
        else if (maxSize > 1024 * 1024) {
            throw new Error('root directory max size over 1MiB, cancelling download in case of DOS attack');
        }
        const contents = await directoryContents_1.DirectoryContents.deserialize(source, header, sighash, maxSize);
        return new MerkleArchive({
            pubkey,
            signature,
            maxSize,
            context,
        }, contents);
    }
}
exports.MerkleArchive = MerkleArchive;
MerkleArchive.headerSize = 32 + // pubkey
    64 + // signature
    32 + // sighash
    8 + // size
    directoryContents_1.DirectoryContents.headerSize;
class Entry {
    constructor(hash, size, contents) {
        this.hash = hash;
        this.size = size;
        this.contents = contents;
    }
    static async deserialize(source, header) {
        const hash = new Uint8Array(header.next(32));
        const size = header.nextU64();
        const contents = await deserializeEntryContents(source, header, hash, size);
        return new Entry(new Uint8Array(hash), size, contents);
    }
    async verifiedFileContents() {
        if (!this.contents) {
            throw new Error('file is missing from archive');
        }
        if (!(this.contents instanceof fileContents_1.FileContents)) {
            throw new Error('is not a regular file');
        }
        return this.contents.verified(this.hash);
    }
}
exports.Entry = Entry;
async function deserializeEntryContents(source, header, hash, size) {
    const typeId = new Uint8Array(header.next(1))[0];
    switch (typeId) {
        case 0:
            return null;
        case 1:
            return fileContents_1.FileContents.deserialize(source, header, size);
        case 2:
            return directoryContents_1.DirectoryContents.deserialize(source, header, hash, size);
        default:
            throw new Error(`Unknown type id ${typeId} found in MerkleArchive`);
    }
}
//# sourceMappingURL=index.js.map