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

Commit

Permalink
added mapping from pubkey hash to states
Browse files Browse the repository at this point in the history
database connection to retrieve state from pubkeys
updated rpc endpoints
added cors plugin to enable requests
updated rpc and local fee receiver config
fixed toNumber bug in deserialize method
changed max url length and status endpoint
added test case for pubkey2states db
  • Loading branch information
kautukkundan committed Aug 23, 2021
1 parent ea02b74 commit 1661810
Show file tree
Hide file tree
Showing 10 changed files with 571 additions and 347 deletions.
2 changes: 1 addition & 1 deletion config.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"willingnessToBid": 1,
"maxPendingTransactions": 1024,
"feeReceivers": [{
"tokenID": 1,
"tokenID": 0,
"stateID": 0
}]
}
Expand Down
671 changes: 345 additions & 326 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"bn.js": "^5.2.0",
"ethers": "5.4.3",
"fastify": "^3.18.0",
"fastify-cors": "^6.0.2",
"level": "^7.0.0",
"lodash": "^4.17.21",
"mcl-wasm": "0.4.5",
Expand Down
70 changes: 70 additions & 0 deletions test/client/pubkey2states.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import chai, { assert } from "chai";
import chaiAsPromised from "chai-as-promised";
import { StateDatabaseEngine } from "../../ts/client/database";
import { PRODUCTION_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";

chai.use(chaiAsPromised);

const {
MAX_DEPTH: maxDepth,
MAX_DEPOSIT_SUBTREE_DEPTH: maxSubtreeDepth
} = PRODUCTION_PARAMS;

describe("StateDBEngine", () => {
let engine: StateDatabaseEngine;
let p0: PubkeyLeaf;
let p1: PubkeyLeaf;
let statesP0: State[];
let statesP1: State[];

before(async function() {
await del("./leveldb/*");
});

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

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

await init();

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

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

for (let i = 0; i < 4; i++) {
statesP0.push(State.new(p0.itemID, i, i, i));
}

for (let i = 0; i < 2; i++) {
statesP1.push(State.new(p1.itemID, i, i, i));
}
});
it("update pubkey with states", async function() {
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
]);
});
});
1 change: 1 addition & 0 deletions ts/client/database/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export const close = async (): Promise<void> => {
export const pubkeyDB = sub(db, "pubkey");
export const stateDB = sub(db, "state");
export const nodeDB = sub(db, "node");
export const pubkey2statesDB = sub(db, "pubkey2states");
29 changes: 29 additions & 0 deletions ts/client/database/pubkey2states.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import _ from "lodash";
import { PubkeyLeafFactory } from "../../tree/leaves/PubkeyLeaf";
import { pubkey2statesDB } from "./connection";

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

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

try {
const states: string = await pubkey2statesDB.get(pubkeyHash);
const appended = _.union<number>(JSON.parse(states), [stateID]);
await pubkey2statesDB.put(pubkeyHash, JSON.stringify(appended));
} catch (error) {
if (error.name === "NotFoundError") {
await pubkey2statesDB.put(
pubkeyHash,
JSON.stringify([stateID])
);
} else {
throw error;
}
}
}
}
13 changes: 13 additions & 0 deletions ts/client/database/stateEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { State } from "../../state";
import { StateLeafFactory } from "../../tree/leaves/StateLeaf";
import { StorageEngine } from "../storageEngine/interfaces";
import { DatabaseEngine } from "./databaseEngine";
import { Pubkey2StatesDB } from "./pubkey2states";

export interface StateStorageEngine extends StorageEngine<State> {}

Expand All @@ -10,4 +11,16 @@ export class StateDatabaseEngine extends DatabaseEngine<State>
constructor(depth: number) {
super(depth, StateLeafFactory());
}

public async updateBatch(
path: number,
depth: number,
items: State[]
): Promise<void> {
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);
}
}
}
6 changes: 3 additions & 3 deletions ts/client/features/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,11 @@ export class TransferOffchainTx extends TransferCompressedTx
const signature = { sol: solG1, mcl: mclG1 };

return new this(
obj.fromIndex.toNumber(),
obj.toIndex.toNumber(),
obj.fromIndex,
obj.toIndex,
obj.amount,
obj.fee,
obj.nonce.toNumber(),
obj.nonce,
signature
);
}
Expand Down
2 changes: 1 addition & 1 deletion ts/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async function main() {
console.info("Starting Hubble node...");
const config = await configFromPath(configPath);

const fast = fastify({ logger: true });
const fast = fastify({ logger: true, maxParamLength: 512 });
fast.setErrorHandler(console.error);

const node = await HubbleNode.init(config, fast);
Expand Down
123 changes: 107 additions & 16 deletions ts/client/services/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { arrayify } from "@ethersproject/bytes";
import { FastifyInstance } from "fastify";
import { CoreAPI } from "../coreAPI";
import { Pubkey2StatesDB } from "../database/pubkey2states";
import { ITransferPool, TransferOffchainTx } from "../features/transfer";
import cors from "fastify-cors";

const tx = {
schema: {
Expand All @@ -20,30 +22,115 @@ export class RPC {
fastify: FastifyInstance,
transferPool?: ITransferPool
) {
fastify.register(cors, {
origin: "*"
});
fastify.get<{ Params: { stateID: number } }>(
"/user/state/:stateID",
async function(request) {
const stateID = request.params.stateID;
const state = await l2Storage.state.get(stateID);
return state.toJSON();
async function(request, reply) {
try {
const { stateID } = request.params;
const state = await l2Storage.state.get(stateID);
return state.toJSON();
} catch (error) {
if (error.name === "NotFoundError") {
return reply
.status(404)
.send({ error: "pubkey not found" });
} else {
console.error(error);
return reply.status(500);
}
}
}
);
fastify.get<{ Params: { pubkeyHash: string } }>(
"/user/state/pubkey/:pubkeyHash",
async function(request, reply) {
try {
const { pubkeyHash } = request.params;
const stateIndices = await Pubkey2StatesDB.getStates(
pubkeyHash
);
let data = stateIndices.map(async id => {
let state = await l2Storage.state.get(Number(id));
return {
stateId: id,
balance: state.balance.toString(),
tokenId: state.tokenID.toString(),
nonce: state.nonce.toString()
};
});
return { states: await Promise.all(data) };
} catch (error) {
if (error.name === "NotFoundError") {
return reply
.status(404)
.send({ error: "pubkey not found" });
} else {
console.error(error);
return reply.status(500);
}
}
}
);
fastify.get<{ Params: { pubkeyID: number } }>(
"/user/pubkey/hash/:pubkeyID",
async function(request, reply) {
try {
const { pubkeyID } = request.params;
const pubkey = await l2Storage.pubkey.get(pubkeyID);
return { hash: pubkey.hash() };
} catch (error) {
return reply
.status(404)
.send({ error: "pubkey not found" });
}
}
);
fastify.get<{ Params: { pubkeyHash: string } }>(
"/user/pubkey/id/:pubkeyHash",
async function(request, reply) {
try {
const { pubkeyHash } = request.params;
const stateIndices = await Pubkey2StatesDB.getStates(
pubkeyHash
);
let data = await l2Storage.state.get(
Number(stateIndices[0])
);
return { id: data.pubkeyID.toNumber() };
} catch (error) {
if (error.name === "NotFoundError") {
return reply
.status(404)
.send({ error: "pubkey not found" });
} else {
console.error(error);
return reply.status(500);
}
}
}
);
fastify.post<{ Body: { bytes: string } }>(
"/tx",
tx,
async (request, reply) => {
if (!transferPool) {
reply.status(409).send("not a proposer");
return;
}

const bytes = arrayify(request.body.bytes);
const transfer = TransferOffchainTx.deserialize(bytes);
console.log(transfer.toString());
try {
if (!transferPool) {
reply.status(409).send("not a proposer");
return;
}

await transferPool.push(transfer);
await l2Storage.transactions.pending(transfer);
return { txHash: transfer.hash() };
const bytes = arrayify(request.body.bytes);
const transfer = TransferOffchainTx.deserialize(bytes);
await transferPool.push(transfer);
await l2Storage.transactions.pending(transfer);
return { txHash: transfer.hash() };
} catch (error) {
console.error(error);
return reply.status(500);
}
}
);
fastify.get<{ Params: { txMsg: string } }>(
Expand All @@ -57,7 +144,11 @@ export class RPC {
}
// In the future, we may want to clean up
// this JSON serialization to something more minimal.
return JSON.stringify(txStatus);
return JSON.stringify({
status: txStatus.status,
l1BlockIncluded: txStatus.l1BlockIncluded,
l1TxnHash: txStatus.l1TxnHash
});
}
);
}
Expand Down

0 comments on commit 1661810

Please sign in to comment.