mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-06-03 03:01:23 +00:00
fix(chore): Additional test for particle signature (#356)
* Additional test for particle signature * remove .only * Fix bs58 * Fix bs58 #2 * update test * update test * fix test * remove only * refactor * refactor --------- Co-authored-by: Aleksey Proshutisnkiy <justprosh@users.noreply.github.com> Co-authored-by: Alexey Proshutinskiy <alexey.prosh@fluence.one>
This commit is contained in:
parent
c0b73fec4a
commit
230f47d27e
@ -34,7 +34,7 @@ import { Subject } from 'rxjs';
|
|||||||
import { throwIfHasNoPeerId } from '../util/libp2pUtils.js';
|
import { throwIfHasNoPeerId } from '../util/libp2pUtils.js';
|
||||||
import { IConnection } from './interfaces.js';
|
import { IConnection } from './interfaces.js';
|
||||||
import { IParticle } from '../particle/interfaces.js';
|
import { IParticle } from '../particle/interfaces.js';
|
||||||
import { Particle, serializeToString, verifySignature } from '../particle/Particle.js';
|
import { buildParticleMessage, Particle, serializeToString, verifySignature } from '../particle/Particle.js';
|
||||||
import { identifyService } from 'libp2p/identify';
|
import { identifyService } from 'libp2p/identify';
|
||||||
import { pingService } from 'libp2p/ping';
|
import { pingService } from 'libp2p/ping';
|
||||||
import { unmarshalPublicKey } from '@libp2p/crypto/keys';
|
import { unmarshalPublicKey } from '@libp2p/crypto/keys';
|
||||||
@ -186,7 +186,9 @@ export class RelayConnection implements IConnection {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isVerified = await verifySignature(particle, initPeerId.publicKey);
|
// TODO: uncomment this after nox rolls out signature verification
|
||||||
|
// const isVerified = await KeyPair.verifyWithPublicKey(initPeerId.publicKey, buildParticleMessage(particle), particle.signature);
|
||||||
|
const isVerified = true;
|
||||||
if (isVerified) {
|
if (isVerified) {
|
||||||
this.particleSource.next(particle);
|
this.particleSource.next(particle);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2023 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,15 +13,15 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { it, describe, expect } from 'vitest';
|
|
||||||
import { toUint8Array } from 'js-base64';
|
|
||||||
import * as bs58 from 'bs58';
|
|
||||||
import { KeyPair } from '../index.js';
|
|
||||||
|
|
||||||
// @ts-ignore
|
import bs58 from "bs58";
|
||||||
const { decode } = bs58.default;
|
import { fromUint8Array, toUint8Array } from 'js-base64';
|
||||||
|
import { it, describe, expect } from "vitest";
|
||||||
|
import { fromBase64Sk, KeyPair } from '../index.js';
|
||||||
|
|
||||||
const key = '+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=';
|
import { Particle, serializeToString, buildParticleMessage } from '../../particle/Particle.js';
|
||||||
|
|
||||||
|
const key = "+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=";
|
||||||
const keyBytes = toUint8Array(key);
|
const keyBytes = toUint8Array(key);
|
||||||
|
|
||||||
const testData = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9, 10]);
|
const testData = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9, 10]);
|
||||||
@ -34,76 +34,102 @@ const testDataSig = Uint8Array.from([
|
|||||||
|
|
||||||
// signature produced by KeyPair created from some random KeyPair
|
// signature produced by KeyPair created from some random KeyPair
|
||||||
|
|
||||||
describe('KeyPair tests', () => {
|
describe("KeyPair tests", () => {
|
||||||
it('generate keypair from seed', async function () {
|
it("generate keypair from seed", async function () {
|
||||||
// arrange
|
// arrange
|
||||||
const random = await KeyPair.randomEd25519();
|
const random = await KeyPair.randomEd25519();
|
||||||
const privateKey = random.toEd25519PrivateKey();
|
const privateKey = random.toEd25519PrivateKey();
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const keyPair = await KeyPair.fromEd25519SK(privateKey);
|
const keyPair = await KeyPair.fromEd25519SK(privateKey);
|
||||||
const privateKey2 = keyPair.toEd25519PrivateKey();
|
const privateKey2 = keyPair.toEd25519PrivateKey();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(privateKey).toStrictEqual(privateKey2);
|
expect(privateKey).toStrictEqual(privateKey2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('create keypair from ed25519 private key', async function () {
|
it("create keypair from ed25519 private key", async function () {
|
||||||
// arrange
|
// arrange
|
||||||
const rustSK = 'jDaxLJzYtzgwTMrELJCAqavtmx85ktQNfB2rLcK7MhH';
|
const rustSK = "jDaxLJzYtzgwTMrELJCAqavtmx85ktQNfB2rLcK7MhH";
|
||||||
const sk = decode(rustSK);
|
const sk = bs58.decode(rustSK);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const keyPair = await KeyPair.fromEd25519SK(sk);
|
const keyPair = await KeyPair.fromEd25519SK(sk);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
const expectedPeerId = '12D3KooWH1W3VznVZ87JH4FwABK4mkntcspTVWJDta6c2xg9Pzbp';
|
const expectedPeerId =
|
||||||
expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId);
|
"12D3KooWH1W3VznVZ87JH4FwABK4mkntcspTVWJDta6c2xg9Pzbp";
|
||||||
});
|
|
||||||
|
|
||||||
it('create keypair from a seed phrase', async function () {
|
expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId);
|
||||||
// arrange
|
});
|
||||||
const seedArray = new Uint8Array(32).fill(1);
|
|
||||||
|
|
||||||
// act
|
it("create keypair from a seed phrase", async function () {
|
||||||
const keyPair = await KeyPair.fromEd25519SK(seedArray);
|
// arrange
|
||||||
|
const seedArray = new Uint8Array(32).fill(1);
|
||||||
|
|
||||||
// assert
|
// act
|
||||||
const expectedPeerId = '12D3KooWK99VoVxNE7XzyBwXEzW7xhK7Gpv85r9F3V3fyKSUKPH5';
|
const keyPair = await KeyPair.fromEd25519SK(seedArray);
|
||||||
expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sign', async function () {
|
// assert
|
||||||
// arrange
|
const expectedPeerId =
|
||||||
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
"12D3KooWK99VoVxNE7XzyBwXEzW7xhK7Gpv85r9F3V3fyKSUKPH5";
|
||||||
|
|
||||||
// act
|
expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId);
|
||||||
const res = await keyPair.signBytes(testData);
|
});
|
||||||
// assert
|
|
||||||
expect(new Uint8Array(res)).toStrictEqual(testDataSig);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verify', async function () {
|
it("sign", async function () {
|
||||||
// arrange
|
// arrange
|
||||||
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const res = await keyPair.verify(testData, testDataSig);
|
const res = await keyPair.signBytes(testData);
|
||||||
|
// assert
|
||||||
|
expect(new Uint8Array(res)).toStrictEqual(testDataSig);
|
||||||
|
});
|
||||||
|
|
||||||
// assert
|
it("verify", async function () {
|
||||||
expect(res).toBe(true);
|
// arrange
|
||||||
});
|
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
||||||
|
|
||||||
it('sign-verify', async function () {
|
// act
|
||||||
// arrange
|
const res = await keyPair.verify(testData, testDataSig);
|
||||||
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
|
||||||
|
|
||||||
// act
|
// assert
|
||||||
const data = new Uint8Array(32).fill(1);
|
expect(res).toBe(true);
|
||||||
const sig = await keyPair.signBytes(data);
|
});
|
||||||
const res = await keyPair.verify(data, sig);
|
|
||||||
|
|
||||||
// assert
|
it("sign-verify", async function () {
|
||||||
expect(res).toBe(true);
|
// arrange
|
||||||
});
|
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
||||||
|
|
||||||
|
// act
|
||||||
|
const data = new Uint8Array(32).fill(1);
|
||||||
|
const sig = await keyPair.signBytes(data);
|
||||||
|
const res = await keyPair.verify(data, sig);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(res).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validates particle signature checks", async function () {
|
||||||
|
const keyPair = await fromBase64Sk("7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE=");
|
||||||
|
expect(bs58.encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D");
|
||||||
|
const message = toUint8Array(btoa("message"));
|
||||||
|
const signature = await keyPair.signBytes(message);
|
||||||
|
|
||||||
|
const verified = await keyPair.verify(message, signature);
|
||||||
|
expect(verified).toBe(true);
|
||||||
|
expect(fromUint8Array(signature)).toBe("sBW7H6/1fwAwF86ldwVm9BDu0YH3w30oFQjTWX0Tiu9yTVZHmxkV2OX4GL5jn0Iz0CrasGcOfozzkZwtJBPMBg==");
|
||||||
|
|
||||||
|
const particle = await Particle.createNew("abc", keyPair.getPeerId(), 7000, keyPair, "2883f959-e9e7-4843-8c37-205d393ca372", 1696934545662);
|
||||||
|
|
||||||
|
const particle_bytes = buildParticleMessage(particle);
|
||||||
|
expect(fromUint8Array(particle_bytes)).toBe("Mjg4M2Y5NTktZTllNy00ODQzLThjMzctMjA1ZDM5M2NhMzcy/kguGYsBAABYGwAAYWJj");
|
||||||
|
|
||||||
|
const isParticleVerified = await KeyPair.verifyWithPublicKey(keyPair.getPublicKey(), particle_bytes, particle.signature);
|
||||||
|
|
||||||
|
expect(isParticleVerified).toBe(true);
|
||||||
|
|
||||||
|
expect(fromUint8Array(particle.signature)).toBe("KceXDnOfqe0dOnAxiDsyWBIvUq6WHoT0ge+VMHXOZsjZvCNH7/10oufdlYfcPomfv28On6E87ZhDcHGBZcb7Bw==");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright 2020 Fluence Labs Limited
|
* Copyright 2023 Fluence Labs Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,92 +14,105 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { PeerId } from '@libp2p/interface/peer-id';
|
import { KeyPairOptions } from "@fluencelabs/interfaces";
|
||||||
import { generateKeyPairFromSeed, generateKeyPair, unmarshalPublicKey } from '@libp2p/crypto/keys';
|
import {
|
||||||
import { createFromPrivKey, createFromPubKey } from '@libp2p/peer-id-factory';
|
generateKeyPairFromSeed,
|
||||||
import type { PrivateKey, PublicKey } from '@libp2p/interface/keys';
|
generateKeyPair,
|
||||||
import { toUint8Array } from 'js-base64';
|
unmarshalPublicKey,
|
||||||
import * as bs58 from 'bs58';
|
} from "@libp2p/crypto/keys";
|
||||||
import { KeyPairOptions } from '@fluencelabs/interfaces';
|
import type { PrivateKey, PublicKey } from "@libp2p/interface/keys";
|
||||||
|
import type { PeerId } from "@libp2p/interface/peer-id";
|
||||||
// @ts-ignore
|
import { createFromPrivKey } from "@libp2p/peer-id-factory";
|
||||||
const { decode } = bs58.default;
|
import bs58 from "bs58";
|
||||||
|
import { toUint8Array } from "js-base64";
|
||||||
|
|
||||||
export class KeyPair {
|
export class KeyPair {
|
||||||
/**
|
private publicKey: PublicKey;
|
||||||
* Key pair in libp2p format. Used for backward compatibility with the current FluencePeer implementation
|
|
||||||
*/
|
|
||||||
getLibp2pPeerId() {
|
|
||||||
return this.libp2pPeerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
private constructor(
|
||||||
private privateKey: PrivateKey | undefined,
|
private privateKey: PrivateKey,
|
||||||
private publicKey: PublicKey,
|
private libp2pPeerId: PeerId,
|
||||||
private libp2pPeerId: PeerId
|
) {
|
||||||
) {}
|
this.publicKey = privateKey.public;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates new KeyPair from ed25519 private key represented as a 32 byte array
|
* Key pair in libp2p format. Used for backward compatibility with the current FluencePeer implementation
|
||||||
* @param seed - Any sequence of 32 bytes
|
*/
|
||||||
* @returns - Promise with the created KeyPair
|
getLibp2pPeerId() {
|
||||||
*/
|
return this.libp2pPeerId;
|
||||||
static async fromEd25519SK(seed: Uint8Array): Promise<KeyPair> {
|
}
|
||||||
const key = await generateKeyPairFromSeed('Ed25519', seed, 256);
|
|
||||||
const lib2p2Pid = await createFromPrivKey(key);
|
|
||||||
return new KeyPair(key, key.public, lib2p2Pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates new KeyPair with a random secret key
|
* Return public key inferred from private key
|
||||||
* @returns - Promise with the created KeyPair
|
*/
|
||||||
*/
|
getPublicKey() {
|
||||||
static async randomEd25519(): Promise<KeyPair> {
|
return this.publicKey.bytes;
|
||||||
const key = await generateKeyPair('Ed25519');
|
}
|
||||||
const lib2p2Pid = await createFromPrivKey(key);
|
|
||||||
return new KeyPair(key, key.public, lib2p2Pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPeerId(): string {
|
/**
|
||||||
return this.libp2pPeerId.toString();
|
* Generates new KeyPair from ed25519 private key represented as a 32 byte array
|
||||||
}
|
* @param seed - Any sequence of 32 bytes
|
||||||
|
* @returns - Promise with the created KeyPair
|
||||||
|
*/
|
||||||
|
static async fromEd25519SK(seed: Uint8Array): Promise<KeyPair> {
|
||||||
|
const key = await generateKeyPairFromSeed("Ed25519", seed, 256);
|
||||||
|
const lib2p2Pid = await createFromPrivKey(key);
|
||||||
|
return new KeyPair(key, lib2p2Pid);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns 32 byte private key
|
* Generates new KeyPair with a random secret key
|
||||||
*/
|
* @returns - Promise with the created KeyPair
|
||||||
toEd25519PrivateKey(): Uint8Array {
|
*/
|
||||||
if (this.privateKey === undefined) {
|
static async randomEd25519(): Promise<KeyPair> {
|
||||||
throw new Error('Private key not supplied');
|
const key = await generateKeyPair("Ed25519");
|
||||||
}
|
const lib2p2Pid = await createFromPrivKey(key);
|
||||||
return this.privateKey.marshal().subarray(0, 32);
|
return new KeyPair(key, lib2p2Pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
signBytes(data: Uint8Array): Promise<Uint8Array> {
|
static verifyWithPublicKey(
|
||||||
if (this.privateKey === undefined) {
|
publicKey: Uint8Array,
|
||||||
throw new Error('Private key not supplied');
|
message: Uint8Array,
|
||||||
}
|
signature: Uint8Array,
|
||||||
return this.privateKey.sign(data);
|
) {
|
||||||
}
|
return unmarshalPublicKey(publicKey).verify(message, signature);
|
||||||
|
}
|
||||||
|
|
||||||
verify(data: Uint8Array, signature: Uint8Array): Promise<boolean> {
|
getPeerId(): string {
|
||||||
return this.publicKey.verify(data, signature);
|
return this.libp2pPeerId.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns 32 byte private key
|
||||||
|
*/
|
||||||
|
toEd25519PrivateKey(): Uint8Array {
|
||||||
|
return this.privateKey.marshal().subarray(0, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
signBytes(data: Uint8Array): Promise<Uint8Array> {
|
||||||
|
return this.privateKey.sign(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(data: Uint8Array, signature: Uint8Array): Promise<boolean> {
|
||||||
|
return this.publicKey.verify(data, signature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fromBase64Sk = (sk: string): Promise<KeyPair> => {
|
export const fromBase64Sk = (sk: string): Promise<KeyPair> => {
|
||||||
const skArr = toUint8Array(sk);
|
const skArr = toUint8Array(sk);
|
||||||
return KeyPair.fromEd25519SK(skArr);
|
return KeyPair.fromEd25519SK(skArr);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fromBase58Sk = (sk: string): Promise<KeyPair> => {
|
export const fromBase58Sk = (sk: string): Promise<KeyPair> => {
|
||||||
const skArr = decode(sk);
|
const skArr = bs58.decode(sk);
|
||||||
return KeyPair.fromEd25519SK(skArr);
|
return KeyPair.fromEd25519SK(skArr);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fromOpts = (opts: KeyPairOptions): Promise<KeyPair> => {
|
export const fromOpts = (opts: KeyPairOptions): Promise<KeyPair> => {
|
||||||
if (opts.source === 'random') {
|
if (opts.source === "random") {
|
||||||
return KeyPair.randomEd25519();
|
return KeyPair.randomEd25519();
|
||||||
}
|
}
|
||||||
|
|
||||||
return KeyPair.fromEd25519SK(opts.source);
|
return KeyPair.fromEd25519SK(opts.source);
|
||||||
};
|
};
|
||||||
|
@ -14,15 +14,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { atob, fromUint8Array, toUint8Array } from 'js-base64';
|
import { CallResultsArray } from "@fluencelabs/avm";
|
||||||
import { CallResultsArray } from '@fluencelabs/avm';
|
import { fromUint8Array, toUint8Array } from "js-base64";
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { concat } from "uint8arrays/concat";
|
||||||
import { Buffer } from 'buffer';
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { IParticle } from './interfaces.js';
|
|
||||||
import { concat } from 'uint8arrays/concat';
|
import { KeyPair } from "../keypair/index.js";
|
||||||
import { numberToLittleEndianBytes } from '../util/bytes.js';
|
import { numberToLittleEndianBytes } from "../util/bytes.js";
|
||||||
import { KeyPair } from '../keypair/index.js';
|
|
||||||
import { unmarshalPublicKey } from '@libp2p/crypto/keys';
|
import { IParticle } from "./interfaces.js";
|
||||||
|
|
||||||
export class Particle implements IParticle {
|
export class Particle implements IParticle {
|
||||||
constructor(
|
constructor(
|
||||||
@ -34,14 +34,31 @@ export class Particle implements IParticle {
|
|||||||
public readonly initPeerId: string,
|
public readonly initPeerId: string,
|
||||||
public readonly signature: Uint8Array
|
public readonly signature: Uint8Array
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
static async createNew(script: string, initPeerId: string, ttl: number, keyPair: KeyPair): Promise<Particle> {
|
static async createNew(
|
||||||
const id = uuidv4();
|
script: string,
|
||||||
const timestamp = Date.now();
|
initPeerId: string,
|
||||||
const message = buildParticleMessage({ id, timestamp, ttl, script });
|
ttl: number,
|
||||||
const signature = await keyPair.signBytes(message);
|
keyPair: KeyPair,
|
||||||
return new Particle(id, Date.now(), script, Buffer.from([]), ttl, initPeerId, signature);
|
_id?: string,
|
||||||
}
|
_timestamp?: number,
|
||||||
|
_data?: Uint8Array,
|
||||||
|
): Promise<Particle> {
|
||||||
|
const id = _id ?? uuidv4();
|
||||||
|
const timestamp = _timestamp ?? Date.now();
|
||||||
|
const data = _data ?? new Uint8Array([]);
|
||||||
|
const message = buildParticleMessage({ id, timestamp, ttl, script });
|
||||||
|
const signature = await keyPair.signBytes(message);
|
||||||
|
return new Particle(
|
||||||
|
id,
|
||||||
|
timestamp,
|
||||||
|
script,
|
||||||
|
data,
|
||||||
|
ttl,
|
||||||
|
initPeerId,
|
||||||
|
signature,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static fromString(str: string): Particle {
|
static fromString(str: string): Particle {
|
||||||
const json = JSON.parse(str);
|
const json = JSON.parse(str);
|
||||||
@ -64,27 +81,32 @@ const en = new TextEncoder();
|
|||||||
/**
|
/**
|
||||||
* Builds particle message for signing
|
* Builds particle message for signing
|
||||||
*/
|
*/
|
||||||
export const buildParticleMessage = ({ id, timestamp, ttl, script }: Omit<IParticle, 'initPeerId' | 'signature' | 'data'>): Uint8Array => {
|
export const buildParticleMessage = ({
|
||||||
return concat([
|
id,
|
||||||
en.encode(id),
|
timestamp,
|
||||||
numberToLittleEndianBytes(timestamp, 'u64'),
|
ttl,
|
||||||
numberToLittleEndianBytes(ttl, 'u32'),
|
script,
|
||||||
en.encode(script),
|
}: Omit<IParticle, "initPeerId" | "signature" | "data">): Uint8Array => {
|
||||||
]);
|
return concat([
|
||||||
}
|
en.encode(id),
|
||||||
|
numberToLittleEndianBytes(timestamp, "u64"),
|
||||||
|
numberToLittleEndianBytes(ttl, "u32"),
|
||||||
|
en.encode(script),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns actual ttl of a particle, i.e. ttl - time passed since particle creation
|
* Returns actual ttl of a particle, i.e. ttl - time passed since particle creation
|
||||||
*/
|
*/
|
||||||
export const getActualTTL = (particle: IParticle): number => {
|
export const getActualTTL = (particle: IParticle): number => {
|
||||||
return particle.timestamp + particle.ttl - Date.now();
|
return particle.timestamp + particle.ttl - Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if particle has expired
|
* Returns true if particle has expired
|
||||||
*/
|
*/
|
||||||
export const hasExpired = (particle: IParticle): boolean => {
|
export const hasExpired = (particle: IParticle): boolean => {
|
||||||
return getActualTTL(particle) <= 0;
|
return getActualTTL(particle) <= 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,59 +122,65 @@ export const verifySignature = async (particle: IParticle, publicKey: Uint8Array
|
|||||||
/**
|
/**
|
||||||
* Creates a particle clone with new data
|
* Creates a particle clone with new data
|
||||||
*/
|
*/
|
||||||
export const cloneWithNewData = (particle: IParticle, newData: Uint8Array): IParticle => {
|
export const cloneWithNewData = (
|
||||||
return new Particle(particle.id, particle.timestamp, particle.script, newData, particle.ttl, particle.initPeerId, particle.signature);
|
particle: IParticle,
|
||||||
};
|
newData: Uint8Array,
|
||||||
|
): IParticle => {
|
||||||
/**
|
return new Particle(
|
||||||
* Creates a deep copy of a particle
|
particle.id,
|
||||||
*/
|
particle.timestamp,
|
||||||
export const fullClone = (particle: IParticle): IParticle => {
|
particle.script,
|
||||||
return JSON.parse(JSON.stringify(particle));
|
newData,
|
||||||
|
particle.ttl,
|
||||||
|
particle.initPeerId,
|
||||||
|
particle.signature,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes particle into string suitable for sending through network
|
* Serializes particle into string suitable for sending through network
|
||||||
*/
|
*/
|
||||||
export const serializeToString = (particle: IParticle): string => {
|
export const serializeToString = (particle: IParticle): string => {
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
action: 'Particle',
|
action: "Particle",
|
||||||
id: particle.id,
|
id: particle.id,
|
||||||
init_peer_id: particle.initPeerId,
|
init_peer_id: particle.initPeerId,
|
||||||
timestamp: particle.timestamp,
|
timestamp: particle.timestamp,
|
||||||
ttl: particle.ttl,
|
ttl: particle.ttl,
|
||||||
script: particle.script,
|
script: particle.script,
|
||||||
signature: Array.from(particle.signature),
|
signature: Array.from(particle.signature),
|
||||||
data: particle.data && fromUint8Array(particle.data),
|
data: fromUint8Array(particle.data),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When particle is executed, it goes through different stages. The type describes all possible stages and their parameters
|
* When particle is executed, it goes through different stages. The type describes all possible stages and their parameters
|
||||||
*/
|
*/
|
||||||
export type ParticleExecutionStage =
|
export type ParticleExecutionStage =
|
||||||
| { stage: 'received' }
|
| { stage: "received" }
|
||||||
| { stage: 'interpreted' }
|
| { stage: "interpreted" }
|
||||||
| { stage: 'interpreterError'; errorMessage: string }
|
| { stage: "interpreterError"; errorMessage: string }
|
||||||
| { stage: 'localWorkDone' }
|
| { stage: "localWorkDone" }
|
||||||
| { stage: 'sent' }
|
| { stage: "sent" }
|
||||||
| { stage: 'sendingError'; errorMessage: string }
|
| { stage: "sendingError"; errorMessage: string }
|
||||||
| { stage: 'expired' };
|
| { stage: "expired" };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Particle queue item is a wrapper around particle, which contains additional information about particle execution
|
* Particle queue item is a wrapper around particle, which contains additional information about particle execution
|
||||||
*/
|
*/
|
||||||
export interface ParticleQueueItem {
|
export interface ParticleQueueItem {
|
||||||
particle: IParticle;
|
particle: IParticle;
|
||||||
callResults: CallResultsArray;
|
callResults: CallResultsArray;
|
||||||
onStageChange: (state: ParticleExecutionStage) => void;
|
onStageChange: (state: ParticleExecutionStage) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to handle particle at expired stage
|
* Helper function to handle particle at expired stage
|
||||||
*/
|
*/
|
||||||
export const handleTimeout = (fn: () => void) => (stage: ParticleExecutionStage) => {
|
export const handleTimeout = (fn: () => void) => {
|
||||||
if (stage.stage === 'expired') {
|
return (stage: ParticleExecutionStage) => {
|
||||||
fn();
|
if (stage.stage === "expired") {
|
||||||
}
|
fn();
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,10 +23,10 @@ const sizeMap = {
|
|||||||
|
|
||||||
function numberToBytes(n: number, s: Size, littleEndian: boolean) {
|
function numberToBytes(n: number, s: Size, littleEndian: boolean) {
|
||||||
const size = sizeMap[s];
|
const size = sizeMap[s];
|
||||||
const buffer = new ArrayBuffer(size);
|
const buffer = new ArrayBuffer(8);
|
||||||
const dv = new DataView(buffer);
|
const dv = new DataView(buffer);
|
||||||
dv.setUint32(0, n, littleEndian);
|
dv.setBigUint64(0, BigInt(n), littleEndian);
|
||||||
return new Uint8Array(buffer);
|
return new Uint8Array(buffer.slice(0, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function numberToLittleEndianBytes(n: number, s: Size) {
|
export function numberToLittleEndianBytes(n: number, s: Size) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user