momiji.js 7.67 KB
Newer Older
Johnny's avatar
Johnny committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Publisher = void 0;
const interfaces = __importStar(require("../lib/utils/interfaces"));
const rover_1 = require("../lib/utils/rover");
const batchBuilder_1 = require("../lib/utils/batchBuilder");
class Publisher {
    constructor(config, signer) {
        this.proofsStarted = 0;
        this.proofsCompleted = 0;
        this.initialize = async () => {
            await this.prover.initialize();
            await this.batchBuilder.initialize();
            this.provingQueue = this.batchBuilder.initBatchCircuitInputs();
            // Start watching for events
            this.prover.momiji.contract.on('TransactionSent', async (res) => {
                let tx = this.prover.resultToTransaction(res);
                console.log(`🟢 Transaction enqueued: ${tx.tx_id}.`);
                // This proves 1 tx and updates prover.status
                this.queueToProve(tx);
            });
            this.prover.momiji.contract.on('BatchPublish', async (event) => {
                // Someone else published (or we did? - check)
                //console.log(`🎯 Batch published: ${event}.`);
                // reset proving queue and mempool.queue
                this.resetCounters();
                this.batchBuilder.status = {
                    txs_hash: "",
                    tx_ids: [],
                    proof: "",
                    aggregation_object: []
                };
                await this.batchBuilder.fetchQueue();
                await this.batchBuilder.refreshState();
                this.provingQueue = this.batchBuilder.initBatchCircuitInputs();
                this.printRoot();
                this.publishTimeout = setInterval(this.publish, Number(process.env.TIMEOUT)); // every minute, publish (if there's a batch to publish)
                return;
            });
            console.log(`⌚ Event watcher started.`);
            this.printRoot();
            console.log(`🟣 Welcome to the Offshift Prover Network.`);
            this.signer && console.log(`🔑 Publisher Address: ${await this.signer.getAddress()}.`);
            const _thisQueue = await this.prover.fetchQueue();
            this.publishTimeout = setInterval(this.publish, Number(process.env.TIMEOUT)); // every minute, publish (if there's a batch to publish)
            // for each item in queue, add it to the proving queue
            if (_thisQueue.length > 0) {
                console.log(`📃 Fetched ${_thisQueue.length} unpublished transactions from queue.`);
                for (let i = 0; i < _thisQueue.length; i++) {
                    this.queueToProve(_thisQueue[i]); // queue every queued tx for proving
                }
            }
        };
        this.printRoot = () => console.log(`🌳 Current State Root: ${this.prover.stateRoot.value}`);
        // compare this.batchBuilder.status to the state contract and see if the last non-zero tx_id matches
        this.stillProving = () => this.proofsCompleted < this.proofsStarted;
        this.queueToProve = async (tx) => {
            this.proofsStarted++;
            this.provingQueue = this.provingQueue.then(async (data) => {
                return this.prove(data, tx);
            }).catch(async (e) => {
                console.log(e);
                console.log(`❌ An error occured! Batch proofs reset.`); // TODO: Gracefully restart proving with the queue when this happens
                return await this.batchBuilder.initBatchCircuitInputs(); // reset the batch
            });
        };
        this.prove = async (d, tx) => {
            return new Promise(async (res, rej) => {
                // check to make sure a batch wasn't published during the time, and kick out if it was
                if (await this.prover.momiji.contract.merkleRoot() != this.prover.stateRoot.value) {
                    // make batchBuilder dump the queue and start over
                    this.batchBuilder.clearQueue();
                    this.provingQueue = this.batchBuilder.initBatchCircuitInputs();
                    await this.provingQueue;
                    this.resetCounters();
                    return res(this.provingQueue);
                }
                console.log(`⏳ Proving ${tx.tx_id}.`);
                const proof = await this.batchBuilder.addProvenTransaction(tx, d);
                console.log(`✔️ Proved ${tx.tx_id}.`);
                this.proofsCompleted++;
                return res(proof);
            });
        };
        this.publish = async () => {
            if (this.stillProving()) {
                //console.log(`⚠️ Still proving...`)
                return; // If we're still proving something, don't publish right now
            }
            if (this.batchBuilder.status.proof === "") {
                //console.log(`⚠️ No batch to publish...`)
                return;
            }
            if (this.proofsCompleted == 0)
                return; // nothing to submit
            clearInterval(this.publishTimeout);
            console.log(`🗞️ Publishing batch...`);
            // Publish to chain
            const merkleRoot = await this.prover.momiji.contract.merkleRoot();
            const filter = this.prover.momiji.contract.filters.TransactionSent(null, merkleRoot, null);
            const txEvents = await this.prover.momiji.contract.queryFilter(filter);
            const txContractInputs = txEvents.map(e => this.prover.resultToTransaction(this.prover.toResult(e)));
            let finals = await this.batchBuilder.createFinalPublisherInputs({
                ...(await this.provingQueue),
                proof: this.batchBuilder.status.proof,
                aggregation_object: this.batchBuilder.status.aggregation_object,
                transactions: txContractInputs
            });
            const _contract = new interfaces.Contract(this.prover.config.stateContract.address, JSON.stringify(this.prover.config.stateContract.abi), this.signer);
            await _contract.publish(finals.proof, finals.aggregation_object, finals.batch_calldata)
                .then((tx) => console.log(`📡 Batch published: ${tx.hash}.`), (error) => console.log(error.message));
            return;
        };
        this.resetCounters = () => {
            // reset proof counters
            this.proofsStarted = 0;
            this.proofsCompleted = 0;
        };
        this.publishTimeout = {};
        this.provingQueue = {};
        this.prover = new rover_1.Prover(config);
        this.batchBuilder = new batchBuilder_1.BatchBuilder(config);
        this.signer = signer;
        if (!this.signer)
            throw new Error("momiji.constructor: Signer is undefined, but is required");
        const address = config.stateContract.address;
        if (!address)
            throw new Error("momiji.constructor: State Contract address is not set, but is required.");
    }
}
exports.Publisher = Publisher;