Skip to content
Merged
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
1 change: 1 addition & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/functions/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules/
*.local
coverage/
lib/
allowed_keys.txt
2 changes: 1 addition & 1 deletion frontend/functions/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if (fs.existsSync(keysFilePath)) {
.map(key => key.trim())
.filter(key => key.length > 0);

const validatorFilePath = path.resolve(__dirname, 'functions/submitterValidator.ts');
const validatorFilePath = path.resolve(__dirname, 'lib/submitterValidator.js');
let validatorFileContent = fs.readFileSync(validatorFilePath, 'utf-8');

const keysSetString = keys.map(key => `'${key}'`).join(',\n ');
Expand Down
8 changes: 4 additions & 4 deletions frontend/functions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions frontend/functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"logs": "firebase functions:log",
"test": "jest",
"test:watch": "jest --watch",
"build": "node build.js && tsc -p tsconfig.json",
"build": "tsc -p tsconfig.json && node build.js",
"build:watch": "tsc --watch"
},
"engines": {
Expand All @@ -21,7 +21,7 @@
"blake2": "^5.0.0",
"bs58check": "^3.0.1",
"firebase-admin": "^12.1.0",
"firebase-functions": "^6.2.0",
"firebase-functions": "^6.3.2",
"mina-signer": "^3.0.7"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "frontend",
"version": "1.0.130",
"version": "1.0.180",
"scripts": {
"install:deps": "npm install",
"start": "npm install && ng serve --configuration local --open",
Expand Down Expand Up @@ -91,4 +91,4 @@
"webpack": "^5.88.2",
"webpack-bundle-analyzer": "^4.9.0"
}
}
}
22 changes: 16 additions & 6 deletions frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ import { ReactiveFormsModule } from '@angular/forms';
import { WebNodeLandingPageComponent } from '@app/layout/web-node-landing-page/web-node-landing-page.component';
import * as Sentry from '@sentry/angular';
import { Router } from '@angular/router';
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { getApp, initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { getAnalytics, provideAnalytics, ScreenTrackingService } from '@angular/fire/analytics';
import { getPerformance, providePerformance } from '@angular/fire/performance';
import { BlockProductionPillComponent } from '@app/layout/block-production-pill/block-production-pill.component';
import { MenuTabsComponent } from '@app/layout/menu-tabs/menu-tabs.component';
import { getFirestore, provideFirestore } from '@angular/fire/firestore';
import { LeaderboardModule } from '@leaderboard/leaderboard.module';
import { UptimePillComponent } from '@app/layout/uptime-pill/uptime-pill.component';
import { provideAppCheck } from '@angular/fire/app-check';
import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';
import { SETTINGS } from '@angular/fire/compat/firestore';

registerLocaleData(localeFr, 'fr');
registerLocaleData(localeEn, 'en');
Expand Down Expand Up @@ -129,15 +132,20 @@ export class AppGlobalErrorhandler implements ErrorHandler {
}

const firebaseProviders = [
{
provide: SETTINGS,
useValue: { experimentalForceLongPolling: true },
},
provideFirebaseApp(() => initializeApp(CONFIG.globalConfig.firebase)),
provideClientHydration(),
provideHttpClient(withFetch()),
provideFirebaseApp(() => initializeApp(CONFIG.globalConfig.firebase)),
provideAnalytics(() => getAnalytics()),
ScreenTrackingService,
// provideAppCheck(() => {
// // TODO get a reCAPTCHA Enterprise here https://console.cloud.google.com/security/recaptcha?project=_
// const provider = new ReCaptchaEnterpriseProvider(/* reCAPTCHA Enterprise site key */);
// return initializeAppCheck(undefined, { provider, isTokenAutoRefreshEnabled: true });
// const app = getApp();
// const provider = new ReCaptchaV3Provider('6LfAB-QqAAAAAEu9BO6upFj6Sewd08lf0UtFC16c');
// return initializeAppCheck(app, { provider, isTokenAutoRefreshEnabled: true });
// }),
providePerformance(() => getPerformance()),
provideFirestore(() => getFirestore()),
Expand Down Expand Up @@ -192,7 +200,8 @@ const firebaseProviders = [
{ provide: Sentry.TraceService, deps: [Router] },
{
provide: APP_INITIALIZER,
useFactory: () => () => {},
useFactory: () => () => {
},
deps: [Sentry.TraceService],
multi: true,
},
Expand All @@ -203,4 +212,5 @@ const firebaseProviders = [
MenuComponent,
],
})
export class AppModule {}
export class AppModule {
}
38 changes: 35 additions & 3 deletions frontend/src/app/app.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import { Injectable } from '@angular/core';
import { map, Observable, of } from 'rxjs';
import { map, Observable, of, tap } from 'rxjs';
import { MinaNode } from '@shared/types/core/environment/mina-env.type';
import { CONFIG } from '@shared/constants/config';
import { RustService } from '@core/services/rust.service';
import { AppNodeDetails, AppNodeStatus } from '@shared/types/app/app-node-details.type';
import { getNetwork } from '@shared/helpers/mina.helper';
import { getLocalStorage, nanOrElse, ONE_MILLION } from '@openmina/shared';
import { BlockProductionWonSlotsStatus } from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type';
import {
BlockProductionWonSlotsStatus
} from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type';
import { AppEnvBuild } from '@shared/types/app/app-env-build.type';
import { SentryService } from '@core/services/sentry.service';
import { WebNodeService } from '@core/services/web-node.service';

@Injectable({
providedIn: 'root',
})
export class AppService {

constructor(private rust: RustService) { }
private previousProducedBlock: BlockProductionAttempt;

constructor(private rust: RustService,
private sentryService: SentryService,
private webnodeService: WebNodeService) {
}

getActiveNode(nodes: MinaNode[]): Observable<MinaNode> {
const nodeName = new URL(location.href).searchParams.get('node');
Expand All @@ -37,6 +46,7 @@ export class AppService {
getActiveNodeDetails(): Observable<AppNodeDetails> {
return this.rust.get<NodeDetailsResponse>('/status')
.pipe(
tap((data: NodeDetailsResponse) => this.notifyPrevBlockChanged(data)),
map((data: NodeDetailsResponse): AppNodeDetails => ({
status: this.getStatus(data),
blockHeight: data.transition_frontier.best_tip?.height,
Expand All @@ -55,6 +65,27 @@ export class AppService {
);
}

private notifyPrevBlockChanged(data: NodeDetailsResponse): void {
if (!this.rust.activeNodeIsWebNode) {
return;
}

const isInProduction = (status: BlockProductionWonSlotsStatus) =>
![
BlockProductionWonSlotsStatus.Discarded,
BlockProductionWonSlotsStatus.Orphaned,
BlockProductionWonSlotsStatus.Canonical,
].includes(status)

if (
this.previousProducedBlock && data.previous_block_production_attempt
&& isInProduction(this.previousProducedBlock.status) !== isInProduction(data.previous_block_production_attempt.status)
) {
this.sentryService.updateProducedBlock(data.previous_block_production_attempt, this.webnodeService.publicKey);
}
this.previousProducedBlock = data.previous_block_production_attempt;
}

private getStatus(data: NodeDetailsResponse): AppNodeStatus {
switch (data.transition_frontier.sync.phase) {
case 'Bootstrap':
Expand All @@ -76,6 +107,7 @@ export interface NodeDetailsResponse {
snark_pool: SnarkPool;
chain_id: string | undefined;
current_block_production_attempt: BlockProductionAttempt;
previous_block_production_attempt: BlockProductionAttempt;
}

export interface BlockProductionAttempt {
Expand Down
41 changes: 15 additions & 26 deletions frontend/src/app/core/services/firestore.service.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
import { Injectable, Optional } from '@angular/core';
import { collection, CollectionReference, deleteDoc, doc, DocumentData, Firestore, updateDoc } from '@angular/fire/firestore';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError, Observable, of } from 'rxjs';
import { catchError, Observable, of, tap } from 'rxjs';
import { SentryService } from '@core/services/sentry.service';

@Injectable({
providedIn: 'root',
})
export class FirestoreService {
private heartbeatCollection: CollectionReference<DocumentData>;
private cloudFunctionUrl = 'https://us-central1-webnode-gtm-test.cloudfunctions.net/handleValidationAndStore';

constructor(@Optional() private firestore: Firestore,
private http: HttpClient) {
if (this.firestore) {
this.heartbeatCollection = collection(this.firestore, 'heartbeat');
}
}
constructor(private sentryService: SentryService,
private http: HttpClient) { }

addHeartbeat(data: any): Observable<any> {
console.log('Posting to cloud function:', data);
return this.http.post(this.cloudFunctionUrl, { data }).pipe(
catchError(error => {
console.error('Error while posting to cloud function:', error);
return of(null);
}),
);
}

updateHeartbeat(id: string, data: any): Promise<void> {
const docRef = doc(this.heartbeatCollection, id);
return updateDoc(docRef, data);
}

deleteHeartbeat(id: string): Promise<void> {
const docRef = doc(this.heartbeatCollection, id);
return deleteDoc(docRef);
return this.http.post(this.cloudFunctionUrl, { data })
.pipe(
// tap(() => {
// this.sentryService.updateHeartbeat(data, data.submitter);
// }),
catchError(error => {
console.error('Error while posting heartbeat', error);
return of(null);
}),
);
}
}
Loading
Loading