Skip to content

Commit

Permalink
Merge pull request #36 from peaqnetwork/feature-ptp-integration-function
Browse files Browse the repository at this point in the history
(feat): ADD function for subscription to ptp on peaq network
  • Loading branch information
lavish0000 authored Jan 19, 2025
2 parents ecceb8a + 162bed0 commit 6c8344a
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 5 deletions.
28 changes: 23 additions & 5 deletions packages/sdk/src/modules/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import type { Options, SDKMetadata } from '../../types';

import { Base } from '../base';
import { GenerateDidOptions, GenerateDidResult, Did } from '../did';
import { RBAC } from "../rbac";
import { Storage } from "../storage";

import { RBAC } from '../rbac';
import { Storage } from '../storage';
import { Ptp, PtpOptions, type SyncResult } from '../ptp';

/**
* Main class for interacting with the SDK.
Expand All @@ -18,10 +18,11 @@ export class Main extends Base {
private readonly _options: Options;
protected override _api: ApiPromise;
private _metadata: SDKMetadata;

public did: Did;
public rbac: RBAC;
public storage: Storage;
private ptp: Ptp;

constructor(options: Options) {
super();
Expand All @@ -32,6 +33,7 @@ export class Main extends Base {
this.did = new Did(this._api, this._metadata);
this.rbac = new RBAC(this._api, this._metadata);
this.storage = new Storage(this._api, this._metadata);
this.ptp = new Ptp();
}

/**
Expand All @@ -53,7 +55,9 @@ export class Main extends Base {
* @param GenerateDidOptions - The options for generating a DID.
* @returns The hash value of the generated DID document
*/
public static async generateDidDocument(options: GenerateDidOptions): Promise<GenerateDidResult> {
public static async generateDidDocument(
options: GenerateDidOptions
): Promise<GenerateDidResult> {
const did = new Did();
return did.generate(options);
}
Expand Down Expand Up @@ -120,4 +124,18 @@ export class Main extends Base {
...defaultOptions,
});
}

/**
* Subscribes to PTP time synchronization updates
* @param options - PTP configuration options
* @param callback - Function to handle synchronization updates
* @returns Unsubscribe function
*/
public static subscribeToPtp(
options: PtpOptions,
callback: (result: SyncResult) => void
): () => void {
const ptp = new Ptp();
return ptp.subscribe(options, callback);
}
}
103 changes: 103 additions & 0 deletions packages/sdk/src/modules/ptp/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Base } from '../base';
import axios from 'axios';

export interface SyncResult {
offset: bigint;
synchronizedTime: bigint;
}

export interface PtpOptions {
masterUrl: string;
interval?: number; // in milliseconds, defaults to 1000
}

export class Ptp extends Base {
private intervalId?: NodeJS.Timer;

constructor() {
super();
}

/**
* Gets high-resolution timestamp in nanoseconds for both browser and Node.js
* @returns BigInt timestamp in nanoseconds
*/
private getNanoTimestamp(): bigint {
if (typeof window !== 'undefined' && window.performance) {
// Browser environment
return BigInt(Date.now()) * BigInt(1_000_000);
} else if (typeof process !== 'undefined' && process.hrtime) {
// Node.js environment
return process.hrtime.bigint();
} else {
// Fallback (less precise)
return BigInt(Date.now()) * BigInt(1_000_000);
}
}

/**
* Subscribes to time synchronization updates with a master clock
* @param options - Configuration options for PTP synchronization
* @param callback - Function to receive synchronization updates
* @returns Function to unsubscribe from updates
*/
public subscribe(
options: PtpOptions,
callback: (result: SyncResult) => void
): () => void {
const { masterUrl, interval = 1000 } = options;

// Initial sync
this.synchronizeClock(masterUrl).then((result) => {
if (result) {
callback(result);
}
});

// Periodic sync
this.intervalId = setInterval(async () => {
const result = await this.synchronizeClock(masterUrl);
if (result) {
callback(result);
}
}, interval);

// Return unsubscribe function
return () => {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = undefined;
}
};
}

/**
* Performs a single clock synchronization with the master
* @param masterUrl - URL of the master clock server
* @returns Synchronization result containing offset and synchronized time
*/
private async synchronizeClock(
masterUrl: string
): Promise<SyncResult | undefined> {
try {
// Step 1: Sync Message
const { data: syncData } = await axios.get(`${masterUrl}/sync`);
const T1 = BigInt(syncData.T1);
const T1_prime = this.getNanoTimestamp();

// Step 2: Delay Request
const T2 = this.getNanoTimestamp();
const { data: delayData } = await axios.post(`${masterUrl}/delay_req`);
const T2_prime = BigInt(delayData.T2_prime);

// Step 3: Calculate offset
const offset = (T1_prime - T1 - (T2 - T2_prime)) / BigInt(2);
const synchronizedTime = T1_prime + offset;

return { offset, synchronizedTime };
} catch (error) {
console.error('Error during synchronization:', error);
return undefined;
}
}
}

0 comments on commit 6c8344a

Please sign in to comment.