@aztec/prover-node 0.0.0-test.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dest/config.d.ts +28 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/config.js +71 -0
- package/dest/factory.d.ts +25 -0
- package/dest/factory.d.ts.map +1 -0
- package/dest/factory.js +60 -0
- package/dest/http.d.ts +8 -0
- package/dest/http.d.ts.map +1 -0
- package/dest/http.js +9 -0
- package/dest/index.d.ts +6 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +5 -0
- package/dest/job/epoch-proving-job.d.ts +54 -0
- package/dest/job/epoch-proving-job.d.ts.map +1 -0
- package/dest/job/epoch-proving-job.js +255 -0
- package/dest/metrics.d.ts +26 -0
- package/dest/metrics.d.ts.map +1 -0
- package/dest/metrics.js +129 -0
- package/dest/monitors/epoch-monitor.d.ts +40 -0
- package/dest/monitors/epoch-monitor.d.ts.map +1 -0
- package/dest/monitors/epoch-monitor.js +104 -0
- package/dest/monitors/index.d.ts +2 -0
- package/dest/monitors/index.d.ts.map +1 -0
- package/dest/monitors/index.js +1 -0
- package/dest/prover-coordination/config.d.ts +7 -0
- package/dest/prover-coordination/config.d.ts.map +1 -0
- package/dest/prover-coordination/config.js +11 -0
- package/dest/prover-coordination/factory.d.ts +22 -0
- package/dest/prover-coordination/factory.d.ts.map +1 -0
- package/dest/prover-coordination/factory.js +42 -0
- package/dest/prover-coordination/index.d.ts +3 -0
- package/dest/prover-coordination/index.d.ts.map +1 -0
- package/dest/prover-coordination/index.js +2 -0
- package/dest/prover-node-publisher.d.ts +61 -0
- package/dest/prover-node-publisher.d.ts.map +1 -0
- package/dest/prover-node-publisher.js +192 -0
- package/dest/prover-node.d.ts +102 -0
- package/dest/prover-node.d.ts.map +1 -0
- package/dest/prover-node.js +258 -0
- package/dest/test/index.d.ts +10 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +5 -0
- package/package.json +98 -0
- package/src/config.ts +115 -0
- package/src/factory.ts +99 -0
- package/src/http.ts +13 -0
- package/src/index.ts +5 -0
- package/src/job/epoch-proving-job.ts +255 -0
- package/src/metrics.ts +164 -0
- package/src/monitors/epoch-monitor.ts +106 -0
- package/src/monitors/index.ts +1 -0
- package/src/prover-coordination/config.ts +17 -0
- package/src/prover-coordination/factory.ts +72 -0
- package/src/prover-coordination/index.ts +2 -0
- package/src/prover-node-publisher.ts +286 -0
- package/src/prover-node.ts +335 -0
- package/src/test/index.ts +11 -0
package/dest/metrics.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { Attributes, Metrics, ValueType } from '@aztec/telemetry-client';
|
|
3
|
+
import { formatEther } from 'viem';
|
|
4
|
+
export class ProverNodeMetrics {
|
|
5
|
+
client;
|
|
6
|
+
logger;
|
|
7
|
+
proverEpochExecutionDuration;
|
|
8
|
+
provingJobDuration;
|
|
9
|
+
provingJobBlocks;
|
|
10
|
+
provingJobTransactions;
|
|
11
|
+
gasPrice;
|
|
12
|
+
txCount;
|
|
13
|
+
txDuration;
|
|
14
|
+
txGas;
|
|
15
|
+
txCalldataSize;
|
|
16
|
+
txCalldataGas;
|
|
17
|
+
txBlobDataGasUsed;
|
|
18
|
+
txBlobDataGasCost;
|
|
19
|
+
senderBalance;
|
|
20
|
+
constructor(client, name = 'ProverNode', logger = createLogger('prover-node:publisher:metrics')){
|
|
21
|
+
this.client = client;
|
|
22
|
+
this.logger = logger;
|
|
23
|
+
const meter = client.getMeter(name);
|
|
24
|
+
this.proverEpochExecutionDuration = meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
|
|
25
|
+
description: 'Duration of execution of an epoch by the prover',
|
|
26
|
+
unit: 'ms',
|
|
27
|
+
valueType: ValueType.INT
|
|
28
|
+
});
|
|
29
|
+
this.provingJobDuration = meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
|
|
30
|
+
description: 'Duration of proving job',
|
|
31
|
+
unit: 's',
|
|
32
|
+
valueType: ValueType.DOUBLE
|
|
33
|
+
});
|
|
34
|
+
this.provingJobBlocks = meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
|
|
35
|
+
description: 'Number of blocks in a proven epoch',
|
|
36
|
+
valueType: ValueType.INT
|
|
37
|
+
});
|
|
38
|
+
this.provingJobTransactions = meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
|
|
39
|
+
description: 'Number of transactions in a proven epoch',
|
|
40
|
+
valueType: ValueType.INT
|
|
41
|
+
});
|
|
42
|
+
this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
|
|
43
|
+
description: 'The gas price used for transactions',
|
|
44
|
+
unit: 'gwei',
|
|
45
|
+
valueType: ValueType.DOUBLE
|
|
46
|
+
});
|
|
47
|
+
this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
|
|
48
|
+
description: 'The number of transactions processed'
|
|
49
|
+
});
|
|
50
|
+
this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
|
|
51
|
+
description: 'The duration of transaction processing',
|
|
52
|
+
unit: 'ms',
|
|
53
|
+
valueType: ValueType.INT
|
|
54
|
+
});
|
|
55
|
+
this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
|
|
56
|
+
description: 'The gas consumed by transactions',
|
|
57
|
+
unit: 'gas',
|
|
58
|
+
valueType: ValueType.INT
|
|
59
|
+
});
|
|
60
|
+
this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
|
|
61
|
+
description: 'The size of the calldata in transactions',
|
|
62
|
+
unit: 'By',
|
|
63
|
+
valueType: ValueType.INT
|
|
64
|
+
});
|
|
65
|
+
this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
|
|
66
|
+
description: 'The gas consumed by the calldata in transactions',
|
|
67
|
+
unit: 'gas',
|
|
68
|
+
valueType: ValueType.INT
|
|
69
|
+
});
|
|
70
|
+
this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
|
|
71
|
+
description: 'The amount of blob gas used in transactions',
|
|
72
|
+
unit: 'gas',
|
|
73
|
+
valueType: ValueType.INT
|
|
74
|
+
});
|
|
75
|
+
this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
|
|
76
|
+
description: 'The gas cost of blobs in transactions',
|
|
77
|
+
unit: 'gwei',
|
|
78
|
+
valueType: ValueType.INT
|
|
79
|
+
});
|
|
80
|
+
this.senderBalance = meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
|
|
81
|
+
unit: 'eth',
|
|
82
|
+
description: 'The balance of the sender address',
|
|
83
|
+
valueType: ValueType.DOUBLE
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
recordFailedTx() {
|
|
87
|
+
this.txCount.add(1, {
|
|
88
|
+
[Attributes.L1_TX_TYPE]: 'submitProof',
|
|
89
|
+
[Attributes.OK]: false
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
recordSubmitProof(durationMs, stats) {
|
|
93
|
+
this.recordTx(durationMs, stats);
|
|
94
|
+
}
|
|
95
|
+
recordProvingJob(executionTimeMs, totalTimeMs, numBlocks, numTxs) {
|
|
96
|
+
this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
|
|
97
|
+
this.provingJobDuration.record(totalTimeMs / 1000);
|
|
98
|
+
this.provingJobBlocks.record(Math.floor(numBlocks));
|
|
99
|
+
this.provingJobTransactions.record(Math.floor(numTxs));
|
|
100
|
+
}
|
|
101
|
+
recordSenderBalance(wei, senderAddress) {
|
|
102
|
+
const eth = parseFloat(formatEther(wei, 'wei'));
|
|
103
|
+
this.senderBalance.record(eth, {
|
|
104
|
+
[Attributes.SENDER_ADDRESS]: senderAddress
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
recordTx(durationMs, stats) {
|
|
108
|
+
const attributes = {
|
|
109
|
+
[Attributes.L1_TX_TYPE]: 'submitProof',
|
|
110
|
+
[Attributes.L1_SENDER]: stats.sender
|
|
111
|
+
};
|
|
112
|
+
this.txCount.add(1, {
|
|
113
|
+
...attributes,
|
|
114
|
+
[Attributes.OK]: true
|
|
115
|
+
});
|
|
116
|
+
this.txDuration.record(Math.ceil(durationMs), attributes);
|
|
117
|
+
this.txGas.record(// safe to downcast - total block limit is 30M gas which fits in a JS number
|
|
118
|
+
Number(stats.gasUsed), attributes);
|
|
119
|
+
this.txCalldataGas.record(stats.calldataGas, attributes);
|
|
120
|
+
this.txCalldataSize.record(stats.calldataSize, attributes);
|
|
121
|
+
this.txBlobDataGasCost.record(Number(stats.blobDataGas), attributes);
|
|
122
|
+
this.txBlobDataGasUsed.record(Number(stats.blobGasUsed), attributes);
|
|
123
|
+
try {
|
|
124
|
+
this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
|
|
125
|
+
} catch (e) {
|
|
126
|
+
// ignore
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
2
|
+
import { type L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
3
|
+
import { type TelemetryClient, type Traceable, type Tracer } from '@aztec/telemetry-client';
|
|
4
|
+
export interface EpochMonitorHandler {
|
|
5
|
+
handleEpochReadyToProve(epochNumber: bigint): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Fires an event when a new epoch ready to prove is detected.
|
|
9
|
+
*
|
|
10
|
+
* We define an epoch as ready to prove when:
|
|
11
|
+
* - The epoch is complete
|
|
12
|
+
* - Its blocks have not been reorg'd out due to a missing L2 proof
|
|
13
|
+
* - Its first block is the immediate successor of the last proven block
|
|
14
|
+
*
|
|
15
|
+
* This class periodically hits the L2BlockSource.
|
|
16
|
+
* On start it will trigger the event for the last epoch ready to prove.
|
|
17
|
+
*/
|
|
18
|
+
export declare class EpochMonitor implements Traceable {
|
|
19
|
+
private readonly l2BlockSource;
|
|
20
|
+
private readonly l1Constants;
|
|
21
|
+
private options;
|
|
22
|
+
private runningPromise;
|
|
23
|
+
private log;
|
|
24
|
+
readonly tracer: Tracer;
|
|
25
|
+
private handler;
|
|
26
|
+
private latestEpochNumber;
|
|
27
|
+
constructor(l2BlockSource: L2BlockSource, l1Constants: Pick<L1RollupConstants, 'epochDuration'>, options: {
|
|
28
|
+
pollingIntervalMs: number;
|
|
29
|
+
}, telemetry?: TelemetryClient);
|
|
30
|
+
static create(l2BlockSource: L2BlockSource, options: {
|
|
31
|
+
pollingIntervalMs: number;
|
|
32
|
+
}, telemetry?: TelemetryClient): Promise<EpochMonitor>;
|
|
33
|
+
start(handler: EpochMonitorHandler): void;
|
|
34
|
+
/** Exposed for testing */
|
|
35
|
+
setHandler(handler: EpochMonitorHandler): void;
|
|
36
|
+
stop(): Promise<void>;
|
|
37
|
+
work(): Promise<void>;
|
|
38
|
+
private getEpochNumberToProve;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=epoch-monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epoch-monitor.d.ts","sourceRoot":"","sources":["../../src/monitors/epoch-monitor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,6BAA6B,CAAC;AACrF,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,MAAM,EAGZ,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,YAAW,SAAS;IAS1C,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,OAAO;IAVjB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,GAAG,CAA6C;IACxD,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,iBAAiB,CAAqB;gBAG3B,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EAC9D,OAAO,EAAE;QAAE,iBAAiB,EAAE,MAAM,CAAA;KAAE,EAC9C,SAAS,GAAE,eAAsC;WAM/B,MAAM,CACxB,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE;QAAE,iBAAiB,EAAE,MAAM,CAAA;KAAE,EACtC,SAAS,GAAE,eAAsC,GAChD,OAAO,CAAC,YAAY,CAAC;IAKjB,KAAK,CAAC,OAAO,EAAE,mBAAmB;IAMzC,0BAA0B;IACnB,UAAU,CAAC,OAAO,EAAE,mBAAmB;IAIjC,IAAI;IAMJ,IAAI;YAsBH,qBAAqB;CAYpC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
8
|
+
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
9
|
+
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
10
|
+
import { getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
11
|
+
/**
|
|
12
|
+
* Fires an event when a new epoch ready to prove is detected.
|
|
13
|
+
*
|
|
14
|
+
* We define an epoch as ready to prove when:
|
|
15
|
+
* - The epoch is complete
|
|
16
|
+
* - Its blocks have not been reorg'd out due to a missing L2 proof
|
|
17
|
+
* - Its first block is the immediate successor of the last proven block
|
|
18
|
+
*
|
|
19
|
+
* This class periodically hits the L2BlockSource.
|
|
20
|
+
* On start it will trigger the event for the last epoch ready to prove.
|
|
21
|
+
*/ export class EpochMonitor {
|
|
22
|
+
l2BlockSource;
|
|
23
|
+
l1Constants;
|
|
24
|
+
options;
|
|
25
|
+
runningPromise;
|
|
26
|
+
log;
|
|
27
|
+
tracer;
|
|
28
|
+
handler;
|
|
29
|
+
latestEpochNumber;
|
|
30
|
+
constructor(l2BlockSource, l1Constants, options, telemetry = getTelemetryClient()){
|
|
31
|
+
this.l2BlockSource = l2BlockSource;
|
|
32
|
+
this.l1Constants = l1Constants;
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.log = createLogger('prover-node:epoch-monitor');
|
|
35
|
+
this.tracer = telemetry.getTracer('EpochMonitor');
|
|
36
|
+
this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.options.pollingIntervalMs);
|
|
37
|
+
}
|
|
38
|
+
static async create(l2BlockSource, options, telemetry = getTelemetryClient()) {
|
|
39
|
+
const l1Constants = await l2BlockSource.getL1Constants();
|
|
40
|
+
return new EpochMonitor(l2BlockSource, l1Constants, options, telemetry);
|
|
41
|
+
}
|
|
42
|
+
start(handler) {
|
|
43
|
+
this.handler = handler;
|
|
44
|
+
this.runningPromise.start();
|
|
45
|
+
this.log.info('Started EpochMonitor', this.options);
|
|
46
|
+
}
|
|
47
|
+
/** Exposed for testing */ setHandler(handler) {
|
|
48
|
+
this.handler = handler;
|
|
49
|
+
}
|
|
50
|
+
async stop() {
|
|
51
|
+
await this.runningPromise.stop();
|
|
52
|
+
this.log.info('Stopped EpochMonitor');
|
|
53
|
+
}
|
|
54
|
+
async work() {
|
|
55
|
+
const { epochToProve, blockNumber, slotNumber } = await this.getEpochNumberToProve();
|
|
56
|
+
if (epochToProve === undefined) {
|
|
57
|
+
this.log.trace(`Next block to prove ${blockNumber} not yet mined`, {
|
|
58
|
+
blockNumber
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (this.latestEpochNumber !== undefined && epochToProve <= this.latestEpochNumber) {
|
|
63
|
+
this.log.trace(`Epoch ${epochToProve} already processed`, {
|
|
64
|
+
epochToProve,
|
|
65
|
+
blockNumber,
|
|
66
|
+
slotNumber
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const isCompleted = await this.l2BlockSource.isEpochComplete(epochToProve);
|
|
71
|
+
if (!isCompleted) {
|
|
72
|
+
this.log.trace(`Epoch ${epochToProve} is not complete`, {
|
|
73
|
+
epochToProve,
|
|
74
|
+
blockNumber,
|
|
75
|
+
slotNumber
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.log.debug(`Epoch ${epochToProve} is ready to be proven`);
|
|
80
|
+
await this.handler?.handleEpochReadyToProve(epochToProve);
|
|
81
|
+
this.latestEpochNumber = epochToProve;
|
|
82
|
+
}
|
|
83
|
+
async getEpochNumberToProve() {
|
|
84
|
+
const lastBlockProven = await this.l2BlockSource.getProvenBlockNumber();
|
|
85
|
+
const firstBlockToProve = lastBlockProven + 1;
|
|
86
|
+
const firstBlockHeaderToProve = await this.l2BlockSource.getBlockHeader(firstBlockToProve);
|
|
87
|
+
if (!firstBlockHeaderToProve) {
|
|
88
|
+
return {
|
|
89
|
+
epochToProve: undefined,
|
|
90
|
+
blockNumber: firstBlockToProve
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const firstSlotOfEpochToProve = firstBlockHeaderToProve.getSlot();
|
|
94
|
+
const epochToProve = getEpochAtSlot(firstSlotOfEpochToProve, this.l1Constants);
|
|
95
|
+
return {
|
|
96
|
+
epochToProve,
|
|
97
|
+
blockNumber: firstBlockToProve,
|
|
98
|
+
slotNumber: firstSlotOfEpochToProve
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
_ts_decorate([
|
|
103
|
+
trackSpan('EpochMonitor.work')
|
|
104
|
+
], EpochMonitor.prototype, "work", null);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/monitors/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './epoch-monitor.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ConfigMappingsType } from '@aztec/foundation/config';
|
|
2
|
+
export type ProverCoordinationConfig = {
|
|
3
|
+
proverCoordinationNodeUrl: string | undefined;
|
|
4
|
+
};
|
|
5
|
+
export declare const proverCoordinationConfigMappings: ConfigMappingsType<ProverCoordinationConfig>;
|
|
6
|
+
export declare function getTxProviderConfigFromEnv(): ProverCoordinationConfig;
|
|
7
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/prover-coordination/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAyB,MAAM,0BAA0B,CAAC;AAE1F,MAAM,MAAM,wBAAwB,GAAG;IACrC,yBAAyB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C,CAAC;AAEF,eAAO,MAAM,gCAAgC,EAAE,kBAAkB,CAAC,wBAAwB,CAMzF,CAAC;AAEF,wBAAgB,0BAA0B,IAAI,wBAAwB,CAErE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getConfigFromMappings } from '@aztec/foundation/config';
|
|
2
|
+
export const proverCoordinationConfigMappings = {
|
|
3
|
+
proverCoordinationNodeUrl: {
|
|
4
|
+
env: 'PROVER_COORDINATION_NODE_URL',
|
|
5
|
+
description: 'The URL of the tx provider node',
|
|
6
|
+
parseEnv: (val)=>val
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
export function getTxProviderConfigFromEnv() {
|
|
10
|
+
return getConfigFromMappings(proverCoordinationConfigMappings);
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ArchiveSource, Archiver } from '@aztec/archiver';
|
|
2
|
+
import type { EpochCache } from '@aztec/epoch-cache';
|
|
3
|
+
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
4
|
+
import type { ProverCoordination, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
5
|
+
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
6
|
+
import type { ProverNodeConfig } from '../config.js';
|
|
7
|
+
type ProverCoordinationDeps = {
|
|
8
|
+
aztecNodeTxProvider?: ProverCoordination;
|
|
9
|
+
worldStateSynchronizer?: WorldStateSynchronizer;
|
|
10
|
+
archiver?: Archiver | ArchiveSource;
|
|
11
|
+
telemetry?: TelemetryClient;
|
|
12
|
+
epochCache?: EpochCache;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Creates a prover coordination service.
|
|
16
|
+
* If p2p is enabled, prover coordination is done via p2p.
|
|
17
|
+
* If an Aztec node URL is provided, prover coordination is done via the Aztec node over http.
|
|
18
|
+
* If an aztec node is provided, it is returned directly.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createProverCoordination(config: ProverNodeConfig & DataStoreConfig, deps: ProverCoordinationDeps): Promise<ProverCoordination>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/prover-coordination/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAK9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAGlG,OAAO,EAAE,KAAK,eAAe,EAAmB,MAAM,yBAAyB,CAAC;AAEhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,KAAK,sBAAsB,GAAG;IAC5B,mBAAmB,CAAC,EAAE,kBAAkB,CAAC;IACzC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,QAAQ,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IACpC,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,gBAAgB,GAAG,eAAe,EAC1C,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAqC7B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
|
|
2
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
4
|
+
import { createP2PClient } from '@aztec/p2p';
|
|
5
|
+
import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
|
|
6
|
+
import { createAztecNodeClient } from '@aztec/stdlib/interfaces/client';
|
|
7
|
+
import { P2PClientType } from '@aztec/stdlib/p2p';
|
|
8
|
+
import { getComponentsVersionsFromConfig } from '@aztec/stdlib/versioning';
|
|
9
|
+
import { makeTracedFetch } from '@aztec/telemetry-client';
|
|
10
|
+
/**
|
|
11
|
+
* Creates a prover coordination service.
|
|
12
|
+
* If p2p is enabled, prover coordination is done via p2p.
|
|
13
|
+
* If an Aztec node URL is provided, prover coordination is done via the Aztec node over http.
|
|
14
|
+
* If an aztec node is provided, it is returned directly.
|
|
15
|
+
*/ export async function createProverCoordination(config, deps) {
|
|
16
|
+
const log = createLogger('prover-node:prover-coordination');
|
|
17
|
+
if (deps.aztecNodeTxProvider) {
|
|
18
|
+
log.info('Using prover coordination via aztec node');
|
|
19
|
+
return deps.aztecNodeTxProvider;
|
|
20
|
+
}
|
|
21
|
+
if (config.p2pEnabled) {
|
|
22
|
+
log.info('Using prover coordination via p2p');
|
|
23
|
+
if (!deps.archiver || !deps.worldStateSynchronizer || !deps.telemetry || !deps.epochCache) {
|
|
24
|
+
throw new Error('Missing dependencies for p2p prover coordination');
|
|
25
|
+
}
|
|
26
|
+
const proofVerifier = config.realProofs ? await BBCircuitVerifier.new(config) : new TestCircuitVerifier();
|
|
27
|
+
const p2pClient = await createP2PClient(P2PClientType.Prover, config, deps.archiver, proofVerifier, deps.worldStateSynchronizer, deps.epochCache, deps.telemetry);
|
|
28
|
+
await p2pClient.start();
|
|
29
|
+
return p2pClient;
|
|
30
|
+
}
|
|
31
|
+
if (config.proverCoordinationNodeUrl) {
|
|
32
|
+
log.info('Using prover coordination via node url');
|
|
33
|
+
const versions = getComponentsVersionsFromConfig(config, protocolContractTreeRoot, getVKTreeRoot());
|
|
34
|
+
return createAztecNodeClient(config.proverCoordinationNodeUrl, versions, makeTracedFetch([
|
|
35
|
+
1,
|
|
36
|
+
2,
|
|
37
|
+
3
|
|
38
|
+
], false));
|
|
39
|
+
} else {
|
|
40
|
+
throw new Error(`Aztec Node URL for Tx Provider is not set.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prover-coordination/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
|
|
2
|
+
import type { L1TxUtils, RollupContract } from '@aztec/ethereum';
|
|
3
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
4
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
5
|
+
import { type Tuple } from '@aztec/foundation/serialize';
|
|
6
|
+
import type { PublisherConfig, TxSenderConfig } from '@aztec/sequencer-client';
|
|
7
|
+
import type { Proof } from '@aztec/stdlib/proofs';
|
|
8
|
+
import type { FeeRecipient, RootRollupPublicInputs } from '@aztec/stdlib/rollup';
|
|
9
|
+
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
10
|
+
/**
|
|
11
|
+
* Stats for a sent transaction.
|
|
12
|
+
*/
|
|
13
|
+
/** Arguments to the submitEpochProof method of the rollup contract */
|
|
14
|
+
export type L1SubmitEpochProofArgs = {
|
|
15
|
+
epochSize: number;
|
|
16
|
+
previousArchive: Fr;
|
|
17
|
+
endArchive: Fr;
|
|
18
|
+
previousBlockHash: Fr;
|
|
19
|
+
endBlockHash: Fr;
|
|
20
|
+
endTimestamp: Fr;
|
|
21
|
+
outHash: Fr;
|
|
22
|
+
proverId: Fr;
|
|
23
|
+
fees: Tuple<FeeRecipient, typeof AZTEC_MAX_EPOCH_DURATION>;
|
|
24
|
+
proof: Proof;
|
|
25
|
+
};
|
|
26
|
+
export declare class ProverNodePublisher {
|
|
27
|
+
private interruptibleSleep;
|
|
28
|
+
private sleepTimeMs;
|
|
29
|
+
private interrupted;
|
|
30
|
+
private metrics;
|
|
31
|
+
protected log: import("@aztec/foundation/log").Logger;
|
|
32
|
+
protected rollupContract: RollupContract;
|
|
33
|
+
readonly l1TxUtils: L1TxUtils;
|
|
34
|
+
constructor(config: TxSenderConfig & PublisherConfig, deps: {
|
|
35
|
+
rollupContract: RollupContract;
|
|
36
|
+
l1TxUtils: L1TxUtils;
|
|
37
|
+
telemetry?: TelemetryClient;
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
|
|
41
|
+
* Be warned, the call may return false even if the tx subsequently gets successfully mined.
|
|
42
|
+
* In practice this shouldn't matter, as we'll only ever be calling `interrupt` when we know it's going to fail.
|
|
43
|
+
* A call to `restart` is required before you can continue publishing.
|
|
44
|
+
*/
|
|
45
|
+
interrupt(): void;
|
|
46
|
+
/** Restarts the publisher after calling `interrupt`. */
|
|
47
|
+
restart(): void;
|
|
48
|
+
getSenderAddress(): EthAddress;
|
|
49
|
+
submitEpochProof(args: {
|
|
50
|
+
epochNumber: number;
|
|
51
|
+
fromBlock: number;
|
|
52
|
+
toBlock: number;
|
|
53
|
+
publicInputs: RootRollupPublicInputs;
|
|
54
|
+
proof: Proof;
|
|
55
|
+
}): Promise<boolean>;
|
|
56
|
+
private validateEpochProofSubmission;
|
|
57
|
+
private sendSubmitEpochProofTx;
|
|
58
|
+
private getSubmitEpochProofArgs;
|
|
59
|
+
protected sleepOrInterrupted(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=prover-node-publisher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prover-node-publisher.d.ts","sourceRoot":"","sources":["../src/prover-node-publisher.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAE9C,OAAO,EAAE,KAAK,KAAK,EAAqB,MAAM,6BAA6B,CAAC;AAI5E,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAMnF;;GAEG;AACH,sEAAsE;AACtE,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,EAAE,CAAC;IACpB,UAAU,EAAE,EAAE,CAAC;IACf,iBAAiB,EAAE,EAAE,CAAC;IACtB,YAAY,EAAE,EAAE,CAAC;IACjB,YAAY,EAAE,EAAE,CAAC;IACjB,OAAO,EAAE,EAAE,CAAC;IACZ,QAAQ,EAAE,EAAE,CAAC;IACb,IAAI,EAAE,KAAK,CAAC,YAAY,EAAE,OAAO,wBAAwB,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,kBAAkB,CAA4B;IACtD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAoB;IAEnC,SAAS,CAAC,GAAG,yCAA+C;IAE5D,SAAS,CAAC,cAAc,EAAE,cAAc,CAAC;IAEzC,SAAgB,SAAS,EAAE,SAAS,CAAC;gBAGnC,MAAM,EAAE,cAAc,GAAG,eAAe,EACxC,IAAI,EAAE;QACJ,cAAc,EAAE,cAAc,CAAC;QAC/B,SAAS,EAAE,SAAS,CAAC;QACrB,SAAS,CAAC,EAAE,eAAe,CAAC;KAC7B;IAYH;;;;;OAKG;IACI,SAAS;IAKhB,wDAAwD;IACjD,OAAO;IAIP,gBAAgB;IAIV,gBAAgB,CAAC,IAAI,EAAE;QAClC,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,sBAAsB,CAAC;QACrC,KAAK,EAAE,KAAK,CAAC;KACd,GAAG,OAAO,CAAC,OAAO,CAAC;YAgDN,4BAA4B;YAyD5B,sBAAsB;IAoDpC,OAAO,CAAC,uBAAuB;cA+Bf,kBAAkB;CAGnC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { AGGREGATION_OBJECT_LENGTH, AZTEC_MAX_EPOCH_DURATION } from '@aztec/constants';
|
|
2
|
+
import { makeTuple } from '@aztec/foundation/array';
|
|
3
|
+
import { areArraysEqual, times } from '@aztec/foundation/collection';
|
|
4
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
6
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
7
|
+
import { serializeToBuffer } from '@aztec/foundation/serialize';
|
|
8
|
+
import { InterruptibleSleep } from '@aztec/foundation/sleep';
|
|
9
|
+
import { Timer } from '@aztec/foundation/timer';
|
|
10
|
+
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
11
|
+
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
12
|
+
import { encodeFunctionData } from 'viem';
|
|
13
|
+
import { ProverNodeMetrics } from './metrics.js';
|
|
14
|
+
export class ProverNodePublisher {
|
|
15
|
+
interruptibleSleep = new InterruptibleSleep();
|
|
16
|
+
sleepTimeMs;
|
|
17
|
+
interrupted = false;
|
|
18
|
+
metrics;
|
|
19
|
+
log = createLogger('prover-node:l1-tx-publisher');
|
|
20
|
+
rollupContract;
|
|
21
|
+
l1TxUtils;
|
|
22
|
+
constructor(config, deps){
|
|
23
|
+
this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
|
|
24
|
+
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
25
|
+
this.metrics = new ProverNodeMetrics(telemetry, 'ProverNode');
|
|
26
|
+
this.rollupContract = deps.rollupContract;
|
|
27
|
+
this.l1TxUtils = deps.l1TxUtils;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
|
|
31
|
+
* Be warned, the call may return false even if the tx subsequently gets successfully mined.
|
|
32
|
+
* In practice this shouldn't matter, as we'll only ever be calling `interrupt` when we know it's going to fail.
|
|
33
|
+
* A call to `restart` is required before you can continue publishing.
|
|
34
|
+
*/ interrupt() {
|
|
35
|
+
this.interrupted = true;
|
|
36
|
+
this.interruptibleSleep.interrupt();
|
|
37
|
+
}
|
|
38
|
+
/** Restarts the publisher after calling `interrupt`. */ restart() {
|
|
39
|
+
this.interrupted = false;
|
|
40
|
+
}
|
|
41
|
+
getSenderAddress() {
|
|
42
|
+
return EthAddress.fromString(this.l1TxUtils.getSenderAddress());
|
|
43
|
+
}
|
|
44
|
+
async submitEpochProof(args) {
|
|
45
|
+
const { epochNumber, fromBlock, toBlock } = args;
|
|
46
|
+
const ctx = {
|
|
47
|
+
epochNumber,
|
|
48
|
+
fromBlock,
|
|
49
|
+
toBlock
|
|
50
|
+
};
|
|
51
|
+
if (!this.interrupted) {
|
|
52
|
+
const timer = new Timer();
|
|
53
|
+
// Validate epoch proof range and hashes are correct before submitting
|
|
54
|
+
await this.validateEpochProofSubmission(args);
|
|
55
|
+
const txReceipt = await this.sendSubmitEpochProofTx(args);
|
|
56
|
+
if (!txReceipt) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
this.metrics.recordSenderBalance(await this.l1TxUtils.getSenderBalance(), this.l1TxUtils.getSenderAddress());
|
|
61
|
+
} catch (err) {
|
|
62
|
+
this.log.warn(`Failed to record the ETH balance of the prover node: ${err}`);
|
|
63
|
+
}
|
|
64
|
+
// Tx was mined successfully
|
|
65
|
+
if (txReceipt.status) {
|
|
66
|
+
const tx = await this.l1TxUtils.getTransactionStats(txReceipt.transactionHash);
|
|
67
|
+
const stats = {
|
|
68
|
+
gasPrice: txReceipt.effectiveGasPrice,
|
|
69
|
+
gasUsed: txReceipt.gasUsed,
|
|
70
|
+
transactionHash: txReceipt.transactionHash,
|
|
71
|
+
calldataGas: tx.calldataGas,
|
|
72
|
+
calldataSize: tx.calldataSize,
|
|
73
|
+
sender: tx.sender,
|
|
74
|
+
blobDataGas: 0n,
|
|
75
|
+
blobGasUsed: 0n,
|
|
76
|
+
eventName: 'proof-published-to-l1'
|
|
77
|
+
};
|
|
78
|
+
this.log.info(`Published epoch proof to L1 rollup contract`, {
|
|
79
|
+
...stats,
|
|
80
|
+
...ctx
|
|
81
|
+
});
|
|
82
|
+
this.metrics.recordSubmitProof(timer.ms(), stats);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
this.metrics.recordFailedTx();
|
|
86
|
+
this.log.error(`Rollup.submitEpochProof tx status failed: ${txReceipt.transactionHash}`, ctx);
|
|
87
|
+
await this.sleepOrInterrupted();
|
|
88
|
+
}
|
|
89
|
+
this.log.verbose('L2 block data syncing interrupted while processing blocks.', ctx);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
async validateEpochProofSubmission(args) {
|
|
93
|
+
const { fromBlock, toBlock, publicInputs, proof } = args;
|
|
94
|
+
// Check that the block numbers match the expected epoch to be proven
|
|
95
|
+
const { pendingBlockNumber: pending, provenBlockNumber: proven } = await this.rollupContract.getTips();
|
|
96
|
+
if (proven !== BigInt(fromBlock) - 1n) {
|
|
97
|
+
throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`);
|
|
98
|
+
}
|
|
99
|
+
if (toBlock > pending) {
|
|
100
|
+
throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`);
|
|
101
|
+
}
|
|
102
|
+
// Check the block hash and archive for the immediate block before the epoch
|
|
103
|
+
const blockLog = await this.rollupContract.getBlock(proven);
|
|
104
|
+
if (publicInputs.previousArchive.root.toString() !== blockLog.archive) {
|
|
105
|
+
throw new Error(`Previous archive root mismatch: ${publicInputs.previousArchive.root.toString()} !== ${blockLog.archive}`);
|
|
106
|
+
}
|
|
107
|
+
// TODO: Remove zero check once we inject the proper zero blockhash
|
|
108
|
+
if (blockLog.blockHash !== Fr.ZERO.toString() && publicInputs.previousBlockHash.toString() !== blockLog.blockHash) {
|
|
109
|
+
throw new Error(`Previous block hash mismatch: ${publicInputs.previousBlockHash.toString()} !== ${blockLog.blockHash}`);
|
|
110
|
+
}
|
|
111
|
+
// Check the block hash and archive for the last block in the epoch
|
|
112
|
+
const endBlockLog = await this.rollupContract.getBlock(BigInt(toBlock));
|
|
113
|
+
if (publicInputs.endArchive.root.toString() !== endBlockLog.archive) {
|
|
114
|
+
throw new Error(`End archive root mismatch: ${publicInputs.endArchive.root.toString()} !== ${endBlockLog.archive}`);
|
|
115
|
+
}
|
|
116
|
+
if (publicInputs.endBlockHash.toString() !== endBlockLog.blockHash) {
|
|
117
|
+
throw new Error(`End block hash mismatch: ${publicInputs.endBlockHash.toString()} !== ${endBlockLog.blockHash}`);
|
|
118
|
+
}
|
|
119
|
+
// Compare the public inputs computed by the contract with the ones injected
|
|
120
|
+
const rollupPublicInputs = await this.rollupContract.getEpochProofPublicInputs(this.getSubmitEpochProofArgs(args));
|
|
121
|
+
const aggregationObject = proof.isEmpty() ? times(AGGREGATION_OBJECT_LENGTH, Fr.zero) : proof.extractAggregationObject();
|
|
122
|
+
const argsPublicInputs = [
|
|
123
|
+
...publicInputs.toFields(),
|
|
124
|
+
...aggregationObject
|
|
125
|
+
];
|
|
126
|
+
if (!areArraysEqual(rollupPublicInputs.map(Fr.fromHexString), argsPublicInputs, (a, b)=>a.equals(b))) {
|
|
127
|
+
const fmt = (inputs)=>inputs.map((x)=>x.toString()).join(', ');
|
|
128
|
+
throw new Error(`Root rollup public inputs mismatch:\nRollup: ${fmt(rollupPublicInputs)}\nComputed:${fmt(argsPublicInputs)}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async sendSubmitEpochProofTx(args) {
|
|
132
|
+
const proofHex = `0x${args.proof.withoutPublicInputs().toString('hex')}`;
|
|
133
|
+
const argsArray = this.getSubmitEpochProofArgs(args);
|
|
134
|
+
const txArgs = [
|
|
135
|
+
{
|
|
136
|
+
start: argsArray[0],
|
|
137
|
+
end: argsArray[1],
|
|
138
|
+
args: argsArray[2],
|
|
139
|
+
fees: argsArray[3],
|
|
140
|
+
blobPublicInputs: argsArray[4],
|
|
141
|
+
aggregationObject: argsArray[5],
|
|
142
|
+
proof: proofHex
|
|
143
|
+
}
|
|
144
|
+
];
|
|
145
|
+
this.log.info(`SubmitEpochProof proofSize=${args.proof.withoutPublicInputs().length} bytes`);
|
|
146
|
+
const data = encodeFunctionData({
|
|
147
|
+
abi: RollupAbi,
|
|
148
|
+
functionName: 'submitEpochRootProof',
|
|
149
|
+
args: txArgs
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({
|
|
153
|
+
to: this.rollupContract.address,
|
|
154
|
+
data
|
|
155
|
+
});
|
|
156
|
+
return receipt;
|
|
157
|
+
} catch (err) {
|
|
158
|
+
this.log.error(`Rollup submit epoch proof failed`, err);
|
|
159
|
+
const errorMsg = await this.l1TxUtils.tryGetErrorFromRevertedTx(data, {
|
|
160
|
+
args: [
|
|
161
|
+
...txArgs
|
|
162
|
+
],
|
|
163
|
+
functionName: 'submitEpochRootProof',
|
|
164
|
+
abi: RollupAbi,
|
|
165
|
+
address: this.rollupContract.address
|
|
166
|
+
}, /*blobInputs*/ undefined, /*stateOverride*/ []);
|
|
167
|
+
this.log.error(`Rollup submit epoch proof tx reverted. ${errorMsg}`);
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
getSubmitEpochProofArgs(args) {
|
|
172
|
+
return [
|
|
173
|
+
BigInt(args.fromBlock),
|
|
174
|
+
BigInt(args.toBlock),
|
|
175
|
+
{
|
|
176
|
+
previousArchive: args.publicInputs.previousArchive.root.toString(),
|
|
177
|
+
endArchive: args.publicInputs.endArchive.root.toString(),
|
|
178
|
+
previousBlockHash: args.publicInputs.previousBlockHash.toString(),
|
|
179
|
+
endBlockHash: args.publicInputs.endBlockHash.toString(),
|
|
180
|
+
endTimestamp: args.publicInputs.endTimestamp.toBigInt(),
|
|
181
|
+
outHash: args.publicInputs.outHash.toString(),
|
|
182
|
+
proverId: EthAddress.fromField(args.publicInputs.proverId).toString()
|
|
183
|
+
},
|
|
184
|
+
makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, (i)=>i % 2 === 0 ? args.publicInputs.fees[i / 2].recipient.toField().toString() : args.publicInputs.fees[(i - 1) / 2].value.toString()),
|
|
185
|
+
`0x${args.publicInputs.blobPublicInputs.filter((_, i)=>i < args.toBlock - args.fromBlock + 1).map((b)=>b.toString()).join(``)}`,
|
|
186
|
+
`0x${serializeToBuffer(args.proof.extractAggregationObject()).toString('hex')}`
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
async sleepOrInterrupted() {
|
|
190
|
+
await this.interruptibleSleep.sleep(this.sleepTimeMs);
|
|
191
|
+
}
|
|
192
|
+
}
|