Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic block trigger #73

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@
"@typescript-eslint/no-unnecessary-condition": [
"off"
],
"nodejs/declare": "off"
"nodejs/declare": "off",
"unicorn/prefer-event-target": "off"
},

"overrides": [
Expand Down
22 changes: 10 additions & 12 deletions packages/api/src/graphql/modules/BlockStorageResolver.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
/* eslint-disable new-cap */
import { inject, injectable } from "tsyringe";
import {
Arg,
Field,
ObjectType,
Query,
Resolver,
} from "type-graphql";
import { Arg, Field, ObjectType, Query, Resolver } from "type-graphql";
import { IsBoolean } from "class-validator";
import {
BlockStorage,
Expand Down Expand Up @@ -53,10 +47,15 @@ export class ComputedBlockTransactionModel {

@ObjectType()
export class ComputedBlockModel {
public static fromServiceLayerModel({ txs, proof }: ComputedBlock) {
public static fromServiceLayerModel({
txs,
proof,
}: ComputedBlock): ComputedBlockModel {
return new ComputedBlockModel(
txs.map((tx) => ComputedBlockTransactionModel.fromServiceLayerModel(tx)),
JSON.stringify(proof.toJSON())
proof.proof === "mock-proof"
? "mock-proof"
: JSON.stringify(proof.toJSON())
);
}

Expand All @@ -74,7 +73,6 @@ export class ComputedBlockModel {

@graphqlModule()
export class BlockStorageResolver extends GraphqlModule<object> {
// TODO seperate these two block interfaces
public constructor(
@inject("BlockStorage")
private readonly blockStorage: BlockStorage & HistoricalBlockStorage
Expand All @@ -83,12 +81,12 @@ export class BlockStorageResolver extends GraphqlModule<object> {
}

@Query(() => ComputedBlockModel, { nullable: true })
public async block(
public async settlements(
@Arg("height", () => Number, { nullable: true })
height: number | undefined
) {
const blockHeight =
height ?? (await this.blockStorage.getCurrentBlockHeight());
height ?? (await this.blockStorage.getCurrentBlockHeight()) - 1;

const block = await this.blockStorage.getBlockAt(blockHeight);

Expand Down
10 changes: 8 additions & 2 deletions packages/api/src/graphql/modules/NodeStatusResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export class NodeStatusObject {
return new NodeStatusObject(
status.uptime,
status.uptimeHumanReadable,
status.height
status.height,
status.settlements
);
}

Expand All @@ -20,17 +21,22 @@ export class NodeStatusObject {
@Field()
public height: number;

@Field()
public settlements: number;

@Field()
public uptimeHumanReadable: string;

public constructor(
uptime: number,
uptimeHumanReadable: string,
height: number
height: number,
settlements: number
) {
this.uptime = uptime;
this.uptimeHumanReadable = uptimeHumanReadable;
this.height = height;
this.settlements = settlements;
}
}

Expand Down
65 changes: 65 additions & 0 deletions packages/api/src/graphql/modules/UnprovenBlockResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { inject } from "tsyringe";
import {
HistoricalUnprovenBlockStorage,
UnprovenBlock,
UnprovenBlockStorage,
} from "@proto-kit/sequencer";
import { Arg, Field, ObjectType, Query } from "type-graphql";

import { GraphqlModule, graphqlModule } from "../GraphqlModule";

import { ComputedBlockTransactionModel } from "./BlockStorageResolver";

@ObjectType()
export class UnprovenBlockModel {
public static fromServiceLayerModel(unprovenBlock: UnprovenBlock) {
return new UnprovenBlockModel(
Number(unprovenBlock.networkState.block.height.toBigInt()),
unprovenBlock.transactions.map((tx) =>
ComputedBlockTransactionModel.fromServiceLayerModel({
tx: tx.tx,
status: tx.status.toBoolean(),
statusMessage: tx.statusMessage,
})
)
);
}

@Field()
height: number;

@Field(() => [ComputedBlockTransactionModel])
txs: ComputedBlockTransactionModel[];

private constructor(height: number, txs: ComputedBlockTransactionModel[]) {
this.height = height;
this.txs = txs;
}
}

@graphqlModule()
export class UnprovenBlockResolver extends GraphqlModule<object> {
public constructor(
@inject("UnprovenBlockStorage")
private readonly blockStorage: HistoricalUnprovenBlockStorage &
UnprovenBlockStorage
) {
super();
}

@Query(() => UnprovenBlockModel, { nullable: true })
public async block(
@Arg("height", () => Number, { nullable: true })
height: number | undefined
) {
const blockHeight =
height ?? (await this.blockStorage.getCurrentBlockHeight()) - 1;

const block = await this.blockStorage.getBlockAt(blockHeight);

if (block !== undefined) {
return UnprovenBlockModel.fromServiceLayerModel(block);
}
return undefined;
}
}
12 changes: 8 additions & 4 deletions packages/api/src/graphql/services/NodeStatusService.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
import { inject, injectable } from "tsyringe";
import { BlockStorage } from "@proto-kit/sequencer";
import { BlockStorage, UnprovenBlockStorage } from "@proto-kit/sequencer";
import humanizeDuration from "humanize-duration";

export interface NodeStatus {
uptime: number;
uptimeHumanReadable: string;
height: number;
settlements: number;
}

@injectable()
export class NodeStatusService {
private readonly startupTime = Date.now();

public constructor(
@inject("UnprovenBlockStorage")
private readonly unprovenBlockStorage: UnprovenBlockStorage,
@inject("BlockStorage") private readonly blockStorage: BlockStorage
) {
}
) {}

public async getNodeStatus(): Promise<NodeStatus> {
const uptime = Date.now() - this.startupTime;
const uptimeHumanReadable = humanizeDuration(uptime);
const height = await this.blockStorage.getCurrentBlockHeight();
const height = await this.unprovenBlockStorage.getCurrentBlockHeight();
const settlements = await this.blockStorage.getCurrentBlockHeight();

return {
uptime,
uptimeHumanReadable,
height,
settlements,
};
}
}
1 change: 1 addition & 0 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./graphql/modules/QueryGraphqlModule";
export * from "./graphql/modules/MempoolResolver";
export * from "./graphql/modules/BlockStorageResolver";
export * from "./graphql/modules/UnprovenBlockResolver";
export * from "./graphql/GraphqlModule";
export * from "./graphql/GraphqlServer";
export * from "./graphql/GraphqlSequencerModule";
Expand Down
68 changes: 68 additions & 0 deletions packages/common/src/events/EventEmitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { EventsRecord } from "./EventEmittingComponent";

type ListenersHolder<Events extends EventsRecord> = {
[key in keyof Events]?: {
id: number;
listener: (...args: Events[key]) => void;
}[];
};

export class EventEmitter<Events extends EventsRecord> {
private readonly listeners: ListenersHolder<Events> = {};

private counter = 0;

// Fields used for offSelf()
private currentListenerId: number | undefined = undefined;

private currentListenerEventName: keyof Events | undefined = undefined;

public emit(event: keyof Events, ...parameters: Events[typeof event]) {
const listeners = this.listeners[event];
if (listeners !== undefined) {
this.currentListenerEventName = event;

listeners.forEach((listener) => {
this.currentListenerId = listener.id;

listener.listener(...parameters);

this.currentListenerId = undefined;
});
this.currentListenerEventName = undefined;
}
}

public on<Key extends keyof Events>(
event: Key,
listener: (...args: Events[Key]) => void
): number {
// eslint-disable-next-line no-multi-assign
const id = (this.counter += 1);
(this.listeners[event] ??= []).push({
id,
listener,
});
return id;
}

// eslint-disable-next-line no-warning-comments
// TODO Improve to be thread-safe
public offSelf() {
if (
this.currentListenerEventName !== undefined &&
this.currentListenerId !== undefined
) {
this.off(this.currentListenerEventName, this.currentListenerId);
}
}

public off<Key extends keyof Events>(event: Key, id: number) {
const listeners = this.listeners[event];
if (listeners !== undefined) {
this.listeners[event] = listeners.filter(
(listener) => listener.id !== id
);
}
}
}
11 changes: 11 additions & 0 deletions packages/common/src/events/EventEmittingComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { EventEmitter } from "./EventEmitter";

export type EventSignature = unknown[];

export interface EventsRecord {
[key: string]: EventSignature;
}

export interface EventEmittingComponent<Events extends EventsRecord> {
events: EventEmitter<Events>;
}
3 changes: 3 additions & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ export * from "./zkProgrammable/provableMethod";
export * from "./utils";
export * from "./dependencyFactory/DependencyFactory";
export * from "./log";
export * from "./quickmaths";
export * from "./events/EventEmittingComponent";
export * from "./events/EventEmitter";
24 changes: 24 additions & 0 deletions packages/common/src/quickmaths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Computes the greatest common divisor of a and b
* @param a
* @param b
*/
export function gcd(a: number, b: number): number {
a = Math.abs(a);
b = Math.abs(b);
if (b > a) {
a = b;
b = a;
}
while (a > 0 && b > 0) {
if (b === 0) {
return a;
}
a %= b;
if (a === 0) {
return b;
}
b %= a;
}
return 1;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MerkleTreeStore } from "./MerkleTreeStore";

export class InMemoryMerkleTreeStorage implements MerkleTreeStore {
protected readonly nodes: {
protected nodes: {
[key: number]: {
[key: string]: bigint;
};
Expand Down
10 changes: 0 additions & 10 deletions packages/protocol/src/utils/merkletree/MerkleTreeStore.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
export interface AsyncMerkleTreeStore {
openTransaction: () => void;

commit: () => void;

setNodeAsync: (key: bigint, level: number, value: bigint) => Promise<void>;

getNodeAsync: (key: bigint, level: number) => Promise<bigint | undefined>;
}

export interface MerkleTreeStore {
setNode: (key: bigint, level: number, value: bigint) => void;

Expand Down
Loading
Loading