Skip to content

Commit

Permalink
feat(specification): credits api in datastore
Browse files Browse the repository at this point in the history
chore(specification): remove gift cards
feat(crypto): cli to import identities
feat(specification): short datastore versions
  • Loading branch information
blakebyrnes committed Jan 16, 2023
1 parent 545bbb0 commit 0dfab62
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 105 deletions.
57 changes: 57 additions & 0 deletions crypto/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { randomBytes } from 'crypto';
import Identity from '@ulixee/crypto/lib/Identity';
import * as Path from 'path';
import TypeSerializer from '@ulixee/commons/lib/TypeSerializer';
import UlixeeConfig from '@ulixee/commons/config/index';
import Address from './lib/Address';
import IAddressSettings, { ISignerType } from './interfaces/IAddressSettings';
import Ed25519 from './lib/Ed25519';

const logError = (err: Error): void => {
if (err instanceof APIError) {
Expand Down Expand Up @@ -132,5 +134,60 @@ export default function cliCommands(): Command {
}
});

cryptoCommands
.command('save-identity')
.description('Save an Identity PEM to a local file.')
.option('-k, --privateKey <key>', 'The private key bytes')
.option(
'-f, --filename <path>',
'Save this Identity to a filepath. If not specified, will be placed in <CACHE>/identities.',
)
.option('-p, --passphrase <phrase>', 'Save identity to a file with a passphrase.')
.option(
'-c, --passphrase-cipher <cipher>',
'Encrypt the internal key with a cipher (pkcs8 format).',
Identity.defaultPkcsCipher,
)
.action(async ({ privateKey, filename, passphraseCipher, passphrase }) => {
const ed25519 = Ed25519.createPrivateKeyFromBytes(Buffer.from(privateKey, 'base64'));

const identity = new Identity(ed25519);
identity.verifyKeys();

filename ||= Path.join(
UlixeeConfig.global.directoryPath,
'identities',
`${identity.bech32}.pem`,
);

await identity.save(filename, { passphrase, cipher: passphraseCipher });
console.log('Saved %s to %s', identity.bech32, filename); // eslint-disable-line no-console
});

cryptoCommands
.command('read-identity')
.description('Output the bech32 value of an identity.')
.option('--pem <pem>', 'The raw bytes of the PEM.')
.option(
'-f, --filename <path>',
'Save this Identity to a filepath. If not specified, will be console logged.',
)
.option('-p, --passphrase <phrase>', 'Save identity to a file with a passphrase.')
.enablePositionalOptions(true)
.action(({ pem, filename, passphrase }) => {
pem = pem.replaceAll('\\n', '\n');
if (filename) {
const identity = Identity.loadFromFile(Path.resolve(process.cwd(), filename), {
keyPassphrase: passphrase,
});

console.log(identity.bech32); // eslint-disable-line no-console
} else {
const identity = Identity.loadFromPem(pem, { keyPassphrase: passphrase });

console.log(identity.bech32); // eslint-disable-line no-console
}
});

return cryptoCommands;
}
16 changes: 3 additions & 13 deletions specification/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@ export const identityValidation = z
'This is not a Ulixee identity (Bech32 encoded hash starting with "id1").',
);

export const giftCardIdValidation = z.string().length(12);

export const giftCardRemptionKeyValidation = z
.string()
.length(62)
.regex(
/^gft1[ac-hj-np-z02-9]{58}/,
'This is not a Ulixee gift card redemption key (Bech32 encoded hash starting with "gft1").',
);

export const hashValidation = z
.instanceof(Buffer)
.refine(x => x.length === 32, { message: 'Hashes must be 32 bytes' });
Expand All @@ -52,8 +42,8 @@ export const micronoteTokenValidation = z.number().int().positive();

export const datastoreVersionHashValidation = z
.string()
.length(62)
.length(22)
.regex(
/^dbx1[ac-hj-np-z02-9]{58}/,
'This is not a Datastore versionHash (Bech32 encoded hash starting with "dbx1").',
/^dbx1[ac-hj-np-z02-9]{18}/,
'This is not a Datastore versionHash (first 21 characters of the bech32 encoded hash starting with "dbx1").',
);
68 changes: 56 additions & 12 deletions specification/datastore/DatastoreApis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,56 @@ export const DatastoreApiSchemas = {
.describe(
'Allow this upload to start a new version chain (do not link to previous versions)',
),
uploaderIdentity: identityValidation
adminIdentity: identityValidation
.optional()
.describe('If this server is private, an approved uploader Identity'),
uploaderSignature: signatureValidation
.describe(
'If this server is in production mode, an AdminIdentity approved on the Server or Datastore.',
),
adminSignature: signatureValidation
.optional()
.describe('A signature from an approved uploader Identity'),
.describe('A signature from an approved AdminIdentity'),
}),
result: z.object({
success: z.boolean(),
}),
},
'Datastore.creditsBalance': {
args: z.object({
datastoreVersionHash: datastoreVersionHashValidation.describe(
'The hash of the Datastore version to look at credits for.',
),
creditId: z.string().describe('CreditId issued by this datastore.'),
}),
result: z.object({
issuedCredits: micronoteTokenValidation.describe('Issued credits balance in microgons.'),
balance: micronoteTokenValidation.describe('Remaining credits balance in microgons.'),
}),
},
'Datastore.admin': {
args: z.object({
versionHash: datastoreVersionHashValidation.describe(
'The hash of a unique datastore version',
),
adminIdentity: identityValidation
.optional()
.describe('An admin identity for this Datastore.'),
adminSignature: signatureValidation
.optional()
.describe('A signature from the admin Identity'),
adminFunction: z.object({
ownerType: z
.enum(['table', 'crawler', 'function', 'datastore'])
.describe('Where to locate the function.'),
ownerName: z
.string()
.describe('The name of the owning function, table or crawler (if applicable).')
.optional(),
functionName: z.string().describe('The name of the function'),
}),
functionArgs: z.any().array().describe('The args to provide to the function.'),
}),
result: z.any().describe('A flexible result based on the type of api.'),
},
'Datastore.meta': {
args: z.object({
versionHash: datastoreVersionHashValidation.describe(
Expand Down Expand Up @@ -69,7 +108,7 @@ export const DatastoreApiSchemas = {
),
priceBreakdown: DatastoreFunctionPricing.array(),

schemaJson: z.string().optional().describe('The schema JSON if requested'),
schemaJson: z.any().optional().describe('The schema JSON if requested'),
}),
),
tablesByName: z.record(
Expand All @@ -88,7 +127,7 @@ export const DatastoreApiSchemas = {
.optional(),
})
.array(),
schemaJson: z.string().optional().describe('The schema JSON if requested'),
schemaJson: z.any().optional().describe('The schema JSON if requested'),
}),
),
schemaInterface: z
Expand All @@ -100,9 +139,6 @@ export const DatastoreApiSchemas = {
computePricePerQuery: micronoteTokenValidation.describe(
'The current server price per query. NOTE: if a server is implementing surge pricing, this amount could vary.',
),
giftCardIssuerIdentities: identityValidation
.array()
.describe('The identities this datastore allows gift card payments for (if any).'),
}),
},
'Datastore.stream': {
Expand All @@ -113,9 +149,12 @@ export const DatastoreApiSchemas = {
versionHash: datastoreVersionHashValidation.describe(
'The hash of this unique datastore version',
),
payment: PaymentSchema.optional().describe(
'Payment for this request created with an approved Ulixee Sidechain.',
),
payment: PaymentSchema.optional().describe('Payment for this request.'),
affiliateId: z
.string()
.regex(/aff[a-zA-Z_0-9-]{10}/)
.optional()
.describe('A tracking id to attribute payments to source affiliates.'),
authentication: z
.object({
identity: identityValidation,
Expand Down Expand Up @@ -152,6 +191,11 @@ export const DatastoreApiSchemas = {
versionHash: datastoreVersionHashValidation.describe(
'The hash of this unique datastore version',
),
affiliateId: z
.string()
.regex(/aff[a-zA-Z_0-9-]{10}/)
.optional()
.describe('A tracking id to attribute payments to source affiliates.'),
payment: PaymentSchema.optional().describe(
'Payment for this request created with an approved Ulixee Sidechain.',
),
Expand Down
66 changes: 0 additions & 66 deletions specification/sidechain/GiftCardApis.ts

This file was deleted.

1 change: 0 additions & 1 deletion specification/sidechain/SidechainInfoApis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export const SidechainInfoApiSchemas = {
args: z.undefined().nullish(),
result: z.object({
micronote: MicronoteBatchSchema.array(),
giftCard: MicronoteBatchSchema.optional(),
}),
},
};
Expand Down
2 changes: 0 additions & 2 deletions specification/sidechain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { MicronoteApiSchemas } from './MicronoteApis';
import { FundingTransferApiSchemas } from './FundingTransferApis';
import { AddressApiSchemas } from './AddressApis';
import { StakeApiSchemas } from './StakeApis';
import { GiftCardApiSchemas } from './GiftCardApis';
import { SidechainInfoApiSchemas } from './SidechainInfoApis';
import { RampApiSchemas } from './RampApis';
import { IZodHandlers, IZodSchemaToApiTypes } from '../utils/IZodApi';
Expand All @@ -17,7 +16,6 @@ const SidechainApiSchema = {
...MicronoteBatchApiSchemas,
...NoteApiSchemas,
...StakeApiSchemas,
...GiftCardApiSchemas,
...RampApiSchemas,
};

Expand Down
8 changes: 5 additions & 3 deletions specification/types/IDatastoreManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export const DatastoreManifestSchema = z.object({
/^scr1[ac-hj-np-z02-9]{58}/,
'This is not a Datastore scripthash (Bech32 encoded hash starting with "scr").',
),
adminIdentities: identityValidation
.array()
.describe(
'Administrators of this Datastore. If none are present, defaults to Administrators on the Miner.',
),
scriptEntrypoint: z.string().describe('A relative path from a project root'),
coreVersion: z.string().describe('Version of the Datastore Core Runtime'),
schemaInterface: z.string().optional().describe('The raw typescript schema for this Datastore'),
Expand Down Expand Up @@ -79,9 +84,6 @@ export const DatastoreManifestSchema = z.object({
}),
),
paymentAddress: addressValidation.optional(),
giftCardIssuerIdentity: identityValidation
.optional()
.describe('A gift card issuer identity for this Datastore.'),
});

export type IVersionHistoryEntry = z.infer<
Expand Down
3 changes: 1 addition & 2 deletions specification/types/IMicronoteBatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ export const MicronoteBatchSchema = z.object({
batchHost: z.string().url(),
batchSlug: z
.string()
.regex(/^(?:gifts_|micro_)[0-9A-F]+$/i)
.regex(/^[0-9A-Fa-f]+$/)
.length(14),
plannedClosingTime: z.date(),
stopNewNotesTime: z.date(),
isGiftCardBatch: z.boolean(),
minimumFundingCentagons: z.bigint().refine(x => x >= 1n),
micronoteBatchIdentity: identityValidation,
micronoteBatchAddress: addressValidation,
Expand Down
15 changes: 9 additions & 6 deletions specification/types/IPayment.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { z } from 'zod';
import {
blockHeightValidation,
giftCardIdValidation,
giftCardRemptionKeyValidation,
identityValidation,
micronoteIdValidation,
micronoteTokenValidation,
Expand Down Expand Up @@ -32,11 +30,16 @@ export const PaymentSchema = z.object({
.optional()
.describe('A hold authorization code granting sub-holds on a micronote.'),
}).optional(),
giftCard: z
credits: z
.object({
id: giftCardIdValidation,
sidechainIdentity: identityValidation,
redemptionKey: giftCardRemptionKeyValidation,
id: z
.string()
.length(12)
.regex(
/^cred[A-Za-z0-9_]{8}$/,
'This is not a Datastore credits id (starting with "cred", following by 8 alphanumeric characters).',
),
secret: z.string().length(12),
})
.optional(),
});
Expand Down

0 comments on commit 0dfab62

Please sign in to comment.