Skip to content

Commit

Permalink
feat(commons): env utils + timed cache
Browse files Browse the repository at this point in the history
feat(specifications): remove register signature
feat(specifications): move api to get batches
  • Loading branch information
blakebyrnes committed Aug 26, 2022
1 parent b8c2419 commit 8583846
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 19 deletions.
18 changes: 18 additions & 0 deletions commons/lib/TimedCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default class TimedCache<T> {
public set value(value: T) {
this.#value = value;
if (value !== null) {
this.#expireTime = Date.now() + this.cacheSeconds * 1e3;
}
}

public get value(): T {
if (this.#expireTime && this.#expireTime < Date.now()) this.#value = null;
return this.#value;
}

#value: T;
#expireTime: number;

constructor(readonly cacheSeconds: number) {}
}
3 changes: 2 additions & 1 deletion commons/lib/TypeSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default class TypeSerializer {
object: T,
options?: { ignoreProperties?: (keyof T)[]; sortKeys?: boolean },
): unknown {
if (!object) return object;
if (object === undefined || object === null) return object;

const replaced = this.replacer(null, object);
if (replaced !== object || (typeof replaced === 'object' && '__type' in replaced)) {
Expand All @@ -84,6 +84,7 @@ export default class TypeSerializer {
}

const keys = Object.keys(object);

if (options?.sortKeys) keys.sort();
const response: any = {};
for (const key of keys) {
Expand Down
72 changes: 72 additions & 0 deletions commons/lib/envUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as Fs from 'fs';
import * as Path from 'path';

/**
* Will load env files with this precedence (.env.defaults, .env.<NODE_ENV>, .env)
*/
export function loadEnv(baseDir: string): void {
const envName = process.env.NODE_ENV?.toLowerCase() ?? 'development';
const env: Record<string, string> = {};
for (const envFile of ['.env.defaults', `.env.${envName}`, '.env']) {
const path = Path.join(baseDir, envFile);
if (!Fs.existsSync(path)) continue;
applyEnvironmentVariables(path, env);
}
// don't overwrite already set variables
for (const [key, value] of Object.entries(env)) {
if (!process.env[key]) process.env[key] = value;
}
}

// NOTE: imported from dotenv
export function applyEnvironmentVariables(path: string, env: Record<string, string>): void {
let lines = Fs.readFileSync(path, 'utf8');

// Convert line breaks to same format
lines = lines.replace(/\r\n?/gm, '\n');

const LineRegex =
/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;

const StripSurroundingQuotesRegex = /^(['"`])([\s\S]*)\1$/gm;

let match = LineRegex.exec(lines);
while (match) {
// eslint-disable-next-line prefer-const
let [, key, value] = match;

value = (value ?? '').trim();

// Check if double quoted before stripping
const isQuoted = StripSurroundingQuotesRegex.test(value);

value = value.replace(StripSurroundingQuotesRegex, '$2');

if (isQuoted) {
// Expand newlines if double quoted
value = value.replace(/\\n/g, '\n').replace(/\\r/g, '\r');
}

if (value === '') {
delete env[key];
} else {
env[key] = value;
}
match = LineRegex.exec(lines);
}
}

export function parseEnvList(envvar: string): string[] {
if (!envvar) return [];
return envvar.split(',').map(x => x.trim());
}

export function parseEnvInt(envvar: string): number | null {
if (!envvar) return null;
return parseInt(envvar, 10);
}

export function parseEnvBigint(envvar: string): bigint | null {
if (!envvar) return null;
return BigInt(envvar);
}
4 changes: 3 additions & 1 deletion commons/lib/hashUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createHash } from 'crypto';
import TypeSerializer from './TypeSerializer';

export const hashMessagePrefix = '\x18Ulixee Signed Message:\n';

export function sha3(data: Buffer | string): Buffer {
return createHash('sha3-256').update(data).digest();
}
Expand All @@ -22,7 +24,7 @@ export function hashObject<T>(
// sort keys for consistent hash
const json = sortedJsonStringify(obj, options?.ignoreProperties);

let buffer = Buffer.from(json);
let buffer = Buffer.from(`${hashMessagePrefix}${json.length}${json}`);
if (options?.prefix) buffer = Buffer.concat([options.prefix, buffer]);

return createHash('sha3-256').update(buffer).digest();
Expand Down
2 changes: 1 addition & 1 deletion crypto/lib/Address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export default class Address {
);
if (address.bech32 !== bech32)
throw new Error(
`Failed to load Address Address. Different key calculated. (calculated: ${address.bech32}, stored: ${bech32})`,
`Failed to load Address. Different key calculated. (calculated: ${address.bech32}, stored: ${bech32})`,
);
return address;
}
Expand Down
7 changes: 5 additions & 2 deletions crypto/lib/Identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ export default class Identity {

// CLASS METHODS ////////////////////////

public static loadFromFile(filepath: string, options?: { keyPassphrase?: string }): Identity {
public static loadFromFile(
filepath: string,
options?: { relativeToPath?: string; keyPassphrase?: string },
): Identity {
if (!path.isAbsolute(filepath)) {
filepath = path.join(process.cwd(), filepath);
filepath = path.join(options?.relativeToPath ?? process.cwd(), filepath);
}
const data = readFileSync(filepath, 'utf8');
return this.loadFromPem(data, options);
Expand Down
2 changes: 0 additions & 2 deletions specification/sidechain/AddressApis.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { z } from 'zod';
import { addressValidation } from '../common';
import { IZodSchemaToApiTypes } from '../utils/IZodApi';
import { AddressSignatureSchema } from '../types/IAddressSignature';

export const AddressApiSchemas = {
'Address.getBalance': {
Expand All @@ -15,7 +14,6 @@ export const AddressApiSchemas = {
'Address.register': {
args: z.object({
address: addressValidation,
signature: AddressSignatureSchema,
}),
result: z.object({
success: z.boolean(),
Expand Down
7 changes: 0 additions & 7 deletions specification/sidechain/MicronoteBatchApis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ import { IZodSchemaToApiTypes } from '../utils/IZodApi';
const fundsIdValidation = z.number().int().positive();

export const MicronoteBatchApiSchemas = {
'MicronoteBatch.get': {
args: z.undefined().nullish(),
result: z.object({
active: MicronoteBatchSchema,
giftCard: MicronoteBatchSchema.optional(),
}),
},
'MicronoteBatch.fund': {
args: z.object({
note: NoteSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { z } from 'zod';
import { IZodSchemaToApiTypes } from '../utils/IZodApi';
import { identityValidation, micronoteTokenValidation, signatureValidation } from '../common';
import { BlockSettingsSchema } from '../types/IBlockSettings';
import { MicronoteBatchSchema } from '../types/IMicronoteBatch';

export const SidechainSettingsApiSchemas = {
export const SidechainInfoApiSchemas = {
'Sidechain.settings': {
args: z.object({
identity: identityValidation
Expand All @@ -17,11 +18,19 @@ export const SidechainSettingsApiSchemas = {
rootIdentities: identityValidation.array(),
identityProofSignatures: signatureValidation.array().optional(),
latestBlockSettings: BlockSettingsSchema,
usdToArgonConversionRate: z.number(),
settlementFeeMicrogons: micronoteTokenValidation,
batchDurationMinutes: z.number().int(),
}),
},
'Sidechain.openBatches': {
args: z.undefined().nullish(),
result: z.object({
micronote: MicronoteBatchSchema.array(),
giftCard: MicronoteBatchSchema.optional(),
}),
},
};

type ISidechainSettingsApis = IZodSchemaToApiTypes<typeof SidechainSettingsApiSchemas>;
export default ISidechainSettingsApis;
type ISidechainInfoApis = IZodSchemaToApiTypes<typeof SidechainInfoApiSchemas>;
export default ISidechainInfoApis;
4 changes: 2 additions & 2 deletions specification/sidechain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { FundingTransferApiSchemas } from './FundingTransferApis';
import { AddressApiSchemas } from './AddressApis';
import { StakeApiSchemas } from './StakeApis';
import { GiftCardApiSchemas } from './GiftCardApis';
import { SidechainSettingsApiSchemas } from './SidechainSettingsApis';
import { SidechainInfoApiSchemas } from './SidechainInfoApis';
import { IZodHandlers, IZodSchemaToApiTypes } from '../utils/IZodApi';

const SidechainApiSchema = {
...SidechainSettingsApiSchemas,
...SidechainInfoApiSchemas,
...AddressApiSchemas,
...FundingTransferApiSchemas,
...MicronoteApiSchemas,
Expand Down
3 changes: 3 additions & 0 deletions specification/types/IMicronoteBatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { z } from 'zod';
import { addressValidation, identityValidation, signatureValidation } from '../common';

export const MicronoteBatchSchema = z.object({
batchHost: z.string().url(),
batchSlug: z
.string()
.regex(/^(?:gifts_|micro_)[0-9A-F]+$/i)
.length(14),
plannedClosingTime: z.date(),
stopNewNotesTime: z.date(),
isGiftCardBatch: z.boolean(),
micronoteBatchIdentity: identityValidation,
micronoteBatchAddress: addressValidation,
Expand Down

0 comments on commit 8583846

Please sign in to comment.