Skip to content

Commit 9bb93cf

Browse files
committed
Added lightnet and created integration tests
1 parent ad0cde0 commit 9bb93cf

File tree

14 files changed

+730
-462
lines changed

14 files changed

+730
-462
lines changed

packages/deployment/docker/docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ include:
77
- ./sequencer/docker-compose.yml
88
# Enabled by worker profile
99
- ./worker/docker-compose.yml
10+
11+
- ./lightnet/docker-compose.yml
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
services:
2+
lightnet:
3+
image: o1labs/mina-local-network:compatible-latest-lightnet
4+
container_name: lightnet
5+
environment:
6+
- RUN_ARCHIVE_NODE=true
7+
- LOG_LEVEL=INFO
8+
- PROOF_LEVEL=none
9+
- NETWORK_TYPE=single-node
10+
ports:
11+
- 3085:3085
12+
- 8080:8080
13+
- 8181:8181
14+
# archive endpoints
15+
- 5432:5432
16+
- 8282:8282

packages/sequencer/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"test:file": "node --experimental-vm-modules --experimental-wasm-modules --experimental-wasm-threads ../../node_modules/jest/bin/jest.js",
1212
"test": "npm run test:file -- ./test/**",
1313
"test:watch": "npm run test:file -- ./test/** --watch",
14+
"integration": "npm run test:file -- ./test-integration/** --runInBand",
1415
"start": "npm run build && node --experimental-vm-modules --experimental-wasm-modules --experimental-wasm-threads --es-module-specifier-resolution=node ./dist/src/entry.js"
1516
},
1617
"main": "dist/index.js",
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { injectable, inject } from "tsyringe";
2+
import { range } from "@proto-kit/common";
3+
import { BaseLayer } from "../BaseLayer";
4+
import { MinaBaseLayer } from "../MinaBaseLayer";
5+
import { PrivateKey, Mina, Lightnet, PublicKey, AccountUpdate } from "o1js";
6+
import { MinaTransactionSender } from "../../../settlement/transactions/MinaTransactionSender";
7+
import { FeeStrategy } from "../fees/FeeStrategy";
8+
9+
type LocalBlockchain = Awaited<ReturnType<typeof Mina.LocalBlockchain>>;
10+
11+
@injectable()
12+
export class MinaBlockchainAccounts {
13+
public constructor(
14+
@inject("BaseLayer")
15+
private readonly baseLayer: BaseLayer,
16+
@inject("TransactionSender")
17+
private readonly transactionSender: MinaTransactionSender,
18+
@inject("FeeStrategy")
19+
private readonly feeStrategy: FeeStrategy
20+
) {}
21+
22+
private keysRetrieved = 0;
23+
24+
private isMinaBaseLayer(
25+
baseLayer: BaseLayer | undefined
26+
): baseLayer is MinaBaseLayer {
27+
return baseLayer !== undefined && baseLayer instanceof MinaBaseLayer;
28+
}
29+
30+
public async getFundedAccounts(num: number = 1): Promise<PrivateKey[]> {
31+
const { baseLayer } = this;
32+
if (!this.isMinaBaseLayer(baseLayer)) {
33+
throw new Error("Baselayer not defined or not subclass of MinaBaseLayer");
34+
}
35+
if (baseLayer.config.network.type === "local") {
36+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37+
const local = baseLayer.network! as LocalBlockchain;
38+
const accounts = local.testAccounts.slice(
39+
this.keysRetrieved,
40+
this.keysRetrieved + num
41+
);
42+
this.keysRetrieved += num;
43+
return accounts.map((acc) => acc.key);
44+
}
45+
if (baseLayer.config.network.type === "lightnet") {
46+
return await Promise.all(
47+
range(num).map(async (i) => {
48+
const pair = await Lightnet.acquireKeyPair({
49+
isRegularAccount: true,
50+
});
51+
return pair.privateKey;
52+
})
53+
);
54+
}
55+
throw new Error("Can't acquire keys for remote non-lighnet network");
56+
}
57+
58+
public async fundAccountFrom(
59+
sender: PrivateKey,
60+
receiver: PublicKey,
61+
amount: number
62+
) {
63+
const { baseLayer } = this;
64+
if (!this.isMinaBaseLayer(baseLayer)) {
65+
throw new Error("Baselayer not defined or not subclass of MinaBaseLayer");
66+
}
67+
if (baseLayer.config.network.type === "local") {
68+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
69+
(baseLayer.network as LocalBlockchain).addAccount(
70+
receiver,
71+
amount.toString()
72+
);
73+
} else {
74+
const tx = await Mina.transaction(
75+
{ sender: sender.toPublicKey(), fee: this.feeStrategy.getFee() },
76+
async () => {
77+
AccountUpdate.fundNewAccount(sender.toPublicKey());
78+
AccountUpdate.createSigned(sender.toPublicKey()).send({
79+
to: receiver,
80+
amount,
81+
});
82+
}
83+
);
84+
await this.transactionSender.proveAndSendTransaction(
85+
tx.sign([sender]),
86+
"included"
87+
);
88+
}
89+
}
90+
}

packages/sequencer/src/settlement/SettlementModule.ts

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
SettlementContractConfig,
1212
MandatorySettlementModulesRecord,
1313
MandatoryProtocolModulesRecord,
14+
BlockProverPublicOutput,
1415
} from "@proto-kit/protocol";
1516
import {
1617
AccountUpdate,
@@ -20,6 +21,7 @@ import {
2021
PublicKey,
2122
Signature,
2223
Transaction,
24+
fetchAccount,
2325
} from "o1js";
2426
import { inject } from "tsyringe";
2527
import {
@@ -45,6 +47,7 @@ import { AsyncMerkleTreeStore } from "../state/async/AsyncMerkleTreeStore";
4547
import { CachedMerkleTreeStore } from "../state/merkle/CachedMerkleTreeStore";
4648
import { BlockProofSerializer } from "../protocol/production/helpers/BlockProofSerializer";
4749
import { Settlement } from "../storage/model/Settlement";
50+
import { FeeStrategy } from "../protocol/baselayer/fees/FeeStrategy";
4851

4952
import { IncomingMessageAdapter } from "./messages/IncomingMessageAdapter";
5053
import type { OutgoingMessageQueue } from "./messages/WithdrawalQueue";
@@ -107,7 +110,9 @@ export class SettlementModule
107110
@inject("TransactionSender")
108111
private readonly transactionSender: MinaTransactionSender,
109112
@inject("AreProofsEnabled")
110-
private readonly areProofsEnabled: AreProofsEnabled
113+
private readonly areProofsEnabled: AreProofsEnabled,
114+
@inject("FeeStrategy")
115+
private readonly feeStrategy: FeeStrategy
111116
) {
112117
super();
113118
}
@@ -234,8 +239,8 @@ export class SettlementModule
234239
sender: feepayer.toPublicKey(),
235240
// eslint-disable-next-line no-plusplus
236241
nonce: nonce++,
237-
fee: String(0.01 * 1e9),
238-
memo: "Protokit settle",
242+
fee: this.feeStrategy.getFee(),
243+
memo: "roll up actions",
239244
},
240245
async () => {
241246
await settlement.rollupOutgoingMessages(
@@ -246,7 +251,10 @@ export class SettlementModule
246251

247252
const signedTx = this.signTransaction(tx, [feepayer]);
248253

249-
await this.transactionSender.proveAndSendTransaction(signedTx);
254+
await this.transactionSender.proveAndSendTransaction(
255+
signedTx,
256+
"included"
257+
);
250258

251259
this.outgoingMessageQueue.pop(OUTGOING_MESSAGE_BATCH_SIZE);
252260

@@ -259,33 +267,51 @@ export class SettlementModule
259267
}
260268
/* eslint-enable no-await-in-loop */
261269

270+
private async fetchContractAccounts() {
271+
const contracts = this.getContracts();
272+
if (contracts !== undefined) {
273+
const c1 = await fetchAccount({
274+
publicKey: contracts.settlement.address,
275+
tokenId: contracts.settlement.tokenId,
276+
});
277+
const c2 = await fetchAccount({
278+
publicKey: contracts.dispatch.address,
279+
tokenId: contracts.dispatch.tokenId,
280+
});
281+
}
282+
}
283+
262284
public async settleBatch(
263285
batch: SettleableBatch,
264286
options: {
265287
nonce?: number;
266288
} = {}
267289
): Promise<Settlement> {
290+
await this.fetchContractAccounts();
268291
const { settlement, dispatch } = this.getContracts();
269292
const { feepayer } = this.config;
270293

271294
log.debug("Preparing settlement");
272295

273-
const lastSettlementL1Block = settlement.lastSettlementL1Block.get().value;
296+
const lastSettlementL1BlockHeight =
297+
settlement.lastSettlementL1BlockHeight.get().value;
274298
const signature = Signature.create(feepayer, [
275299
BATCH_SIGNATURE_PREFIX,
276-
lastSettlementL1Block,
300+
lastSettlementL1BlockHeight,
277301
]);
278302

279-
const fromSequenceStateHash = dispatch.honoredMessagesHash.get();
280-
const latestSequenceStateHash = settlement.account.actionState.get();
303+
const fromSequenceStateHash = BlockProverPublicOutput.fromFields(
304+
batch.proof.publicOutput.map((x) => Field(x))
305+
).incomingMessagesHash;
306+
const latestSequenceStateHash = dispatch.account.actionState.get();
281307

282308
// Fetch actions and store them into the messageStorage
283309
const actions = await this.incomingMessagesAdapter.getPendingMessages(
284-
settlement.address,
310+
dispatch.address,
285311
{
286312
fromActionHash: fromSequenceStateHash.toString(),
287313
toActionHash: latestSequenceStateHash.toString(),
288-
fromL1Block: Number(lastSettlementL1Block.toString()),
314+
fromL1BlockHeight: Number(lastSettlementL1BlockHeight.toString()),
289315
}
290316
);
291317
await this.messageStorage.pushMessages(
@@ -302,7 +328,7 @@ export class SettlementModule
302328
{
303329
sender: feepayer.toPublicKey(),
304330
nonce: options?.nonce,
305-
fee: String(0.01 * 1e9),
331+
fee: this.feeStrategy.getFee(),
306332
memo: "Protokit settle",
307333
},
308334
async () => {
@@ -320,7 +346,7 @@ export class SettlementModule
320346

321347
this.signTransaction(tx, [feepayer]);
322348

323-
await this.transactionSender.proveAndSendTransaction(tx);
349+
await this.transactionSender.proveAndSendTransaction(tx, "included");
324350

325351
log.info("Settlement transaction send queued");
326352

@@ -355,16 +381,24 @@ export class SettlementModule
355381
? new SignedSettlementPermissions()
356382
: new ProvenSettlementPermissions();
357383

384+
if (this.baseLayer.config.network.type !== "local") {
385+
// const ac1 = await fetchAccount({ publicKey: feepayer });
386+
// console.log(ac1);
387+
}
388+
389+
// this.baseLayer
390+
// .originalNetwork!
358391
const tx = await Mina.transaction(
359392
{
360393
sender: feepayer,
361394
nonce,
362-
fee: String(0.01 * 1e9),
395+
fee: this.feeStrategy.getFee(),
363396
memo: "Protokit settlement deploy",
364397
},
365398
async () => {
366399
AccountUpdate.fundNewAccount(feepayer, 2);
367400
await settlement.deploy({
401+
// TODO Create compilation task that generates those artifacts if proofs enabled
368402
verificationKey: undefined,
369403
});
370404
settlement.account.permissions.set(permissions.settlementContract());
@@ -379,7 +413,7 @@ export class SettlementModule
379413

380414
// This should already apply the tx result to the
381415
// cached accounts / local blockchain
382-
await this.transactionSender.proveAndSendTransaction(tx);
416+
await this.transactionSender.proveAndSendTransaction(tx, "included");
383417

384418
this.addresses = {
385419
settlement: settlementKey.toPublicKey(),
@@ -394,7 +428,7 @@ export class SettlementModule
394428
{
395429
sender: feepayer,
396430
nonce: nonce + 1,
397-
fee: String(0.01 * 1e9),
431+
fee: this.feeStrategy.getFee(),
398432
memo: "Protokit settlement init",
399433
},
400434
async () => {
@@ -407,7 +441,10 @@ export class SettlementModule
407441

408442
const initTxSigned = this.signTransaction(initTx, [feepayerKey]);
409443

410-
await this.transactionSender.proveAndSendTransaction(initTxSigned);
444+
await this.transactionSender.proveAndSendTransaction(
445+
initTxSigned,
446+
"included"
447+
);
411448
}
412449

413450
public async start(): Promise<void> {

packages/sequencer/src/settlement/messages/IncomingMessageAdapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface IncomingMessageAdapter {
1414
params: {
1515
fromActionHash: string;
1616
toActionHash?: string;
17-
fromL1Block: number;
17+
fromL1BlockHeight: number;
1818
}
1919
) => Promise<{
2020
from: string;

packages/sequencer/src/settlement/messages/MinaIncomingMessageAdapter.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class MinaIncomingMessageAdapter implements IncomingMessageAdapter {
7373
params: {
7474
fromActionHash: string;
7575
toActionHash?: string;
76-
fromL1Block: number;
76+
fromL1BlockHeight: number;
7777
}
7878
): Promise<{
7979
from: string;
@@ -96,16 +96,20 @@ export class MinaIncomingMessageAdapter implements IncomingMessageAdapter {
9696
// : undefined,
9797
});
9898

99-
const events = await network.fetchEvents(address, undefined, {
100-
from: UInt32.from(Math.max(params.fromL1Block - 5, 0)),
101-
});
102-
10399
if ("error" in actions) {
104100
throw new Error(
105101
`Error ${actions.error.statusCode}: ${actions.error.statusText}`
106102
);
107103
}
108104

105+
if (actions.length > 0) {
106+
console.log(params.fromL1BlockHeight);
107+
}
108+
109+
const events = await network.fetchEvents(address, undefined, {
110+
from: UInt32.from(Math.max(params.fromL1BlockHeight - 5, 0)),
111+
});
112+
109113
const messages = await mapSequential(actions, async (action) => {
110114
// Find events corresponding to the transaction to get the raw args
111115
const tx = RuntimeTransaction.fromHashData(

0 commit comments

Comments
 (0)