Commit f813ab6b authored by John Doe's avatar John Doe
Browse files

🟣

parent 671a85fd
# Private key used for publishing # RPC that supports eth_getLogs
RPC_URL= 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 # If private key is filled, it will be used for both deposits and publishing -- be careful if you are doing both
PRIVATE_KEY= PRIVATE_KEY=
THREADS=0 # Use all available threads THREADS=0 # Use all available threads
...@@ -23,4 +24,7 @@ RELAYS= ...@@ -23,4 +24,7 @@ RELAYS=
# Nostr Relay APIs ("Seed" nodes) -- expects an array returned # Nostr Relay APIs ("Seed" nodes) -- expects an array returned
MAX_RELAYS=8 MAX_RELAYS=8
SEEDS=https://api.nostr.watch/v1/online SEEDS=https://api.nostr.watch/v1/online
\ No newline at end of file
# Your public ip in case you are using a tunneling service
PUBLIC_IP=
\ No newline at end of file
...@@ -24,3 +24,4 @@ yarn-error.log* ...@@ -24,3 +24,4 @@ yarn-error.log*
# Ignore OS generated files # Ignore OS generated files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
package-lock.json
\ No newline at end of file
[submodule "momiji-helpers"]
path = momiji-helpers
url = http://open.offshift.io/greybeard/momiji-helpers.git
FROM node:20.0 FROM node:20.2
RUN mkdir /code && mkdir /code/node RUN mkdir /code && mkdir /code/node
WORKDIR /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 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 . . ...@@ -9,5 +9,9 @@ COPY . .
RUN npm install RUN npm install
RUN cd ./momiji-helpers/ && npm install RUN cd ./momiji-helpers/ && npm install
WORKDIR /code/node WORKDIR /code/node
RUN apt update && apt install -y tor libc++-dev
ENTRYPOINT [ "npm", "start"] 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" ]
...@@ -24,7 +24,7 @@ sudo docker build -t xftnode . ...@@ -24,7 +24,7 @@ sudo docker build -t xftnode .
for running for running
```sh ```sh
sudo docker run -it xftnode sudo docker run -p 5150:5150 -it xftnode
``` ```
### npm usage ### npm usage
- Install noirup, the Nargo package manager - Install noirup, the Nargo package manager
......
// @ts-ignore // @ts-ignore
import * as types from './types'; import * as types from './types';
import { TransactionBuilder } from "./transactionBuilder"; import { TransactionBuilder } from "./transactionBuilder";
import { createLibp2p } from 'libp2p' // import { createLibp2p } from 'libp2p'
import { webSockets } from '@libp2p/websockets' // import * as filters from '@libp2p/websockets/filters'
import * as filters from '@libp2p/websockets/filters'
import { finalizeEvent, verifyEvent, setNostrWasm, VerifiedEvent, Event } from 'nostr-tools/wasm' import { finalizeEvent, verifyEvent, setNostrWasm, VerifiedEvent, Event } from 'nostr-tools/wasm'
import { SimplePool } from 'nostr-tools/pool' import { SimplePool } from 'nostr-tools/pool'
import { gossipsub } from '@chainsafe/libp2p-gossipsub' // import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { noise } from '@chainsafe/libp2p-noise' // import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux' // import { yamux } from '@chainsafe/libp2p-yamux'
import { mplex } from '@libp2p/mplex' // import { mplex } from '@libp2p/mplex'
import { identify } from "@libp2p/identify" // import { identify } from "@libp2p/identify"
import { dcutr } from "@libp2p/dcutr" // import { dcutr } from "@libp2p/dcutr"
import { getPublicKey } from 'nostr-tools/pure'; import { getPublicKey } from 'nostr-tools/pure';
import { initNostrWasm } from 'nostr-wasm' import { initNostrWasm } from 'nostr-wasm'
import { multiaddr } from '@multiformats/multiaddr' // import { multiaddr } from '@multiformats/multiaddr'
import { useWebSocketImplementation } from 'nostr-tools/pool' import { useWebSocketImplementation } from 'nostr-tools/pool'
import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery'
import WebSocket from 'ws' import WebSocket from 'ws'
import { Block } from 'ethers';
if (typeof global === "object") useWebSocketImplementation(WebSocket); if (typeof global === "object") useWebSocketImplementation(WebSocket);
export class Peering extends TransactionBuilder { export class Peering extends TransactionBuilder {
p2p: any | undefined; // p2p: any | undefined;
pool: SimplePool = new SimplePool(); pool: SimplePool = new SimplePool();
relays: string[]; relays: string[];
seeds: string[]; seeds: string[];
rebroadcast: any | undefined; rebroadcast: any | undefined;
callback: Function; // callback: Function;
constructor(_config: types.GlobalConfig) { constructor(_config: types.GlobalConfig) {
super(_config); 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.pool = new SimplePool();
this.relays = this.config.gossip!.relays; this.relays = this.config.gossip!.relays;
this.seeds = this.config.gossip!.seeds; 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, kind: 1,
created_at: _timestamp, created_at: _timestamp,
tags: [], tags: [],
content: this.p2p.getMultiaddrs().map((addr: any) => multiaddr(addr).toString()).join(',') content: ips
}, _sk))) }, _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) { // postTransaction = async (_tx: types.Transaction) => {
await this.p2p.services.pubsub.publish(_pk, new TextEncoder().encode(JSON.stringify(_tx))) // const hexString = (await this.contracts.state.getAddress()).slice(2).padStart(64, '0');
} else { // const matches = hexString.match(/.{1,2}/g);
setTimeout(async () => await this.postTransaction(_tx), 10_000); // 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> => { initializePeering = async (callback?: Function): Promise<void> => {
await this.initializeTransactionBuilder(); 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 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); let _pk = getPublicKey(skUint8Array);
await this.fetchRelays(); await this.fetchRelays();
await initNostrWasm().then(setNostrWasm); await initNostrWasm().then(setNostrWasm);
const latestBlock = await this.config.provider.getBlock("latest"); const latestBlock = await this.config.provider.getBlock("latest");
let ip: string
let multiAddrs: string[] = []; if (this.config.ip == "") {
let ips: string[] = []
this.p2p = await createLibp2p({ let ipServices = ["https://api.ipify.org", "https://icanhazip.com", "https://ifconfig.me/ip", "https://checkip.amazonaws.com/"]
addresses: { for (let i = 0; i < ipServices.length; i++) {
listen: (typeof global === "object") ? ['/ip4/0.0.0.0/tcp/5150/ws'] : [] let res = await fetch(ipServices[i])
}, ips.push((await res.text()).trim())
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) {
} }
} else { let last = ips[0]
try { for (let i = 0; i < ips.length; i++) {
const events: Event[] = await this.pool.querySync(this.relays.slice(0, this.config.gossip!.maxRelays), if (last === ips[i]) {
{ last = ips[i]
kinds: [1], } else {
authors: [_pk], console.log("Please set your public ip in the config file")
since: latestBlock!.timestamp - (typeof global === 'object' ? 0 : 86400) return
} }
)
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)
} }
ip = last
}else {
ip = this.config.ip as string
} }
if (typeof global === 'object') { if (typeof global === 'object') {
this.rebroadcast = setInterval(async () => await this.postEvent(skUint8Array, latestBlock!.timestamp), 60 * 60 * 1000); this.rebroadcast = setInterval(async () => {
await this.postEvent(skUint8Array, latestBlock!.timestamp); await this.postEvent(skUint8Array, latestBlock!.timestamp, ip)
this.p2p.services.pubsub.addEventListener('message', (message: CustomEvent) => { }, 60 * 60 * 1000);
if (!(message.detail.topic == _pk) || !callback) return;
callback(JSON.parse(new TextDecoder().decode(message.detail.data)));
})
await this.postEvent(skUint8Array, latestBlock!.timestamp, ip);
} }
} }
addPeer = async (peerMultiaddr: string): Promise<void> => { queryEvents = async (latestBlock: number) => {
if (!this.p2p) { const hexString = "6835ccbeddc1c1c91fed2116099592a309f98d0df34d8dc72e770df83cc3b065".padStart(64, '0');
throw new Error('P2P is not initialized'); const matches = hexString.match(/.{1,2}/g);
} const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array()
try { let _pk = getPublicKey(skUint8Array);
await this.p2p.dial(multiaddr(peerMultiaddr)); let event = await this.pool.querySync(this.relays, {
console.log(`Successfully connected to peer: ${peerMultiaddr}`); kinds: [1],
} catch (error) { authors: [_pk],
} since: latestBlock - (typeof global === 'object' ? 0 : 86400)
};
})
}
fetchRelays = async (refresh: boolean = false): Promise<string[]> => { fetchRelays = async (refresh: boolean = false): Promise<string[]> => {
if (this.seeds.length == 0 || refresh) return this.relays; if (this.seeds.length == 0 || refresh) return this.relays;
for (const seed of this.seeds) { for (const seed of this.seeds) {
......
...@@ -88,6 +88,7 @@ export type GlobalConfig = { ...@@ -88,6 +88,7 @@ export type GlobalConfig = {
withdrawal?: string, withdrawal?: string,
verbose?: boolean, verbose?: boolean,
threads?: number, threads?: number,
ip?: string,
provider: Provider provider: Provider
} }
......
...@@ -17,27 +17,27 @@ ...@@ -17,27 +17,27 @@
"devDependencies": { "devDependencies": {
"@types/node": "^20.10.4", "@types/node": "^20.10.4",
"@types/validator": "^13.11.7", "@types/validator": "^13.11.7",
"@types/ws": "^8.5.12",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"hardhat": "^2.22.3", "hardhat": "^2.22.3",
"nodemon": "^3.0.2", "nodemon": "^3.0.2",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.3.2" "typescript": "^5.3.2",
"ws": "^8.18.0"
}, },
"dependencies": { "dependencies": {
"@chainlink/contracts": "^0.8.0", "@chainlink/contracts": "^0.8.0",
"@libp2p/bootstrap": "^10.1.0",
"@libp2p/identify": "^2.1.2",
"@noir-lang/noir_js": "^0.28.0", "@noir-lang/noir_js": "^0.28.0",
"@nomicfoundation/hardhat-toolbox": "^5.0.0", "@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts": "^5.0.2",
"@openzeppelin/contracts-upgradeable": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2",
"@openzeppelin/hardhat-upgrades": "^3.2.0", "@openzeppelin/hardhat-upgrades": "^3.2.0",
"@types/pubsub-js": "^1.8.6", "@types/express": "^4.17.21",
"@uniswap/v3-periphery": "^1.4.4", "@uniswap/v3-periphery": "^1.4.4",
"cors": "^2.8.5",
"ethers": "^6.13.0", "ethers": "^6.13.0",
"fonstr": "^0.0.11", "express": "^4.19.2",
"indexeddbshim": "^15.0.0", "indexeddbshim": "^15.0.0",
"memorelay": "^2.0.4",
"reflect-metadata": "^0.1.14", "reflect-metadata": "^0.1.14",
"sequelize": "^6.35.1", "sequelize": "^6.35.1",
"sequelize-typescript": "^2.1.6" "sequelize-typescript": "^2.1.6"
......
...@@ -2,6 +2,7 @@ import { Publisher } from "./modules/momiji"; ...@@ -2,6 +2,7 @@ import { Publisher } from "./modules/momiji";
import * as types from "../momiji-helpers/utils/types"; import * as types from "../momiji-helpers/utils/types";
import { ethers } from "ethers"; import { ethers } from "ethers";
import 'dotenv/config'; import 'dotenv/config';
import { WebSocketServer } from "ws";
const main = async () => { const main = async () => {
let _globalConfig: types.GlobalConfig = { let _globalConfig: types.GlobalConfig = {
...@@ -18,10 +19,50 @@ const main = async () => { ...@@ -18,10 +19,50 @@ const main = async () => {
verbose: (process.env.VERBOSE) ? (process.env.VERBOSE.toLowerCase() === "true") : false, verbose: (process.env.VERBOSE) ? (process.env.VERBOSE.toLowerCase() === "true") : false,
profit: (process.env.PROFIT) ? parseInt(process.env.PROFIT) : 0 profit: (process.env.PROFIT) ? parseInt(process.env.PROFIT) : 0
}; };
let publisher = new Publisher(_globalConfig); let publisher = new Publisher(_globalConfig);
await publisher.initializePublisher(); 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...`) console.log(`💫 Starting publisher...`)
......
...@@ -4,19 +4,22 @@ import { keccak_tx } from '../../momiji-helpers/circuits/helpers/codegen/keccak_ ...@@ -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'; import { tx_as_hash } from '../../momiji-helpers/circuits/helpers/codegen/tx_as_hash';
export class Publisher extends BatchBuilder { export class Publisher extends BatchBuilder {
private publishTimeout: NodeJS.Timeout; // private publishTimeout: NodeJS.Timeout;
private signer: types.EthersSigner | undefined; private signer: types.EthersSigner | undefined;
private mempool: types.Mempool = {}; private mempool: types.Mempool = {};
private provingQueue: Promise<types.RecursionInputs[]> = Promise.resolve([]); private provingQueue: Promise<types.RecursionInputs[]> = Promise.resolve([]);
private batch: types.RecursionInputs[] = []; public batch: types.RecursionInputs[] = [];
private confirmed: types.Confirmed = {}; private confirmed: types.Confirmed = {};
private publishing: boolean = false; private publishing: boolean = false;
private proving: boolean = false; private proving: boolean = false;
private sweeping: 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) { constructor(config: types.GlobalConfig) {
super(config); super(config);
this.publishTimeout = setInterval(this.publish, 60 * 1000); // this.publishTimeout = setInterval(this.publish, 60 * 1000);
this.mempool = {}; this.mempool = {};
this.confirmed = {}; this.confirmed = {};
} }
...@@ -60,12 +63,46 @@ export class Publisher extends BatchBuilder { ...@@ -60,12 +63,46 @@ export class Publisher extends BatchBuilder {
} }
let txid = await keccak_tx(tx.public_inputs); let txid = await keccak_tx(tx.public_inputs);
let _verified = await this.addToMempool(tx, txid) let _verified = await this.addToMempool(tx, txid)
if (_verified) this.queueToProve(tx) if (_verified) this.queueToProve(tx)
else console.log(`🔴 Transaction rejected -- failed to verify: ${txid}`); 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> { async setupListeners(): Promise<void> {
if (!this.contracts) await this.initializePublisher(); if (!this.contracts) await this.initializePublisher();
if (!this.contracts) return; if (!this.contracts) return;
...@@ -104,7 +141,8 @@ export class Publisher extends BatchBuilder { ...@@ -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) => { this.contracts.state.on(this.contracts.state.filters.BatchPublish(undefined, undefined, undefined, undefined, undefined), async (event: any) => {
console.log(`🎯 Batch published: ${event}.`); console.log(`🎯 Batch published: ${event}.`);
this.provingQueue = Promise.resolve([]); // this.provingQueue = Promise.resolve([]);
this.queue = []
this.batch = []; this.batch = [];
this.printRoot(); this.printRoot();
return; return;
...@@ -131,7 +169,7 @@ export class Publisher extends BatchBuilder { ...@@ -131,7 +169,7 @@ export class Publisher extends BatchBuilder {
const withdrawals: types.WithdrawalSwap[] = await this._generateWithdrawals([{ const withdrawals: types.WithdrawalSwap[] = await this._generateWithdrawals([{
amount: (new types.NoirFr(withdrawalAmount)).toString(), amount: (new types.NoirFr(withdrawalAmount)).toString(),
recipient: types.toFixedHex(1, true), recipient: types.toFixedHex(1, true),
swap_percentage: 100 swap_percentage: 100
}], 1) }], 1)
const proverTx: types.Transaction = await this._generateTransactionProof(utxo_commitments, utxo_encrypted, withdrawals); const proverTx: types.Transaction = await this._generateTransactionProof(utxo_commitments, utxo_encrypted, withdrawals);
return proverTx; return proverTx;
...@@ -149,7 +187,13 @@ export class Publisher extends BatchBuilder { ...@@ -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 withdrawalAmount: bigint = utxo_commitments.map(utxo => BigInt(utxo.amount)).reduce((prev, curr) => prev + curr)
const withdrawalAmountEther: bigint = await this._getEtherFromXFT(withdrawalAmount) 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 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 maxFeePerGasAdjusted: bigint = (this.config.profit) ? maxFeePerGas + BigInt(this.config.profit * 1e9) : maxFeePerGas
const txFeeEstimate: bigint = gasEstimate * maxFeePerGasAdjusted const txFeeEstimate: bigint = gasEstimate * maxFeePerGasAdjusted
...@@ -189,6 +233,15 @@ export class Publisher extends BatchBuilder { ...@@ -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 () => { publish = async () => {
if (this.publishing) return; if (this.publishing) return;
...@@ -196,10 +249,9 @@ export class Publisher extends BatchBuilder { ...@@ -196,10 +249,9 @@ export class Publisher extends BatchBuilder {
if (this.sweeping) return; if (this.sweeping) return;
if (this.batch.length === 0) return; if (this.batch.length === 0) return;
await this.provingQueue; // await this.provingQueue;
const contractPublish: types.ContractPublish = await this.preparePublish(this.batch) // const contractPublish: types.ContractPublish = await this.preparePublish(this.batch)
// if (!(await this.publishReady(this.batch, contractPublish))) return;
if (!(await this.publishReady(this.batch, contractPublish))) return;
console.log(`🧹 Sweeping prover fees...`) console.log(`🧹 Sweeping prover fees...`)
this.sweeping = true this.sweeping = true
...@@ -213,7 +265,7 @@ export class Publisher extends BatchBuilder { ...@@ -213,7 +265,7 @@ export class Publisher extends BatchBuilder {
console.log(`🗞️ Publishing batch...`) console.log(`🗞️ Publishing batch...`)
const contractPublishProver: types.ContractPublish = await this.preparePublish(this.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) await this.contracts.state.publish(contractPublishProver.proof, contractPublishProver.batch)
.then((tx: any) => console.log(`📡 Batch published: ${tx.hash}.`), (error: any) => console.log(error.message)); .then((tx: any) => console.log(`📡 Batch published: ${tx.hash}.`), (error: any) => console.log(error.message));
...@@ -221,7 +273,48 @@ export class Publisher extends BatchBuilder { ...@@ -221,7 +273,48 @@ export class Publisher extends BatchBuilder {
this.provingQueue = Promise.resolve([]); this.provingQueue = Promise.resolve([]);
this.batch = []; 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; this.publishing = false;
return; return;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment