Skip to content

Commit e96b22a

Browse files
committed
Added Prisma and Redis Creators
1 parent 16685c6 commit e96b22a

File tree

6 files changed

+118
-33
lines changed

6 files changed

+118
-33
lines changed

packages/persistance/src/PrismaDatabaseConnection.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { PrismaBlockStorage } from "./services/prisma/PrismaBlockStorage";
1212
import { PrismaSettlementStorage } from "./services/prisma/PrismaSettlementStorage";
1313
import { PrismaMessageStorage } from "./services/prisma/PrismaMessageStorage";
1414
import { PrismaTransactionStorage } from "./services/prisma/PrismaTransactionStorage";
15+
import { PrismaStateServiceCreator } from "./creators/PrismaStateServiceCreator";
1516

1617
export interface PrismaDatabaseConfig {
1718
// Either object-based config or connection string
@@ -49,12 +50,9 @@ export class PrismaDatabaseConnection
4950

5051
public dependencies(): OmitKeys<
5152
StorageDependencyMinimumDependencies,
52-
"asyncMerkleStore" | "blockTreeStore" | "unprovenMerkleStore"
53+
"blockTreeStore" | "treeStoreCreator"
5354
> {
5455
return {
55-
asyncStateService: {
56-
useFactory: () => new PrismaStateService(this, "batch"),
57-
},
5856
batchStorage: {
5957
useClass: PrismaBatchStore,
6058
},
@@ -73,8 +71,8 @@ export class PrismaDatabaseConnection
7371
transactionStorage: {
7472
useClass: PrismaTransactionStorage,
7573
},
76-
unprovenStateService: {
77-
useFactory: () => new PrismaStateService(this, "block"),
74+
stateServiceCreator: {
75+
useClass: PrismaStateServiceCreator,
7876
},
7977
};
8078
}

packages/persistance/src/RedisConnection.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { DependencyFactory } from "@proto-kit/common";
77
import isArray from "lodash/isArray";
88

99
import { RedisMerkleTreeStore } from "./services/redis/RedisMerkleTreeStore";
10+
import { RedisTreeStoreCreator } from "./creators/RedisTreeStoreCreator";
1011

1112
export interface RedisConnectionConfig {
1213
host: string;
@@ -39,18 +40,15 @@ export class RedisConnectionModule
3940

4041
public dependencies(): Pick<
4142
StorageDependencyMinimumDependencies,
42-
"asyncMerkleStore" | "blockTreeStore" | "unprovenMerkleStore"
43+
"blockTreeStore" | "treeStoreCreator"
4344
> {
4445
return {
45-
asyncMerkleStore: {
46-
useFactory: () => new RedisMerkleTreeStore(this),
47-
},
48-
unprovenMerkleStore: {
49-
useFactory: () => new RedisMerkleTreeStore(this, "unproven"),
50-
},
5146
blockTreeStore: {
5247
useFactory: () => new RedisMerkleTreeStore(this, "blockHash"),
5348
},
49+
treeStoreCreator: {
50+
useClass: RedisTreeStoreCreator,
51+
},
5452
};
5553
}
5654

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { AsyncStateService, StateServiceCreator } from "@proto-kit/sequencer";
2+
import { inject, injectable } from "tsyringe";
3+
4+
import { PrismaStateService } from "../services/prisma/PrismaStateService";
5+
import type { PrismaConnection } from "../PrismaDatabaseConnection";
6+
7+
@injectable()
8+
export class PrismaStateServiceCreator implements StateServiceCreator {
9+
public constructor(
10+
@inject("Database") private readonly connection: PrismaConnection
11+
) {}
12+
13+
public async createMask(
14+
name: string,
15+
parent: string
16+
): Promise<AsyncStateService> {
17+
return new PrismaStateService(this.connection, name, parent);
18+
}
19+
20+
public getMask(name: string): AsyncStateService {
21+
return new PrismaStateService(this.connection, name);
22+
}
23+
24+
public async mergeIntoParent(name: string): Promise<void> {
25+
const service = new PrismaStateService(this.connection, name);
26+
await service.mergeIntoParent();
27+
}
28+
29+
public async drop(name: string): Promise<void> {
30+
const service = new PrismaStateService(this.connection, name);
31+
await service.drop();
32+
}
33+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {
2+
AsyncMerkleTreeStore,
3+
InMemoryMerkleTreeStoreMask,
4+
MaskGraph,
5+
TreeStoreCreator,
6+
} from "@proto-kit/sequencer";
7+
import { RedisMerkleTreeStore } from "../services/redis/RedisMerkleTreeStore";
8+
9+
export class RedisTreeStoreCreator
10+
extends MaskGraph<
11+
AsyncMerkleTreeStore,
12+
RedisMerkleTreeStore,
13+
InMemoryMerkleTreeStoreMask
14+
>
15+
implements TreeStoreCreator {}

packages/persistance/src/services/prisma/PrismaStateService.ts

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AsyncStateService, StateEntry } from "@proto-kit/sequencer";
1+
import { AsyncStateService, MaskName, StateEntry } from "@proto-kit/sequencer";
22
import { Field } from "o1js";
33
import { Prisma } from "@prisma/client";
44
import { noop } from "@proto-kit/common";
@@ -20,53 +20,85 @@ const Decimal = Prisma.Decimal.clone({
2020
export class PrismaStateService implements AsyncStateService {
2121
private cache: StateEntry[] = [];
2222

23-
private maskId?: number;
23+
private maskId?: { id: number; parentId?: number };
2424

2525
/**
2626
* @param connection
2727
* @param mask A indicator to which masking level the values belong.
2828
* This name has to be unique
29-
* @param parent
29+
* @param parentName
3030
*/
3131
public constructor(
3232
private readonly connection: PrismaConnection,
3333
private readonly mask: string,
34-
private readonly parent?: number
34+
private readonly parentName?: string
3535
) {}
3636

37-
private async getMaskId(): Promise<number> {
37+
private async getMaskId(): Promise<{ id: number; parentId?: number }> {
3838
if (this.maskId === undefined) {
39-
this.maskId = await this.initializeMask(this.mask, this.parent);
39+
this.maskId = await this.initializeMask(this.mask, this.parentName);
4040
}
4141
return this.maskId;
4242
}
4343

44-
private async initializeMask(mask: string, parent?: number): Promise<number> {
44+
private async initializeMask(
45+
mask: string,
46+
parentName?: string
47+
): Promise<{ id: number; parentId?: number }> {
4548
const { prismaClient } = this.connection;
4649

4750
const found = await prismaClient.mask.findFirst({
4851
where: {
4952
name: mask,
50-
parent,
53+
},
54+
include: {
55+
parentMask: true,
5156
},
5257
});
5358

5459
if (found === null) {
60+
// Find parent id
61+
let parentId: number | undefined = undefined;
62+
if (parentName !== undefined) {
63+
const parent = await prismaClient.mask.findFirst({
64+
where: {
65+
name: parentName,
66+
},
67+
});
68+
if (parent === null) {
69+
throw new Error(`Parent mask with name ${parentName} not found`);
70+
}
71+
72+
parentId = parent.id;
73+
} else if (mask !== MaskName.base()) {
74+
throw new Error(
75+
"Can't initialize mask that's not the base using a null-parent "
76+
);
77+
}
78+
79+
// Create mask
5580
const createdMask = await prismaClient.mask.create({
5681
data: {
57-
parent,
82+
parent: parentId,
5883
name: mask,
5984
},
6085
});
61-
return createdMask.id;
86+
return {
87+
id: createdMask.id,
88+
parentId: parentId,
89+
};
6290
}
63-
return found.id;
91+
92+
return {
93+
id: found.id,
94+
parentId: found.parent ?? undefined,
95+
};
6496
}
6597

6698
public async commit(): Promise<void> {
6799
const { prismaClient } = this.connection;
68100

69-
const maskId = await this.getMaskId();
101+
const { id: maskId } = await this.getMaskId();
70102

71103
const data = this.cache
72104
.filter((entry) => entry.value !== undefined)
@@ -92,7 +124,7 @@ export class PrismaStateService implements AsyncStateService {
92124
}
93125

94126
public async getMany(keys: Field[]): Promise<StateEntry[]> {
95-
const maskId = await this.getMaskId();
127+
const { id: maskId } = await this.getMaskId();
96128
const paths = keys.map((key) => new Decimal(key.toString()));
97129

98130
const records: {
@@ -123,16 +155,18 @@ export class PrismaStateService implements AsyncStateService {
123155
}
124156

125157
public async createMask(name: string): Promise<AsyncStateService> {
126-
const maskId = await this.getMaskId();
127-
return new PrismaStateService(this.connection, name, maskId);
158+
// We only call this to make sure this mask actually exists, therefore that the
159+
// relation can be satisfied
160+
await this.getMaskId();
161+
return new PrismaStateService(this.connection, name, this.mask);
128162
}
129163

130164
public async mergeIntoParent(): Promise<void> {
131-
const maskId = await this.getMaskId();
165+
const { id: maskId, parentId } = await this.getMaskId();
132166

133167
const client = this.connection.prismaClient;
134168

135-
if (this.parent !== undefined) {
169+
if (parentId !== undefined) {
136170
// Rough strategy here:
137171
// 1. Delete all entries that are bound to be overwritten from the parent mask
138172
// 2. Update this mask's entries to parent mask id
@@ -150,7 +184,7 @@ export class PrismaStateService implements AsyncStateService {
150184
parent: maskId,
151185
},
152186
data: {
153-
parent: this.parent,
187+
parent: parentId,
154188
},
155189
}),
156190
client.mask.delete({
@@ -165,7 +199,7 @@ export class PrismaStateService implements AsyncStateService {
165199
}
166200

167201
public async drop(): Promise<void> {
168-
const maskId = await this.getMaskId();
202+
const { id: maskId } = await this.getMaskId();
169203

170204
await this.connection.prismaClient.state.deleteMany({
171205
where: {

packages/persistance/src/services/redis/RedisMerkleTreeStore.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
AsyncMerkleTreeStore,
3+
InMemoryMerkleTreeStoreMask,
34
MerkleTreeNode,
45
MerkleTreeNodeQuery,
56
} from "@proto-kit/sequencer";
@@ -10,9 +11,11 @@ import type { RedisConnection } from "../../RedisConnection";
1011
export class RedisMerkleTreeStore implements AsyncMerkleTreeStore {
1112
private cache: MerkleTreeNode[] = [];
1213

14+
public readonly name = "base";
15+
1316
public constructor(
1417
private readonly connection: RedisConnection,
15-
private readonly mask: string = "base"
18+
private readonly mask: string
1619
) {}
1720

1821
private getKey(node: MerkleTreeNodeQuery): string {
@@ -80,4 +83,8 @@ export class RedisMerkleTreeStore implements AsyncMerkleTreeStore {
8083
// });
8184
// console.log(`Reduced ${concat.length} to ${this.cache.length} items to write`)
8285
}
86+
87+
public async createMask(name: string) {
88+
return new InMemoryMerkleTreeStoreMask(this, name);
89+
}
8390
}

0 commit comments

Comments
 (0)