Skip to content
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
4 changes: 2 additions & 2 deletions bin/test-runner/w3f/codec/work-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ export const workPackageFromJson = json.object<JsonWorkPackage, WorkPackage>(
},
({ authorization, auth_code_host, auth_code_hash, authorizer_config, context, items }) =>
WorkPackage.create({
authorization,
authToken: authorization,
authCodeHost: auth_code_host,
authCodeHash: auth_code_hash,
parametrization: authorizer_config,
authConfiguration: authorizer_config,
context,
items: FixedSizeArray.new(items, tryAsWorkItemsCount(items.length)),
}),
Expand Down
18 changes: 9 additions & 9 deletions packages/jam/block/work-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const MAX_NUMBER_OF_WORK_ITEMS = 16;
/**
* A piece of work done within a core.
*
* `P = (j ∈ Y, h ∈ NS, u ∈ H, p ∈ Y, x ∈ X, w ∈ ⟦I⟧1∶I)
* `P = (j ∈ Y, h ∈ NS, u ∈ H, f ∈ Y, x ∈ X, w ∈ ⟦I⟧1∶I)
*
* https://graypaper.fluffylabs.dev/#/579bd12/197000197200
*/
Expand All @@ -40,34 +40,34 @@ export class WorkPackage extends WithDebug {
authCodeHost: codec.u32.asOpaque<ServiceId>(),
authCodeHash: codec.bytes(HASH_SIZE).asOpaque<CodeHash>(),
context: RefineContext.Codec,
authorization: codec.blob,
parametrization: codec.blob,
authToken: codec.blob,
authConfiguration: codec.blob,
items: codec.sequenceVarLen(WorkItem.Codec).convert(
(x) => x,
(items) => FixedSizeArray.new(items, tryAsWorkItemsCount(items.length)),
),
});

static create({
authorization,
authToken,
authCodeHost,
authCodeHash,
parametrization,
authConfiguration,
context,
items,
}: CodecRecord<WorkPackage>) {
return new WorkPackage(authorization, authCodeHost, authCodeHash, parametrization, context, items);
return new WorkPackage(authToken, authCodeHost, authCodeHash, authConfiguration, context, items);
}

private constructor(
/** `j`: simple blob acting as an authorization token */
public readonly authorization: BytesBlob,
public readonly authToken: BytesBlob,
/** `h`: index of the service that hosts the authorization code */
public readonly authCodeHost: ServiceId,
/** `u`: authorization code hash */
public readonly authCodeHash: CodeHash,
/** `p`: authorization parametrization blob */
public readonly parametrization: BytesBlob,
/** `f`: authorization configuration blob */
public readonly authConfiguration: BytesBlob,
/** `x`: context in which the refine function should run */
public readonly context: RefineContext,
/**
Expand Down
27 changes: 27 additions & 0 deletions packages/jam/executor/pvm-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ export type AccumulateHostCallExternalities = {
serviceExternalities: general.AccountsInfo & general.AccountsLookup & general.AccountsWrite & general.AccountsRead;
};

export type IsAuthorizedHostCallExternalities = {
fetchExternalities: general.IIsAuthorizedFetch;
};

type OnTransferHostCallExternalities = {
partialState: general.AccountsInfo & general.AccountsLookup & general.AccountsWrite & general.AccountsRead;
fetchExternalities: general.IFetchExternalities;
Expand Down Expand Up @@ -134,6 +138,17 @@ export class PvmExecutor {
return accumulateHandlers.concat(generalHandlers);
}

/** Prepare is-authorized host call handlers */
private static prepareIsAuthorizedHostCalls(serviceId: ServiceId, externalities: IsAuthorizedHostCallExternalities) {
const generalHandlers: HostCallHandler[] = [
new general.LogHostCall(serviceId),
new general.GasHostCall(serviceId),
new general.Fetch(serviceId, externalities.fetchExternalities),
];

return generalHandlers;
}

/** Prepare on transfer host call handlers */
private static prepareOnTransferHostCalls(serviceId: ServiceId, externalities: OnTransferHostCallExternalities) {
const generalHandlers: HostCallHandler[] = [
Expand Down Expand Up @@ -175,6 +190,18 @@ export class PvmExecutor {
return new PvmExecutor(serviceCode, hostCallHandlers, entrypoint.REFINE, instances);
}

/** A utility function that can be used to prepare is-authorized executor */
static async createIsAuthorizedExecutor(
serviceId: ServiceId,
serviceCode: BytesBlob,
externalities: IsAuthorizedHostCallExternalities,
pvm: PvmBackend,
) {
const hostCallHandlers = PvmExecutor.prepareIsAuthorizedHostCalls(serviceId, externalities);
const instances = await PvmExecutor.prepareBackend(pvm);
return new PvmExecutor(serviceCode, hostCallHandlers, entrypoint.IS_AUTHORIZED, instances);
}

/** A utility function that can be used to prepare accumulate executor */
static async createAccumulateExecutor(
serviceId: ServiceId,
Expand Down
Binary file added packages/jam/in-core/fixtures/authorizer.pvm
Binary file not shown.
76 changes: 59 additions & 17 deletions packages/jam/in-core/in-core.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,63 @@
import assert from "node:assert";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { before, describe, it } from "node:test";
import type { HeaderHash, StateRootHash } from "@typeberry/block";
import type { CodeHash, HeaderHash, StateRootHash } from "@typeberry/block";
import { tryAsCoreIndex, tryAsServiceGas, tryAsServiceId, tryAsTimeSlot } from "@typeberry/block";
import type { WorkPackageHash } from "@typeberry/block/refine-context.js";
import { RefineContext } from "@typeberry/block/refine-context.js";
import { WorkItem } from "@typeberry/block/work-item.js";
import { tryAsWorkItemsCount, WorkPackage } from "@typeberry/block/work-package.js";
import { Bytes, BytesBlob } from "@typeberry/bytes";
import { Encoder } from "@typeberry/codec";
import { asKnownSize, FixedSizeArray } from "@typeberry/collections";
import { asKnownSize, FixedSizeArray, HashDictionary } from "@typeberry/collections";
import { type ChainSpec, PvmBackend, tinyChainSpec } from "@typeberry/config";
import { InMemoryStates } from "@typeberry/database";
import { Blake2b, HASH_SIZE, WithHash } from "@typeberry/hash";
import { tryAsU16 } from "@typeberry/numbers";
import { testState } from "@typeberry/state/test.utils.js";
import { Blake2b, HASH_SIZE, type OpaqueHash, WithHash } from "@typeberry/hash";
import { tryAsU16, tryAsU32, tryAsU64 } from "@typeberry/numbers";
import { InMemoryService, InMemoryState, PreimageItem, ServiceAccountInfo } from "@typeberry/state";
import { InCore, RefineError } from "./in-core.js";

// Load the authorizer PVM fixture (checks authToken === authConfiguration).
const AUTHORIZER_PVM = BytesBlob.blobFrom(readFileSync(resolve(import.meta.dirname, "fixtures/authorizer.pvm")));
const AUTH_SERVICE_ID = tryAsServiceId(1);

let blake2b: Blake2b;

before(async () => {
blake2b = await Blake2b.createHasher();
});

function createWorkItem(serviceId = 1) {
function getAuthCodeHash() {
return blake2b.hashBytes(AUTHORIZER_PVM).asOpaque<CodeHash>();
}

function createService(serviceId: typeof AUTH_SERVICE_ID, codeHash: OpaqueHash, code: BytesBlob): InMemoryService {
return new InMemoryService(serviceId, {
info: ServiceAccountInfo.create({
codeHash: codeHash.asOpaque<CodeHash>(),
balance: tryAsU64(10_000_000_000),
accumulateMinGas: tryAsServiceGas(0n),
onTransferMinGas: tryAsServiceGas(0n),
storageUtilisationBytes: tryAsU64(0),
storageUtilisationCount: tryAsU32(0),
gratisStorage: tryAsU64(0),
created: tryAsTimeSlot(0),
lastAccumulation: tryAsTimeSlot(0),
parentService: tryAsServiceId(0),
}),
preimages: HashDictionary.fromEntries(
[PreimageItem.create({ hash: codeHash.asOpaque(), blob: code })].map((x) => [x.hash, x]),
),
lookupHistory: HashDictionary.fromEntries([]),
storage: new Map(),
});
}

function createWorkItem(codeHash: CodeHash, serviceId = 1) {
return WorkItem.create({
service: tryAsServiceId(serviceId),
codeHash: Bytes.zero(HASH_SIZE).asOpaque(),
codeHash,
payload: BytesBlob.empty(),
refineGasLimit: tryAsServiceGas(1_000_000),
accumulateGasLimit: tryAsServiceGas(1_000_000),
Expand All @@ -35,12 +67,17 @@ function createWorkItem(serviceId = 1) {
});
}

function createWorkPackage(anchorHash: HeaderHash, stateRoot: StateRootHash, lookupAnchorSlot = 0) {
function createWorkPackage(
anchorHash: HeaderHash,
stateRoot: StateRootHash,
authCodeHash: CodeHash,
lookupAnchorSlot = 0,
) {
return WorkPackage.create({
authorization: BytesBlob.empty(),
authCodeHost: tryAsServiceId(1),
authCodeHash: Bytes.zero(HASH_SIZE).asOpaque(),
parametrization: BytesBlob.empty(),
authToken: BytesBlob.empty(),
authCodeHost: AUTH_SERVICE_ID,
authCodeHash,
authConfiguration: BytesBlob.empty(),
context: RefineContext.create({
anchor: anchorHash,
stateRoot,
Expand All @@ -49,7 +86,7 @@ function createWorkPackage(anchorHash: HeaderHash, stateRoot: StateRootHash, loo
lookupAnchorSlot: tryAsTimeSlot(lookupAnchorSlot),
prerequisites: [],
}),
items: FixedSizeArray.new([createWorkItem()], tryAsWorkItemsCount(1)),
items: FixedSizeArray.new([createWorkItem(authCodeHash)], tryAsWorkItemsCount(1)),
});
}

Expand All @@ -68,7 +105,8 @@ describe("InCore", () => {

const anchorHash = Bytes.fill(HASH_SIZE, 1).asOpaque<HeaderHash>();
const stateRoot = Bytes.zero(HASH_SIZE).asOpaque<StateRootHash>();
const workPackage = createWorkPackage(anchorHash, stateRoot);
const authCodeHash = getAuthCodeHash();
const workPackage = createWorkPackage(anchorHash, stateRoot, authCodeHash);

const result = await inCore.refine(
hashWorkPackage(spec, workPackage),
Expand All @@ -86,12 +124,16 @@ describe("InCore", () => {
const states = new InMemoryStates(spec);
const inCore = new InCore(spec, states, PvmBackend.BuiltIn, blake2b);

const authCodeHash = getAuthCodeHash();
const anchorHash = Bytes.fill(HASH_SIZE, 1).asOpaque<HeaderHash>();
const state = testState();
const state = InMemoryState.partial(spec, {
timeslot: tryAsTimeSlot(16),
services: new Map([[AUTH_SERVICE_ID, createService(AUTH_SERVICE_ID, authCodeHash, AUTHORIZER_PVM)]]),
});
await states.insertInitialState(anchorHash, state);

const correctStateRoot = await states.getStateRoot(state);
const workPackage = createWorkPackage(anchorHash, correctStateRoot, state.timeslot);
const workPackage = createWorkPackage(anchorHash, correctStateRoot, authCodeHash, state.timeslot);

const result = await inCore.refine(
hashWorkPackage(spec, workPackage),
Expand All @@ -100,7 +142,7 @@ describe("InCore", () => {
asKnownSize([[]]),
);

assert.strictEqual(result.isOk, true);
assert.strictEqual(result.isOk, true, `Expected OK but got error: ${result.isError ? result.details() : ""}`);
assert.strictEqual(result.ok.report.coreIndex, 0);
assert.strictEqual(result.ok.report.results.length, 1);
});
Expand Down
Loading
Loading