diff --git a/.env.example b/.env.example index c6429ef43f0772af18279a0e30d3b509822cb830..123ff3c4cee7b585a17cc058c8a818f8a91b867e 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ -# Private key used for publishing -RPC_URL= +# RPC that supports eth_getLogs +RPC_URL=https://rpc.mevblocker.io/ +# Private key # If private key is filled, it will be used for both deposits and publishing -- be careful if you are doing both PRIVATE_KEY= THREADS=0 # Use all available threads @@ -23,4 +24,7 @@ RELAYS= # Nostr Relay APIs ("Seed" nodes) -- expects an array returned MAX_RELAYS=8 -SEEDS=https://api.nostr.watch/v1/online \ No newline at end of file +SEEDS=https://api.nostr.watch/v1/online + +# Your public ip in case you are using a tunneling service +PUBLIC_IP= \ No newline at end of file diff --git a/.gitignore b/.gitignore index bfccd05f9a9599b603b1a0006d2c11df4d734a80..23b5b2c4591c90b34da174ce14fb4e53910139b1 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ yarn-error.log* # Ignore OS generated files .DS_Store Thumbs.db +package-lock.json \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..81eda3e9dcf22720de44d8abac5996001e4a0277 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "momiji-helpers"] + path = momiji-helpers + url = http://open.offshift.io/greybeard/momiji-helpers.git diff --git a/Dockerfile b/Dockerfile index ef40147b276e315bbf7481c752d0da340df5e769..cedb60bf7b560db63ce3b64e82ae72c85ba94ba2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.0 +FROM node:20.2 RUN mkdir /code && mkdir /code/node WORKDIR /code/node RUN wget https://github.com/noir-lang/noir/releases/download/v0.28.0/nargo-x86_64-unknown-linux-gnu.tar.gz @@ -9,5 +9,9 @@ COPY . . RUN npm install RUN cd ./momiji-helpers/ && npm install WORKDIR /code/node - -ENTRYPOINT [ "npm", "start"] +RUN apt update && apt install -y tor libc++-dev +RUN echo "HiddenServiceDir /var/lib/tor/hidden_service/" >> /etc/tor/torrc +RUN echo "HiddenServicePort 80 127.0.0.1:5150" >> /etc/tor/torrc +RUN service tor start +RUN update-rc.d tor enable +ENTRYPOINT [ "npm", "start" ] diff --git a/README.md b/README.md index 51603d8d5883886237d08575f152d925f187735d..8af1377eef75a4e021e50522fbf7a791d5733be7 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ sudo docker build -t xftnode . for running ```sh -sudo docker run -it xftnode +sudo docker run -p 5150:5150 -it xftnode ``` ### npm usage - Install noirup, the Nargo package manager diff --git a/momiji-helpers/utils/peering.ts b/momiji-helpers/utils/peering.ts index c1445f288cf4a7287f8a06169121fea4bd8c0b21..8a33b4133b8fe953143175c8ff606d6d8cc95e85 100644 --- a/momiji-helpers/utils/peering.ts +++ b/momiji-helpers/utils/peering.ts @@ -1,199 +1,115 @@ // @ts-ignore import * as types from './types'; import { TransactionBuilder } from "./transactionBuilder"; -import { createLibp2p } from 'libp2p' -import { webSockets } from '@libp2p/websockets' -import * as filters from '@libp2p/websockets/filters' +// import { createLibp2p } from 'libp2p' +// import * as filters from '@libp2p/websockets/filters' import { finalizeEvent, verifyEvent, setNostrWasm, VerifiedEvent, Event } from 'nostr-tools/wasm' import { SimplePool } from 'nostr-tools/pool' -import { gossipsub } from '@chainsafe/libp2p-gossipsub' -import { noise } from '@chainsafe/libp2p-noise' -import { yamux } from '@chainsafe/libp2p-yamux' -import { mplex } from '@libp2p/mplex' -import { identify } from "@libp2p/identify" -import { dcutr } from "@libp2p/dcutr" +// import { gossipsub } from '@chainsafe/libp2p-gossipsub' +// import { noise } from '@chainsafe/libp2p-noise' +// import { yamux } from '@chainsafe/libp2p-yamux' +// import { mplex } from '@libp2p/mplex' +// import { identify } from "@libp2p/identify" +// import { dcutr } from "@libp2p/dcutr" import { getPublicKey } from 'nostr-tools/pure'; import { initNostrWasm } from 'nostr-wasm' -import { multiaddr } from '@multiformats/multiaddr' +// import { multiaddr } from '@multiformats/multiaddr' import { useWebSocketImplementation } from 'nostr-tools/pool' -import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery' import WebSocket from 'ws' +import { Block } from 'ethers'; if (typeof global === "object") useWebSocketImplementation(WebSocket); export class Peering extends TransactionBuilder { - p2p: any | undefined; - pool: SimplePool = new SimplePool(); + // p2p: any | undefined; + pool: SimplePool = new SimplePool(); relays: string[]; seeds: string[]; rebroadcast: any | undefined; - callback: Function; + // callback: Function; constructor(_config: types.GlobalConfig) { super(_config); - if (!this.config.gossip) throw new Error("Gossip config not found"); + // if (!this.config.gossip) throw new Error("Gossip config not found"); this.pool = new SimplePool(); this.relays = this.config.gossip!.relays; this.seeds = this.config.gossip!.seeds; - this.callback = () => true; + // this.callback = () => true; } - postEvent = async (_sk: Uint8Array, _timestamp: number) => Promise.any(this.pool.publish(this.relays, finalizeEvent({ + postEvent = async (_sk: Uint8Array, _timestamp: number, ips: string) => Promise.any(this.pool.publish(this.relays, finalizeEvent({ kind: 1, created_at: _timestamp, tags: [], - content: this.p2p.getMultiaddrs().map((addr: any) => multiaddr(addr).toString()).join(',') + content: ips }, _sk))) - postTransaction = async (_tx: types.Transaction) => { - const hexString = (await this.contracts.state.getAddress()).slice(2).padStart(64, '0'); - const matches = hexString.match(/.{1,2}/g); - const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array(); - const _pk: string = getPublicKey(skUint8Array); - if (this.p2p.services.pubsub.getSubscribers(_pk).length > 0) { - await this.p2p.services.pubsub.publish(_pk, new TextEncoder().encode(JSON.stringify(_tx))) - } else { - setTimeout(async () => await this.postTransaction(_tx), 10_000); - } - }; + // postTransaction = async (_tx: types.Transaction) => { + // const hexString = (await this.contracts.state.getAddress()).slice(2).padStart(64, '0'); + // const matches = hexString.match(/.{1,2}/g); + // const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array(); + // const _pk: string = getPublicKey(skUint8Array); + + // if (this.p2p.services.pubsub.getSubscribers(_pk).length > 0) { + // await this.p2p.services.pubsub.publish(_pk, new TextEncoder().encode(JSON.stringify(_tx))) + // } else { + // setTimeout(async () => await this.postTransaction(_tx), 10_000); + // } + // }; initializePeering = async (callback?: Function): Promise<void> => { await this.initializeTransactionBuilder(); - const hexString = (await this.contracts.state.getAddress()).slice(2).padStart(64, '0'); + const hexString = "6835ccbeddc1c1c91fed2116099592a309f98d0df34d8dc72e770df83cc3b065".padStart(64, '0'); const matches = hexString.match(/.{1,2}/g); - const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array(); + const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array() let _pk = getPublicKey(skUint8Array); - await this.fetchRelays(); await initNostrWasm().then(setNostrWasm); - const latestBlock = await this.config.provider.getBlock("latest"); - - let multiAddrs: string[] = []; - - this.p2p = await createLibp2p({ - addresses: { - listen: (typeof global === "object") ? ['/ip4/0.0.0.0/tcp/5150/ws'] : [] - }, - transports: [webSockets({ - filter: filters.all - })], - connectionEncryption: [noise()], - streamMuxers: [yamux(), mplex()], - services: { - pubsub: gossipsub(), - identify: identify(), - dcutr: dcutr() - }, - connectionGater: { - denyDialMultiaddr: () => { - return false - } - }, - peerDiscovery: [ - pubsubPeerDiscovery({ - interval: 10_000 - }) - ] - }); - - await this.p2p.start() - this.p2p.services.pubsub.subscribe(_pk) - await this.p2p.services.pubsub.start() - - this.p2p.addEventListener('connection:open', async (event: CustomEvent) => { - console.log("peering.ts - connection open", event.detail.id.toString()) - }) - this.p2p.addEventListener('connection:close', () => { - console.log("peering.ts - connection closed") - }) - this.p2p.addEventListener("peer:connect", async (event: CustomEvent) => { - console.log("peering.ts - peer connect") - }) - this.p2p.addEventListener("peer:discovery", (event: CustomEvent) => { - console.log("peering.ts - peer discovered:", event.detail.id.toString()) - }) - - if (typeof global === "object") { - try { - this.pool.subscribeMany( - this.relays.slice(0, this.config.gossip!.maxRelays), - [ - { - kinds: [1], - authors: [_pk], - since: latestBlock!.timestamp - }, - ], - { - onevent: async (event) => event.content.split(',') - .filter(addr => { - try { - multiaddr(addr); - return true; - } catch (error) { - return false; - } - }) - .forEach(async (addr) => this.addPeer(addr)), - oneose: async () => true, - onclose: async () => true, - } - ) - } catch (error) { + let ip: string + if (this.config.ip == "") { + let ips: string[] = [] + let ipServices = ["https://api.ipify.org", "https://icanhazip.com", "https://ifconfig.me/ip", "https://checkip.amazonaws.com/"] + for (let i = 0; i < ipServices.length; i++) { + let res = await fetch(ipServices[i]) + ips.push((await res.text()).trim()) } - } else { - try { - const events: Event[] = await this.pool.querySync(this.relays.slice(0, this.config.gossip!.maxRelays), - { - kinds: [1], - authors: [_pk], - since: latestBlock!.timestamp - (typeof global === 'object' ? 0 : 86400) - } - ) - console.log("peering.ts - nostr events", events) - multiAddrs = events.map(event => event.content.split(",")) - .flat(1) - .filter(addr => { - try { - multiaddr(addr); - return true; - } catch (error) { - return false; - } - }) - - for (let addr in multiAddrs) await this.addPeer(multiAddrs[addr]); - - } catch (error) { - console.log("error, peering.ts - initializePeering(), pool.querySync()", error) + let last = ips[0] + for (let i = 0; i < ips.length; i++) { + if (last === ips[i]) { + last = ips[i] + } else { + console.log("Please set your public ip in the config file") + return + } } + ip = last + }else { + ip = this.config.ip as string } - if (typeof global === 'object') { - this.rebroadcast = setInterval(async () => await this.postEvent(skUint8Array, latestBlock!.timestamp), 60 * 60 * 1000); - await this.postEvent(skUint8Array, latestBlock!.timestamp); - this.p2p.services.pubsub.addEventListener('message', (message: CustomEvent) => { - if (!(message.detail.topic == _pk) || !callback) return; - callback(JSON.parse(new TextDecoder().decode(message.detail.data))); - }) + this.rebroadcast = setInterval(async () => { + await this.postEvent(skUint8Array, latestBlock!.timestamp, ip) + }, 60 * 60 * 1000); + await this.postEvent(skUint8Array, latestBlock!.timestamp, ip); } } - addPeer = async (peerMultiaddr: string): Promise<void> => { - if (!this.p2p) { - throw new Error('P2P is not initialized'); - } - try { - await this.p2p.dial(multiaddr(peerMultiaddr)); - console.log(`Successfully connected to peer: ${peerMultiaddr}`); - } catch (error) { - } - }; + queryEvents = async (latestBlock: number) => { + const hexString = "6835ccbeddc1c1c91fed2116099592a309f98d0df34d8dc72e770df83cc3b065".padStart(64, '0'); + const matches = hexString.match(/.{1,2}/g); + const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array() + let _pk = getPublicKey(skUint8Array); + let event = await this.pool.querySync(this.relays, { + kinds: [1], + authors: [_pk], + since: latestBlock - (typeof global === 'object' ? 0 : 86400) + }) + } fetchRelays = async (refresh: boolean = false): Promise<string[]> => { if (this.seeds.length == 0 || refresh) return this.relays; for (const seed of this.seeds) { diff --git a/momiji-helpers/utils/types.ts b/momiji-helpers/utils/types.ts index 07d834de1ab2c8ecbc2cb622433d8df254c2a65c..0ec00191a166510ac71ae11da3abca26d57a4c83 100644 --- a/momiji-helpers/utils/types.ts +++ b/momiji-helpers/utils/types.ts @@ -88,6 +88,7 @@ export type GlobalConfig = { withdrawal?: string, verbose?: boolean, threads?: number, + ip?: string, provider: Provider } diff --git a/package.json b/package.json index ec843a6fc7b9fbc4897ffcc64c60c901ed2b8410..4a73936830cfacaf8e6ee8066293a8bd34493cae 100644 --- a/package.json +++ b/package.json @@ -17,27 +17,27 @@ "devDependencies": { "@types/node": "^20.10.4", "@types/validator": "^13.11.7", + "@types/ws": "^8.5.12", "dotenv": "^16.4.5", "hardhat": "^2.22.3", "nodemon": "^3.0.2", "ts-node": "^10.9.2", - "typescript": "^5.3.2" + "typescript": "^5.3.2", + "ws": "^8.18.0" }, "dependencies": { "@chainlink/contracts": "^0.8.0", - "@libp2p/bootstrap": "^10.1.0", - "@libp2p/identify": "^2.1.2", "@noir-lang/noir_js": "^0.28.0", "@nomicfoundation/hardhat-toolbox": "^5.0.0", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2", "@openzeppelin/hardhat-upgrades": "^3.2.0", - "@types/pubsub-js": "^1.8.6", + "@types/express": "^4.17.21", "@uniswap/v3-periphery": "^1.4.4", + "cors": "^2.8.5", "ethers": "^6.13.0", - "fonstr": "^0.0.11", + "express": "^4.19.2", "indexeddbshim": "^15.0.0", - "memorelay": "^2.0.4", "reflect-metadata": "^0.1.14", "sequelize": "^6.35.1", "sequelize-typescript": "^2.1.6" diff --git a/src/index.ts b/src/index.ts index 77c8106a71aa07a934044328253396f4a3addaf3..454c9d3e2310a4e69c65aaeda5ea1376a34e63a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import { Publisher } from "./modules/momiji"; import * as types from "../momiji-helpers/utils/types"; import { ethers } from "ethers"; import 'dotenv/config'; +import { WebSocketServer } from "ws"; const main = async () => { let _globalConfig: types.GlobalConfig = { @@ -18,10 +19,50 @@ const main = async () => { verbose: (process.env.VERBOSE) ? (process.env.VERBOSE.toLowerCase() === "true") : false, profit: (process.env.PROFIT) ? parseInt(process.env.PROFIT) : 0 }; - + let publisher = new Publisher(_globalConfig); await publisher.initializePublisher(); + const wss = new WebSocketServer({ + host: '0.0.0.0', + port: 5150 + }); + wss.on('connection', function connection(ws) { + ws.on('error', console.error); + ws.on('message', async function message(data) { + if (publisher.queue.length >= 14){ + ws.send('{status:"error", msg:"Prover queue is full"}') + return + } + const valid = await publisher.newTransactionReceivedMultiple(JSON.parse(data.toString())); + if (valid){ + ws.send('{status:"ok", msg:"Proof added to queue"}'); + } else { + ws.send('{status:"error", msg:"Invalid proof"}'); + } + }); + }); + let queuing = false + const wenQueue = async() => { + if (!queuing && publisher.queue.length > 0) { + queuing = true; + await publisher.proveMultiple() + queuing = false; + } + + } + + let publishing = false + const wenPublish = async () => { + if (!publishing && !queuing && publisher.batch.length > 0) { + publishing = true + await publisher.publishSingle() + publishing = false + } + } + + setInterval(wenQueue, 1e4) + setInterval(wenPublish, 1e5) } console.log(`💫 Starting publisher...`) diff --git a/src/modules/momiji.ts b/src/modules/momiji.ts index cff67d97cc06007a785f2498796dfa8314ce69aa..654c9ad55901a48806785c8be68bbabb640b9e58 100644 --- a/src/modules/momiji.ts +++ b/src/modules/momiji.ts @@ -4,19 +4,22 @@ import { keccak_tx } from '../../momiji-helpers/circuits/helpers/codegen/keccak_ import { tx_as_hash } from '../../momiji-helpers/circuits/helpers/codegen/tx_as_hash'; export class Publisher extends BatchBuilder { - private publishTimeout: NodeJS.Timeout; + // private publishTimeout: NodeJS.Timeout; private signer: types.EthersSigner | undefined; private mempool: types.Mempool = {}; private provingQueue: Promise<types.RecursionInputs[]> = Promise.resolve([]); - private batch: types.RecursionInputs[] = []; + public batch: types.RecursionInputs[] = []; private confirmed: types.Confirmed = {}; private publishing: boolean = false; private proving: boolean = false; private sweeping: boolean = false; + public queue: types.Transaction[] = []; + public accumulator: string = types.ZERO_VALUE; + private contractPublish: types.ContractPublish = {} as types.ContractPublish; constructor(config: types.GlobalConfig) { super(config); - this.publishTimeout = setInterval(this.publish, 60 * 1000); + // this.publishTimeout = setInterval(this.publish, 60 * 1000); this.mempool = {}; this.confirmed = {}; } @@ -60,12 +63,46 @@ export class Publisher extends BatchBuilder { } let txid = await keccak_tx(tx.public_inputs); - let _verified = await this.addToMempool(tx, txid) if (_verified) this.queueToProve(tx) else console.log(`🔴 Transaction rejected -- failed to verify: ${txid}`); } + async newTransactionReceivedSingle(tx: types.Transaction): Promise<void> { + let txid = await keccak_tx(tx.public_inputs); + let _verified = await this.addToMempool(tx, txid) + if (_verified) { + await this.proveSingle(tx); + } + else console.log(`🔴 Transaction rejected -- failed to verify: ${txid}`); + } + + async newTransactionReceivedMultiple(tx: types.Transaction): Promise<boolean> { + let txid = await keccak_tx(tx.public_inputs); + let _verified = await this.addToMempool(tx, txid) + if (_verified) { + this.queue.push(tx) + return true + } + else { + console.log(`🔴 Transaction rejected -- failed to verify: ${txid}`); + return false + }; + } + + async proveMultiple(): Promise<void> { + for (let i = 0; i < this.queue.length; i ++) { + if (this.batch.length >= 15) { + console.log(`âš ï¸ Batch is full. Cannot prove additional transactions.`) + this.queue = this.queue.slice(i) + return + } + this.batch = await this.proveSingle(this.queue[i]) + } + this.queue = [] + return + } + async setupListeners(): Promise<void> { if (!this.contracts) await this.initializePublisher(); if (!this.contracts) return; @@ -104,7 +141,8 @@ export class Publisher extends BatchBuilder { this.contracts.state.on(this.contracts.state.filters.BatchPublish(undefined, undefined, undefined, undefined, undefined), async (event: any) => { console.log(`🎯 Batch published: ${event}.`); - this.provingQueue = Promise.resolve([]); + // this.provingQueue = Promise.resolve([]); + this.queue = [] this.batch = []; this.printRoot(); return; @@ -131,7 +169,7 @@ export class Publisher extends BatchBuilder { const withdrawals: types.WithdrawalSwap[] = await this._generateWithdrawals([{ amount: (new types.NoirFr(withdrawalAmount)).toString(), recipient: types.toFixedHex(1, true), - swap_percentage: 100 + swap_percentage: 100 }], 1) const proverTx: types.Transaction = await this._generateTransactionProof(utxo_commitments, utxo_encrypted, withdrawals); return proverTx; @@ -149,7 +187,13 @@ export class Publisher extends BatchBuilder { }) const withdrawalAmount: bigint = utxo_commitments.map(utxo => BigInt(utxo.amount)).reduce((prev, curr) => prev + curr) const withdrawalAmountEther: bigint = await this._getEtherFromXFT(withdrawalAmount) - const gasEstimate: bigint = await this.contracts.state.publish.estimateGas(contractPublish.proof, contractPublish.batch) + let gasEstimate: bigint + try { + gasEstimate = await this.contracts.state.publish.estimateGas(contractPublish.proof, contractPublish.batch) + } catch { + this.batch.pop() + return false + } const maxFeePerGas: bigint = await this.config.provider.getFeeData().then(feeData => feeData.maxFeePerGas as bigint) const maxFeePerGasAdjusted: bigint = (this.config.profit) ? maxFeePerGas + BigInt(this.config.profit * 1e9) : maxFeePerGas const txFeeEstimate: bigint = gasEstimate * maxFeePerGasAdjusted @@ -189,6 +233,15 @@ export class Publisher extends BatchBuilder { }) } + async proveSingle(tx: types.Transaction): Promise<types.RecursionInputs[]> { + this.proving = true; + console.log(`â³ Proving ${await keccak_tx(tx.public_inputs)}.`) + const proof = await this.rollupTransaction(this.batch, tx); + console.log(`âœ”ï¸ Proved ${await keccak_tx(tx.public_inputs)}.`) + this.proving = false + return proof + } + publish = async () => { if (this.publishing) return; @@ -196,10 +249,9 @@ export class Publisher extends BatchBuilder { if (this.sweeping) return; if (this.batch.length === 0) return; - await this.provingQueue; - const contractPublish: types.ContractPublish = await this.preparePublish(this.batch) - - if (!(await this.publishReady(this.batch, contractPublish))) return; + // await this.provingQueue; + // const contractPublish: types.ContractPublish = await this.preparePublish(this.batch) + // if (!(await this.publishReady(this.batch, contractPublish))) return; console.log(`🧹 Sweeping prover fees...`) this.sweeping = true @@ -213,7 +265,7 @@ export class Publisher extends BatchBuilder { console.log(`ðŸ—žï¸ Publishing batch...`) const contractPublishProver: types.ContractPublish = await this.preparePublish(this.batch) - clearInterval(this.publishTimeout); + // clearInterval(this.publishTimeout); await this.contracts.state.publish(contractPublishProver.proof, contractPublishProver.batch) .then((tx: any) => console.log(`📡 Batch published: ${tx.hash}.`), (error: any) => console.log(error.message)); @@ -221,7 +273,48 @@ export class Publisher extends BatchBuilder { this.provingQueue = Promise.resolve([]); this.batch = []; - this.publishTimeout = setInterval(this.publish, 60 * 1000); + // this.publishTimeout = setInterval(this.publish, 60 * 1000); + this.publishing = false; + + return; + } + + async publishSingle() { + + if (this.publishing) return; + if (this.proving) return; + if (this.batch.length === 0) return; + + // If the accumulator has changed since the last pass + // ie. a new transaction has been added to the batch + if (this.accumulator !== this.batch[this.batch.length - 1].accumulator) { + console.log(`📠Preparing batch publish`) + this.proving = true + this.contractPublish = await this.preparePublish(this.batch) + this.accumulator = this.batch[this.batch.length - 1].accumulator + this.proving = false + return; + } + if (!(await this.publishReady(this.batch, this.contractPublish))) return; + + this.publishing = true + console.log(`🧹 Sweeping prover fees...`) + const transactions: types.Transaction[] = this.batch.filter(tx => tx.transaction !== undefined).map(tx => tx.transaction as types.Transaction) + const proverTx: types.Transaction = await this.sweepProfit(transactions) + const proverBatch: types.RecursionInputs[] = await this.proveSingle(proverTx) + const contractPublishProver: types.ContractPublish = await this.preparePublish(proverBatch) + + console.log(`ðŸ—žï¸ Publishing batch...`) + await this.contracts.state.publish(contractPublishProver.proof, contractPublishProver.batch) + .then((tx: any) => console.log(`📡 Batch published: ${tx.hash}.`), (error: any) => console.log(error.message)); + + this.queue = []; + this.batch = []; + this.accumulator = types.ZERO_VALUE; + this.contractPublish = {} as types.ContractPublish + + if (Object.keys(this.mempool).length >= 1000) this.mempool = {} + if (Object.keys(this.confirmed).length >= 1000) this.confirmed = {} this.publishing = false; return;