Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Updated core ownership records #129

Merged
merged 2 commits into from
Aug 23, 2023
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
24 changes: 15 additions & 9 deletions proto/coreOwnership/v1.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ message CoreOwnership_1 {

Common_1 common = 1;

string action = 5;
bytes coreId = 6;
bytes projectId = 7;
string storeType = 8;
string signature = 9;
int32 authorIndex = 10;
int32 deviceIndex = 11;
bytes authorId = 12;
string capabilityType = 13;
bytes authCoreId = 5;
bytes configCoreId = 6;
bytes dataCoreId = 7;
bytes blobCoreId = 8;
bytes blobIndexCoreId = 9;
CoreSignatures coreSignatures = 10;
bytes identitySignature = 11;

message CoreSignatures {
bytes auth = 1;
bytes config = 2;
bytes data = 3;
bytes blob = 4;
bytes blobIndex = 5;
}
}
39 changes: 28 additions & 11 deletions schema/coreOwnership/v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,40 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://mapeo.world/schemas/coreOwnership/v1.json",
"title": "CoreOwnership",
"description": "Which cores belong to which identity key?",
"type": "object",
"properties": {
"schemaName": {
"type": "string",
"const": "coreOwnership"
},
"action": {
"type": "string"
"authCoreId": {
"type": "string",
"description": "Hex-encoded key of auth store writer core"
},
"configCoreId": {
"type": "string",
"description": "Hex-encoded key of config store writer core"
},
"dataCoreId": {
"type": "string",
"description": "Hex-encoded key of data store writer core"
},
"blobCoreId": {
"type": "string",
"description": "Hex-encoded key of blob store writer core"
},
"coreId": { "type": "string" },
"projectId": { "type": "string" },
"storeType": { "type": "string" },
"signature": { "type": "string" },
"authorIndex": { "type": "integer" },
"deviceIndex": { "type": "integer" },
"authorId": {"type": "string"},
"capabilityType": {"type": "string"}
"blobIndexCoreId": {
"type": "string",
"description": "Hex-encoded key of blobIndex store writer core"
}
},
"required": ["schemaName", "action", "coreId", "projectId", "storeType", "signature", "authorIndex", "deviceIndex", "authorId", "capabilityType"]
"required": [
"schemaName",
"authCoreId",
"configCoreId",
"dataCoreId",
"blobCoreId",
"blobIndexCoreId"
]
}
7 changes: 5 additions & 2 deletions src/decode.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ProtoTypes } from './proto/types.js'
import {
type MapeoDoc,
type ProtoTypesWithSchemaInfo,
type SchemaName,
type DataTypeId,
type ValidSchemaDef,
type MapeoDocInternal,
} from './types.js'

import { Decode } from './proto/index.js'
Expand Down Expand Up @@ -37,7 +37,10 @@ for (const [schemaName, dataTypeId] of Object.entries(dataTypeIds) as Array<
* @param buf Buffer to be decoded
* @param versionObj public key (coreKey) of the core where this block is stored, and the index of the block in the core.
* */
export function decode(buf: Buffer, versionObj: VersionIdObject): MapeoDoc {
export function decode(
buf: Buffer,
versionObj: VersionIdObject
): MapeoDocInternal {
const schemaDef = decodeBlockPrefix(buf)

const encodedMsg = buf.subarray(
Expand Down
12 changes: 10 additions & 2 deletions src/encode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { MapeoDoc, OmitUnion, SchemaName, ValidSchemaDef } from './types.js'
import {
type MapeoDocInternal,
type OmitUnion,
type SchemaName,
type ValidSchemaDef,
} from './types.js'
import { currentSchemaVersions, dataTypeIds } from './config.js'
// @ts-ignore
import * as cenc from 'compact-encoding'
Expand All @@ -13,12 +18,15 @@ import {
convertDeviceInfo,
convertCoreOwnership,
} from './lib/encode-converstions.js'
import { CoreOwnership } from './index.js'

/**
* Encode a an object validated against a schema as a binary protobuf prefixed
* with the encoded data type ID and schema version, to send to an hypercore.
*/
export function encode(mapeoDoc: OmitUnion<MapeoDoc, 'versionId'>): Buffer {
export function encode(
mapeoDoc: OmitUnion<MapeoDocInternal, 'versionId'>
): Buffer {
const { schemaName } = mapeoDoc
const schemaVersion = currentSchemaVersions[schemaName]
const schemaDef = { schemaName, schemaVersion }
Expand Down
26 changes: 21 additions & 5 deletions src/lib/decode-conversions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
type MapeoCommon,
type TagValuePrimitive,
type JsonTagValue,
type MapeoDocInternal,
} from '../types.js'
import { VersionIdObject, getVersionId } from './utils.js'

Expand All @@ -20,7 +21,7 @@ import { VersionIdObject, getVersionId } from './utils.js'
type ConvertFunction<TSchemaName extends SchemaName> = (
message: Extract<ProtoTypesWithSchemaInfo, { schemaName: TSchemaName }>,
versionObj: VersionIdObject
) => FilterBySchemaName<MapeoDoc, TSchemaName>
) => FilterBySchemaName<MapeoDocInternal, TSchemaName>

export const convertProject: ConvertFunction<'project'> = (
message,
Expand Down Expand Up @@ -151,14 +152,29 @@ export const convertCoreOwnership: ConvertFunction<'coreOwnership'> = (
message,
versionObj
) => {
const { common, schemaVersion, ...rest } = message
if (!message.coreSignatures) {
throw new Error('Invalid message: missing core signatures')
}
const {
common,
schemaVersion,
authCoreId,
configCoreId,
dataCoreId,
blobCoreId,
blobIndexCoreId,
...rest
} = message
const jsonSchemaCommon = convertCommon(common, versionObj)
return {
...jsonSchemaCommon,
...rest,
coreId: message.coreId.toString('hex'),
projectId: message.projectId.toString('hex'),
authorId: message.authorId.toString('hex'),
authCoreId: authCoreId.toString('hex'),
configCoreId: configCoreId.toString('hex'),
dataCoreId: dataCoreId.toString('hex'),
blobCoreId: blobCoreId.toString('hex'),
blobIndexCoreId: blobIndexCoreId.toString('hex'),
coreSignatures: message.coreSignatures,
}
}

Expand Down
20 changes: 12 additions & 8 deletions src/lib/encode-converstions.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import { CurrentProtoTypes } from '../proto/types.js'
import {
MapeoDoc,
ProtoTypesWithSchemaInfo,
SchemaName,
MapeoCommon,
TagValuePrimitive,
JsonTagValue,
OmitUnion,
CoreOwnershipSignatures,
MapeoDocInternal,
} from '../types.js'
import { TagValue_1, type TagValue_1_PrimitiveValue } from '../proto/tags/v1.js'
import { Observation_5_Metadata } from '../proto/observation/v5.js'
import { parseVersionId } from './utils.js'
import { CoreOwnership } from '../index.js'

/** Function type for converting a protobuf type of any version for a particular
* schema name, and returning the most recent JSONSchema type */
type ConvertFunction<TSchemaName extends SchemaName> = (
mapeoDoc: Extract<
OmitUnion<MapeoDoc, 'versionId'>,
OmitUnion<MapeoDocInternal, 'versionId'>,
{ schemaName: TSchemaName }
>
) => CurrentProtoTypes[TSchemaName]
Expand Down Expand Up @@ -120,15 +122,17 @@ export const convertDeviceInfo: ConvertFunction<'deviceInfo'> = (mapeoDoc) => {
}
}

export const convertCoreOwnership: ConvertFunction<'coreOwnership'> = (
mapeoDoc
) => {
export const convertCoreOwnership = (
mapeoDoc: Omit<CoreOwnership, 'versionId'> & CoreOwnershipSignatures
): CurrentProtoTypes['coreOwnership'] => {
return {
common: convertCommon(mapeoDoc),
...mapeoDoc,
coreId: Buffer.from(mapeoDoc.coreId, 'hex'),
projectId: Buffer.from(mapeoDoc.projectId, 'hex'),
authorId: Buffer.from(mapeoDoc.authorId, 'hex'),
authCoreId: Buffer.from(mapeoDoc.authCoreId, 'hex'),
configCoreId: Buffer.from(mapeoDoc.configCoreId, 'hex'),
dataCoreId: Buffer.from(mapeoDoc.dataCoreId, 'hex'),
blobCoreId: Buffer.from(mapeoDoc.blobCoreId, 'hex'),
blobIndexCoreId: Buffer.from(mapeoDoc.blobIndexCoreId, 'hex'),
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Shared types
import { type ProtoTypesWithSchemaInfo as AllProtoTypesWithSchemaInfo } from './proto/types.js'
import {
CoreOwnership,
type MapeoDoc as AllMapeoDocs,
type MapeoValue as AllMapeoValues,
type MapeoCommon,
Expand Down Expand Up @@ -52,10 +53,23 @@ export type MapeoValue = FilterBySchemaName<
AllMapeoValues,
SupportedSchemaNames
>
/** The decode and encode functions expect core ownership signatures as buffers,
* but these are not included in the JSON schema definitions because they are
* stripped before they are indexed */
export type MapeoDocInternal =
| Exclude<MapeoDoc, CoreOwnership>
| (CoreOwnership & CoreOwnershipSignatures)

/** Union of all valid data type ids */
export type DataTypeId = Values<typeof dataTypeIds>

type Namespace = 'auth' | 'config' | 'data' | 'blob' | 'blobIndex'

export type CoreOwnershipSignatures = {
coreSignatures: Record<Namespace, Buffer>
identitySignature: Buffer
}

// HELPER TYPES
/**
* This is a Pick over a union, that keeps it as a distributive type
Expand Down
24 changes: 14 additions & 10 deletions test/fixtures/good-docs-completed.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { cachedValues } from './cached.js'
/**
* @type {Array<{
* expected: Partial<import('../../dist/types').MapeoDoc>,
* doc: import('../../dist/types').MapeoDoc
* doc: import('../../dist/types').MapeoDocInternal
* }>}
*/
export const goodDocsCompleted = [
Expand Down Expand Up @@ -168,15 +168,19 @@ export const goodDocsCompleted = [
createdAt: cachedValues.createdAt,
updatedAt: cachedValues.updatedAt,
links: [],
action: 'remove',
coreId: cachedValues.coreId,
projectId: cachedValues.projectId,
storeType: 'blob',
signature: 'mySig',
authorIndex: 100,
deviceIndex: 2,
authorId: cachedValues.authorId,
capabilityType: 'someCapability',
authCoreId: Buffer.from('authCoreId').toString('hex'),
configCoreId: Buffer.from('configCoreId').toString('hex'),
dataCoreId: Buffer.from('dataCoreId').toString('hex'),
blobCoreId: Buffer.from('blobCoreId').toString('hex'),
blobIndexCoreId: Buffer.from('blobIndexCoreId').toString('hex'),
coreSignatures: {
auth: Buffer.from('auth'),
config: Buffer.from('config'),
data: Buffer.from('data'),
blob: Buffer.from('blob'),
blobIndex: Buffer.from('blobIndex'),
},
identitySignature: Buffer.from('identity'),
},
expected: {},
},
Expand Down
24 changes: 14 additions & 10 deletions test/fixtures/good-docs-minimal.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { cachedValues } from './cached.js'
*
* @type {Array<{
* expected: Partial<import('../../dist/types').MapeoDoc>,
* doc: import('../../dist/types').MapeoDoc
* doc: import('../../dist/types').MapeoDocInternal
* }>}
*/
export const goodDocsMinimal = [
Expand Down Expand Up @@ -112,15 +112,19 @@ export const goodDocsMinimal = [
createdAt: cachedValues.createdAt,
updatedAt: cachedValues.updatedAt,
links: [],
action: 'remove',
coreId: cachedValues.coreId,
projectId: cachedValues.projectId,
storeType: 'blob',
signature: 'mySig',
authorId: cachedValues.authorId,
capabilityType: 'someCapability',
authorIndex: 0,
deviceIndex: 0,
authCoreId: Buffer.from('authCoreId').toString('hex'),
configCoreId: Buffer.from('configCoreId').toString('hex'),
dataCoreId: Buffer.from('dataCoreId').toString('hex'),
blobCoreId: Buffer.from('blobCoreId').toString('hex'),
blobIndexCoreId: Buffer.from('blobIndexCoreId').toString('hex'),
coreSignatures: {
auth: Buffer.from('auth'),
config: Buffer.from('config'),
data: Buffer.from('data'),
blob: Buffer.from('blob'),
blobIndex: Buffer.from('blobIndex'),
},
identitySignature: Buffer.from('identity'),
},
expected: {},
},
Expand Down
10 changes: 9 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,17 @@ then decoding and comparing the two objects - extra values shouldn't be present`
})

/**
* Remove undefined properties (deeply) from an object, by round-tripping to
* JSON. Also handles Buffers via JSON.parse reviver
*
* @param {object} obj
* @return {object}
* */
function stripUndef(obj) {
return JSON.parse(JSON.stringify(obj))
return JSON.parse(JSON.stringify(obj), (key, value) => {
if (value.type === 'Buffer') {
return Buffer.from(value.data)
}
return value
})
}
Loading