Skip to content
This repository has been archived by the owner on Jun 29, 2023. It is now read-only.

Parameterized Database #668

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions test/client/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ import { CustomToken__factory } from "../../types/ethers-contracts";
*/
describe("Client Integration", function() {
before(async function() {
await del("./leveldb/*");
await del("./leveldb/test/syncer/*");
await del("./leveldb/test/packer/*");
});

after(async function() {
await del("./leveldb/*");
await del("./leveldb/test/syncer/*");
await del("./leveldb/test/packer/*");
});

it("run", async function() {
Expand All @@ -47,8 +49,12 @@ describe("Client Integration", function() {

await deployKeyless(signer, false);

const storageSyncer = await storageManagerFactory();
const storagePacker = await storageManagerFactory();
const storageSyncer = await storageManagerFactory({
storageDirectory: "./levelDB/test/syncer"
});
const storagePacker = await storageManagerFactory({
storageDirectory: "./levelDB/test/packer"
});

// Ensure initial states match
assert.equal(storageSyncer.state.root, storagePacker.state.root);
Expand Down
48 changes: 32 additions & 16 deletions test/client/pubkey2states.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import chai, { assert } from "chai";
import chaiAsPromised from "chai-as-promised";
import { StateDatabaseEngine } from "../../ts/client/database";
import { PRODUCTION_PARAMS } from "../../ts/constants";
import { PRODUCTION_PARAMS, TESTING_PARAMS } from "../../ts/constants";
import { State } from "../../ts/state";
import del from "del";
import { PubkeyLeaf } from "../../ts/tree/leaves/PubkeyLeaf";
import { init } from "../../ts/mcl";
import { BlsSigner } from "../../ts/blsSigner";
import { Pubkey2StatesDB } from "../../ts/client/database/pubkey2states";
import { Connection } from "../../ts/client/database/connection";

chai.use(chaiAsPromised);

Expand All @@ -22,26 +23,37 @@ describe("StateDBEngine", () => {
let p1: PubkeyLeaf;
let statesP0: State[];
let statesP1: State[];
let connection: Connection;

before(async function() {
await del("./leveldb/*");
await del("./leveldb/test/*");
connection = await Connection.create(TESTING_PARAMS.STORAGE_DIRECTORY);
});

after(async function() {
await del("./leveldb/*");
await connection.close();
await del("./leveldb/test/*");
});

beforeEach(async function() {
engine = new StateDatabaseEngine(maxDepth);
engine = new StateDatabaseEngine(maxDepth, connection);
statesP0 = [];
statesP1 = [];

await init();

p0 = PubkeyLeaf.fromSolG2(BlsSigner.new().pubkey, 0);
p0 = PubkeyLeaf.fromSolG2(
BlsSigner.new().pubkey,
0,
connection.pubkeyDB
);
await p0.toDB();

p1 = PubkeyLeaf.fromSolG2(BlsSigner.new().pubkey, 1);
p1 = PubkeyLeaf.fromSolG2(
BlsSigner.new().pubkey,
1,
connection.pubkeyDB
);
await p1.toDB();

for (let i = 0; i < 4; i++) {
Expand All @@ -56,15 +68,19 @@ describe("StateDBEngine", () => {
await engine.updateBatch(0, maxSubtreeDepth, statesP0);
await engine.updateBatch(1, maxSubtreeDepth, statesP1);

assert.deepEqual(await Pubkey2StatesDB.getStates(p0.item.hash()), [
0,
1,
2,
3
]);
assert.deepEqual(await Pubkey2StatesDB.getStates(p1.item.hash()), [
4,
5
]);
assert.deepEqual(
await Pubkey2StatesDB.getStates(
p0.item.hash(),
connection.pubkey2statesDB
),
[0, 1, 2, 3]
);
assert.deepEqual(
await Pubkey2StatesDB.getStates(
p1.item.hash(),
connection.pubkey2statesDB
),
[4, 5]
);
});
});
11 changes: 10 additions & 1 deletion test/client/transaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import chai, { assert } from "chai";
import chaiAsPromised from "chai-as-promised";
import del from "del";
import { BlsSigner } from "../../ts/blsSigner";
import { Connection } from "../../ts/client/database/connection";
import { OffchainTx } from "../../ts/client/features/interface";
import { TransferOffchainTx } from "../../ts/client/features/transfer";
import { Status } from "../../ts/client/storageEngine/transactions/constants";
import { TransactionDBStorage } from "../../ts/client/storageEngine/transactions/db";
import { TESTING_PARAMS } from "../../ts/constants";
import {
StatusTransitionInvalid,
TransactionAlreadyExists,
Expand Down Expand Up @@ -38,11 +40,18 @@ const txFactory = (
};

describe("TransactionDBStorage", () => {
let storage = new TransactionDBStorage();
let connection: Connection;
let storage: TransactionDBStorage;

before(async function() {
await del("./leveldb/*");
await mcl.init();
connection = await Connection.create(TESTING_PARAMS.STORAGE_DIRECTORY);
storage = new TransactionDBStorage(connection);
});

after(async function() {
await connection.close();
});

describe("get", () => {
Expand Down
46 changes: 36 additions & 10 deletions ts/client/database/connection.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,41 @@
import level from "level";
import sub from "subleveldown";
import { LevelUp } from "levelup";

const db = level("./leveldb", { valueEncoding: "json" });
import { mkdir, stat } from "fs";
import { promisify } from "util";

export const close = async (): Promise<void> => {
await db.close();
};
const mkdirAsync = promisify(mkdir);
const existsAsync = promisify(stat);

export const pubkeyDB = sub(db, "pubkey");
export const stateDB = sub(db, "state");
export const nodeDB = sub(db, "node");
// export const batchDB = sub(db, "batch");
export const txDB = sub(db, "tx");
export const pubkey2statesDB = sub(db, "pubkey2states");
export class Connection {
public readonly pubkeyDB: LevelUp;
public readonly stateDB: LevelUp;
public readonly txDB: LevelUp;
public readonly pubkey2statesDB: LevelUp;

private db: LevelUp;

constructor(path: string) {
this.db = level(`${path}`, { valueEncoding: "json" });

this.pubkeyDB = sub(this.db, "pubkey");
this.stateDB = sub(this.db, "state");
this.txDB = sub(this.db, "tx");
this.pubkey2statesDB = sub(this.db, "pubkey2states");
}

public static async create(path: string) {
try {
await existsAsync(path);
} catch (error) {
await mkdirAsync(path, { recursive: true });
}

return new Connection(path);
}

public async close(): Promise<void> {
await this.db.close();
}
}
4 changes: 3 additions & 1 deletion ts/client/database/databaseEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import { Hasher } from "../../tree";
import { DBTree } from "../../tree/dbTree";
import { LeafFactoryFunc } from "../../tree/leaves/Leaf";
import { StorageEngine, WithWitness } from "../storageEngine/interfaces";
import { LevelUp } from "levelup";

export class DatabaseEngine<Item extends Hashable>
implements StorageEngine<Item> {
private tree: DBTree;
public readonly leafFactory: LeafFactoryFunc<Item>;

constructor(depth: number, factory: LeafFactoryFunc<Item>) {
constructor(depth: number, db: LevelUp, factory: LeafFactoryFunc<Item>) {
this.tree = DBTree.new(
depth,
factory.name,
db,
Hasher.new("bytes", ZERO_BYTES32)
);
this.leafFactory = factory;
Expand Down
16 changes: 12 additions & 4 deletions ts/client/database/pubkey2states.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import _ from "lodash";
import { PubkeyLeafFactory } from "../../tree/leaves/PubkeyLeaf";
import { pubkey2statesDB } from "./connection";
import { LevelUp } from "levelup";

export class Pubkey2StatesDB {
static async getStates(pubkeyHash: string): Promise<number[]> {
static async getStates(
pubkeyHash: string,
pubkey2statesDB: LevelUp
): Promise<number[]> {
return JSON.parse(await pubkey2statesDB.get(pubkeyHash));
}

static async update(pubkeyID: number, stateID: number): Promise<void> {
const pubkeyLeaf = await PubkeyLeafFactory().fromDB(pubkeyID);
static async update(
pubkeyID: number,
stateID: number,
pubkey2statesDB: LevelUp,
pubkeyDB: LevelUp
): Promise<void> {
const pubkeyLeaf = await PubkeyLeafFactory(pubkeyDB).fromDB(pubkeyID);
const pubkeyHash = pubkeyLeaf.item.hash();

try {
Expand Down
9 changes: 7 additions & 2 deletions ts/client/database/pubkeyEngine.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { Pubkey } from "../../pubkey";
import { PubkeyLeafFactory } from "../../tree/leaves/PubkeyLeaf";
import { StorageEngine } from "../storageEngine/interfaces";
import { Connection } from "./connection";
import { DatabaseEngine } from "./databaseEngine";

export interface PubkeyStorageEngine extends StorageEngine<Pubkey> {}

export class PubkeyDatabaseEngine extends DatabaseEngine<Pubkey>
implements PubkeyStorageEngine {
constructor(depth: number) {
super(depth, PubkeyLeafFactory());
constructor(depth: number, connections: Connection) {
super(
depth,
connections.pubkeyDB,
PubkeyLeafFactory(connections.pubkeyDB)
);
}
}
16 changes: 13 additions & 3 deletions ts/client/database/stateEngine.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { State } from "../../state";
import { StateLeafFactory } from "../../tree/leaves/StateLeaf";
import { StorageEngine } from "../storageEngine/interfaces";
import { Connection } from "./connection";
import { DatabaseEngine } from "./databaseEngine";
import { Pubkey2StatesDB } from "./pubkey2states";

export interface StateStorageEngine extends StorageEngine<State> {}

export class StateDatabaseEngine extends DatabaseEngine<State>
implements StateStorageEngine {
constructor(depth: number) {
super(depth, StateLeafFactory());
constructor(depth: number, public readonly connections: Connection) {
super(
depth,
connections.stateDB,
StateLeafFactory(connections.stateDB)
);
}

public async updateBatch(
Expand All @@ -20,7 +25,12 @@ export class StateDatabaseEngine extends DatabaseEngine<State>
for (const [i, item] of items.entries()) {
const itemID = path * 2 ** depth + i;
await this.update(itemID, item);
await Pubkey2StatesDB.update(item.pubkeyID.toNumber(), itemID);
await Pubkey2StatesDB.update(
item.pubkeyID.toNumber(),
itemID,
this.connections.pubkey2statesDB,
this.connections.pubkeyDB
);
}
}
}
7 changes: 3 additions & 4 deletions ts/client/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { SyncCompleteEvent } from "./constants";
import { ClientConfig } from "./config";
import { Genesis } from "../genesis";
import { EmptyConfigPropError, MissingConfigPropError } from "../exceptions";
import { close as closeDB } from "./database/connection";

export type NodeModes = {
isProposer: boolean;
Expand Down Expand Up @@ -45,10 +44,11 @@ export class HubbleNode {
console.error(err);
});

const { MAX_DEPTH } = genesis.parameters;
const { MAX_DEPTH, STORAGE_DIRECTORY } = genesis.parameters;
const storageManager = await storageManagerFactory({
stateTreeDepth: MAX_DEPTH,
pubkeyTreeDepth: MAX_DEPTH
pubkeyTreeDepth: MAX_DEPTH,
storageDirectory: STORAGE_DIRECTORY
});

const signer = provider.getSigner();
Expand Down Expand Up @@ -133,7 +133,6 @@ export class HubbleNode {
this.packer?.stop();
this.bidder?.stop();
console.log("closing leveldb connection");
await closeDB();
}

onSyncComplete = async () => {
Expand Down
6 changes: 4 additions & 2 deletions ts/client/services/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export class RPC {
try {
const { pubkeyHash } = request.params;
const stateIndices = await Pubkey2StatesDB.getStates(
pubkeyHash
pubkeyHash,
l2Storage.connection.pubkey2statesDB
);
let data = stateIndices.map(async id => {
let state = await l2Storage.state.get(Number(id));
Expand Down Expand Up @@ -94,7 +95,8 @@ export class RPC {
try {
const { pubkeyHash } = request.params;
const stateIndices = await Pubkey2StatesDB.getStates(
pubkeyHash
pubkeyHash,
l2Storage.connection.pubkey2statesDB
);
let data = await l2Storage.state.get(
Number(stateIndices[0])
Expand Down
2 changes: 2 additions & 0 deletions ts/client/storageEngine/storageManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Connection } from "../database/connection";
import { BatchStorage } from "./batches/interfaces";
import { PubkeyStorageEngine } from "./pubkeyEngine";
import { StateStorageEngine } from "./stateEngine";
Expand All @@ -11,4 +12,5 @@ export interface StorageManager {
state: StateStorageEngine;
batches: BatchStorage;
transactions: TransactionStorage;
connection: Connection;
}
9 changes: 7 additions & 2 deletions ts/client/storageEngine/transactions/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
TransactionAlreadyExists,
TransactionDoesNotExist
} from "../../../exceptions";
import { txDB } from "../../database/connection";
import { OffchainTx } from "../../features/interface";
import { TransferOffchainTx } from "../../features/transfer";
import { Status } from "./constants";
Expand All @@ -15,12 +14,18 @@ import {
TransactionStorage,
TransationMessageOrObject
} from "./interfaces";
import { LevelUp } from "levelup";
import { Connection } from "../../database/connection";

/**
* levelDB implementation of TransactionStorage
*/
export class TransactionDBStorage implements TransactionStorage {
private readonly db = txDB;
private readonly db: LevelUp;

constructor(connection: Connection) {
this.db = connection.txDB;
}

public async get(
msgOrTxn: TransationMessageOrObject
Expand Down
Loading