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

Commit e09c1fc

Browse files
authored
Merge pull request #647 from thehubbleproject/pubkey2states
added mapping from pubkey hash to states
2 parents ea02b74 + 1661810 commit e09c1fc

File tree

10 files changed

+571
-347
lines changed

10 files changed

+571
-347
lines changed

config.local.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"willingnessToBid": 1,
1111
"maxPendingTransactions": 1024,
1212
"feeReceivers": [{
13-
"tokenID": 1,
13+
"tokenID": 0,
1414
"stateID": 0
1515
}]
1616
}

package-lock.json

Lines changed: 345 additions & 326 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"bn.js": "^5.2.0",
6262
"ethers": "5.4.3",
6363
"fastify": "^3.18.0",
64+
"fastify-cors": "^6.0.2",
6465
"level": "^7.0.0",
6566
"lodash": "^4.17.21",
6667
"mcl-wasm": "0.4.5",

test/client/pubkey2states.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import chai, { assert } from "chai";
2+
import chaiAsPromised from "chai-as-promised";
3+
import { StateDatabaseEngine } from "../../ts/client/database";
4+
import { PRODUCTION_PARAMS } from "../../ts/constants";
5+
import { State } from "../../ts/state";
6+
import del from "del";
7+
import { PubkeyLeaf } from "../../ts/tree/leaves/PubkeyLeaf";
8+
import { init } from "../../ts/mcl";
9+
import { BlsSigner } from "../../ts/blsSigner";
10+
import { Pubkey2StatesDB } from "../../ts/client/database/pubkey2states";
11+
12+
chai.use(chaiAsPromised);
13+
14+
const {
15+
MAX_DEPTH: maxDepth,
16+
MAX_DEPOSIT_SUBTREE_DEPTH: maxSubtreeDepth
17+
} = PRODUCTION_PARAMS;
18+
19+
describe("StateDBEngine", () => {
20+
let engine: StateDatabaseEngine;
21+
let p0: PubkeyLeaf;
22+
let p1: PubkeyLeaf;
23+
let statesP0: State[];
24+
let statesP1: State[];
25+
26+
before(async function() {
27+
await del("./leveldb/*");
28+
});
29+
30+
after(async function() {
31+
await del("./leveldb/*");
32+
});
33+
34+
beforeEach(async function() {
35+
engine = new StateDatabaseEngine(maxDepth);
36+
statesP0 = [];
37+
statesP1 = [];
38+
39+
await init();
40+
41+
p0 = PubkeyLeaf.fromSolG2(BlsSigner.new().pubkey, 0);
42+
await p0.toDB();
43+
44+
p1 = PubkeyLeaf.fromSolG2(BlsSigner.new().pubkey, 1);
45+
await p1.toDB();
46+
47+
for (let i = 0; i < 4; i++) {
48+
statesP0.push(State.new(p0.itemID, i, i, i));
49+
}
50+
51+
for (let i = 0; i < 2; i++) {
52+
statesP1.push(State.new(p1.itemID, i, i, i));
53+
}
54+
});
55+
it("update pubkey with states", async function() {
56+
await engine.updateBatch(0, maxSubtreeDepth, statesP0);
57+
await engine.updateBatch(1, maxSubtreeDepth, statesP1);
58+
59+
assert.deepEqual(await Pubkey2StatesDB.getStates(p0.item.hash()), [
60+
0,
61+
1,
62+
2,
63+
3
64+
]);
65+
assert.deepEqual(await Pubkey2StatesDB.getStates(p1.item.hash()), [
66+
4,
67+
5
68+
]);
69+
});
70+
});

ts/client/database/connection.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export const close = async (): Promise<void> => {
1010
export const pubkeyDB = sub(db, "pubkey");
1111
export const stateDB = sub(db, "state");
1212
export const nodeDB = sub(db, "node");
13+
export const pubkey2statesDB = sub(db, "pubkey2states");

ts/client/database/pubkey2states.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import _ from "lodash";
2+
import { PubkeyLeafFactory } from "../../tree/leaves/PubkeyLeaf";
3+
import { pubkey2statesDB } from "./connection";
4+
5+
export class Pubkey2StatesDB {
6+
static async getStates(pubkeyHash: string): Promise<number[]> {
7+
return JSON.parse(await pubkey2statesDB.get(pubkeyHash));
8+
}
9+
10+
static async update(pubkeyID: number, stateID: number): Promise<void> {
11+
const pubkeyLeaf = await PubkeyLeafFactory().fromDB(pubkeyID);
12+
const pubkeyHash = pubkeyLeaf.item.hash();
13+
14+
try {
15+
const states: string = await pubkey2statesDB.get(pubkeyHash);
16+
const appended = _.union<number>(JSON.parse(states), [stateID]);
17+
await pubkey2statesDB.put(pubkeyHash, JSON.stringify(appended));
18+
} catch (error) {
19+
if (error.name === "NotFoundError") {
20+
await pubkey2statesDB.put(
21+
pubkeyHash,
22+
JSON.stringify([stateID])
23+
);
24+
} else {
25+
throw error;
26+
}
27+
}
28+
}
29+
}

ts/client/database/stateEngine.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { State } from "../../state";
22
import { StateLeafFactory } from "../../tree/leaves/StateLeaf";
33
import { StorageEngine } from "../storageEngine/interfaces";
44
import { DatabaseEngine } from "./databaseEngine";
5+
import { Pubkey2StatesDB } from "./pubkey2states";
56

67
export interface StateStorageEngine extends StorageEngine<State> {}
78

@@ -10,4 +11,16 @@ export class StateDatabaseEngine extends DatabaseEngine<State>
1011
constructor(depth: number) {
1112
super(depth, StateLeafFactory());
1213
}
14+
15+
public async updateBatch(
16+
path: number,
17+
depth: number,
18+
items: State[]
19+
): Promise<void> {
20+
for (const [i, item] of items.entries()) {
21+
const itemID = path * 2 ** depth + i;
22+
await this.update(itemID, item);
23+
await Pubkey2StatesDB.update(item.pubkeyID.toNumber(), itemID);
24+
}
25+
}
1326
}

ts/client/features/transfer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,11 @@ export class TransferOffchainTx extends TransferCompressedTx
197197
const signature = { sol: solG1, mcl: mclG1 };
198198

199199
return new this(
200-
obj.fromIndex.toNumber(),
201-
obj.toIndex.toNumber(),
200+
obj.fromIndex,
201+
obj.toIndex,
202202
obj.amount,
203203
obj.fee,
204-
obj.nonce.toNumber(),
204+
obj.nonce,
205205
signature
206206
);
207207
}

ts/client/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ async function main() {
1212
console.info("Starting Hubble node...");
1313
const config = await configFromPath(configPath);
1414

15-
const fast = fastify({ logger: true });
15+
const fast = fastify({ logger: true, maxParamLength: 512 });
1616
fast.setErrorHandler(console.error);
1717

1818
const node = await HubbleNode.init(config, fast);

ts/client/services/rpc.ts

Lines changed: 107 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { arrayify } from "@ethersproject/bytes";
22
import { FastifyInstance } from "fastify";
33
import { CoreAPI } from "../coreAPI";
4+
import { Pubkey2StatesDB } from "../database/pubkey2states";
45
import { ITransferPool, TransferOffchainTx } from "../features/transfer";
6+
import cors from "fastify-cors";
57

68
const tx = {
79
schema: {
@@ -20,30 +22,115 @@ export class RPC {
2022
fastify: FastifyInstance,
2123
transferPool?: ITransferPool
2224
) {
25+
fastify.register(cors, {
26+
origin: "*"
27+
});
2328
fastify.get<{ Params: { stateID: number } }>(
2429
"/user/state/:stateID",
25-
async function(request) {
26-
const stateID = request.params.stateID;
27-
const state = await l2Storage.state.get(stateID);
28-
return state.toJSON();
30+
async function(request, reply) {
31+
try {
32+
const { stateID } = request.params;
33+
const state = await l2Storage.state.get(stateID);
34+
return state.toJSON();
35+
} catch (error) {
36+
if (error.name === "NotFoundError") {
37+
return reply
38+
.status(404)
39+
.send({ error: "pubkey not found" });
40+
} else {
41+
console.error(error);
42+
return reply.status(500);
43+
}
44+
}
45+
}
46+
);
47+
fastify.get<{ Params: { pubkeyHash: string } }>(
48+
"/user/state/pubkey/:pubkeyHash",
49+
async function(request, reply) {
50+
try {
51+
const { pubkeyHash } = request.params;
52+
const stateIndices = await Pubkey2StatesDB.getStates(
53+
pubkeyHash
54+
);
55+
let data = stateIndices.map(async id => {
56+
let state = await l2Storage.state.get(Number(id));
57+
return {
58+
stateId: id,
59+
balance: state.balance.toString(),
60+
tokenId: state.tokenID.toString(),
61+
nonce: state.nonce.toString()
62+
};
63+
});
64+
return { states: await Promise.all(data) };
65+
} catch (error) {
66+
if (error.name === "NotFoundError") {
67+
return reply
68+
.status(404)
69+
.send({ error: "pubkey not found" });
70+
} else {
71+
console.error(error);
72+
return reply.status(500);
73+
}
74+
}
75+
}
76+
);
77+
fastify.get<{ Params: { pubkeyID: number } }>(
78+
"/user/pubkey/hash/:pubkeyID",
79+
async function(request, reply) {
80+
try {
81+
const { pubkeyID } = request.params;
82+
const pubkey = await l2Storage.pubkey.get(pubkeyID);
83+
return { hash: pubkey.hash() };
84+
} catch (error) {
85+
return reply
86+
.status(404)
87+
.send({ error: "pubkey not found" });
88+
}
89+
}
90+
);
91+
fastify.get<{ Params: { pubkeyHash: string } }>(
92+
"/user/pubkey/id/:pubkeyHash",
93+
async function(request, reply) {
94+
try {
95+
const { pubkeyHash } = request.params;
96+
const stateIndices = await Pubkey2StatesDB.getStates(
97+
pubkeyHash
98+
);
99+
let data = await l2Storage.state.get(
100+
Number(stateIndices[0])
101+
);
102+
return { id: data.pubkeyID.toNumber() };
103+
} catch (error) {
104+
if (error.name === "NotFoundError") {
105+
return reply
106+
.status(404)
107+
.send({ error: "pubkey not found" });
108+
} else {
109+
console.error(error);
110+
return reply.status(500);
111+
}
112+
}
29113
}
30114
);
31115
fastify.post<{ Body: { bytes: string } }>(
32116
"/tx",
33117
tx,
34118
async (request, reply) => {
35-
if (!transferPool) {
36-
reply.status(409).send("not a proposer");
37-
return;
38-
}
39-
40-
const bytes = arrayify(request.body.bytes);
41-
const transfer = TransferOffchainTx.deserialize(bytes);
42-
console.log(transfer.toString());
119+
try {
120+
if (!transferPool) {
121+
reply.status(409).send("not a proposer");
122+
return;
123+
}
43124

44-
await transferPool.push(transfer);
45-
await l2Storage.transactions.pending(transfer);
46-
return { txHash: transfer.hash() };
125+
const bytes = arrayify(request.body.bytes);
126+
const transfer = TransferOffchainTx.deserialize(bytes);
127+
await transferPool.push(transfer);
128+
await l2Storage.transactions.pending(transfer);
129+
return { txHash: transfer.hash() };
130+
} catch (error) {
131+
console.error(error);
132+
return reply.status(500);
133+
}
47134
}
48135
);
49136
fastify.get<{ Params: { txMsg: string } }>(
@@ -57,7 +144,11 @@ export class RPC {
57144
}
58145
// In the future, we may want to clean up
59146
// this JSON serialization to something more minimal.
60-
return JSON.stringify(txStatus);
147+
return JSON.stringify({
148+
status: txStatus.status,
149+
l1BlockIncluded: txStatus.l1BlockIncluded,
150+
l1TxnHash: txStatus.l1TxnHash
151+
});
61152
}
62153
);
63154
}

0 commit comments

Comments
 (0)