Skip to content
Open
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
18 changes: 18 additions & 0 deletions packages/common/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,21 @@ export function assertDefined<T>(
throw new Error(msg ?? "Value is undefined");
}
}

export function compareStrings(a: string, b: string): number {
// eslint-disable-next-line no-nested-ternary
return a === b ? 0 : a > b ? 1 : -1;
}

export function recordByKey<Object>(
array: Object[],
keyMapper: (obj: Object) => string
): Record<string, Object> {
return Object.fromEntries(
array.map((obj) => {
const key = keyMapper(obj);
const copy = { ...obj };
return [key, copy];
})
);
}
106 changes: 42 additions & 64 deletions packages/library/src/hooks/RuntimeFeeAnalyzerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ConfigurableModule,
createMerkleTree,
InMemoryMerkleTreeStorage,
recordByKey,
} from "@proto-kit/common";
import { Runtime, RuntimeModulesRecord } from "@proto-kit/module";
import { container, inject } from "tsyringe";
Expand All @@ -10,6 +11,7 @@ import {
RuntimeTransaction,
NetworkState,
} from "@proto-kit/protocol";
import { RuntimeAnalyzerService } from "@proto-kit/sequencer";
import { Field, Poseidon, Struct } from "o1js";

import { UInt64 } from "../math/UInt64";
Expand Down Expand Up @@ -65,6 +67,7 @@ export class RuntimeFeeAnalyzerService extends ConfigurableModule<RuntimeFeeAnal
};

public constructor(
public runtimeAnalyzerService: RuntimeAnalyzerService,
@inject("Runtime") public runtime: Runtime<RuntimeModulesRecord>
) {
super();
Expand All @@ -81,86 +84,61 @@ export class RuntimeFeeAnalyzerService extends ConfigurableModule<RuntimeFeeAnal
});

container.resolve(RuntimeMethodExecutionContext).clear();
let methodCounter = 0;
const [values, indexes] =
await this.runtime.zkProgrammable.zkProgram.reduce<
Promise<[FeeTreeValues, FeeIndexes]>
>(
async (accum, program) => {
const [valuesProg, indexesProg] = await accum;
const analyzedMethods = await program.analyzeMethods();
const [valuesMeth, indexesMeth] = Object.keys(program.methods).reduce<
[FeeTreeValues, FeeIndexes]
>(
// eslint-disable-next-line @typescript-eslint/no-shadow
([values, indexes], combinedMethodName) => {
const { rows } = analyzedMethods[combinedMethodName];
// const rows = 1000;
const [moduleName, methodName] = combinedMethodName.split(".");
const methodId = this.runtime.methodIdResolver.getMethodId(
moduleName,
methodName
);

/**
* Determine the fee config for the given method id, and merge it with
* the default fee config.
*/
return [
{
...values,

[methodId.toString()]: {
methodId,

baseFee:
this.config.methods[combinedMethodName]?.baseFee ??
this.config.baseFee,

perWeightUnitFee:
this.config.methods[combinedMethodName]
?.perWeightUnitFee ?? this.config.perWeightUnitFee,

weight:
this.config.methods[combinedMethodName]?.weight ??
BigInt(rows),
},
},
{
...indexes,
// eslint-disable-next-line no-plusplus
[methodId.toString()]: BigInt(methodCounter++),
},
];
},
[{}, {}]
);
return [
{ ...valuesProg, ...valuesMeth },
{ ...indexesProg, ...indexesMeth },
];
},
Promise.resolve([{}, {}])
);

const runtimeInfo = await this.runtimeAnalyzerService.getRuntimeInfo();

const values = Object.entries(runtimeInfo).map(
([combinedMethodName, { rows }]) => {
// const rows = 1000;
const [moduleName, methodName] = combinedMethodName.split(".");
const methodId = this.runtime.methodIdResolver.getMethodId(
moduleName,
methodName
);

/**
* Determine the fee config for the given method id, and merge it with
* the default fee config.
*/
return {
methodId,

baseFee:
this.config.methods[combinedMethodName]?.baseFee ??
this.config.baseFee,

perWeightUnitFee:
this.config.methods[combinedMethodName]?.perWeightUnitFee ??
this.config.perWeightUnitFee,

weight:
this.config.methods[combinedMethodName]?.weight ?? BigInt(rows),
};
}
);
const tree = new FeeTree(new InMemoryMerkleTreeStorage());

Object.values(values).forEach((value, index) => {
const indexes = values.map((value, index) => {
const feeConfig = new MethodFeeConfigData({
methodId: Field(value.methodId),
baseFee: UInt64.from(value.baseFee),
weight: UInt64.from(value.weight),
perWeightUnitFee: UInt64.from(value.perWeightUnitFee),
});
tree.setLeaf(BigInt(index), feeConfig.hash());
return [value.methodId.toString(), BigInt(index)] as const;
});

this.persistedFeeTree = { tree, values, indexes };
this.persistedFeeTree = {
tree,
values: recordByKey(values, (v) => v.methodId.toString()),
indexes: Object.fromEntries<bigint>(indexes),
};
}

public getFeeTree() {
if (this.persistedFeeTree === undefined) {
throw new Error("Fee Tree not intialized");
throw new Error("Fee Tree not initialized");
}

return this.persistedFeeTree;
Expand Down
10 changes: 8 additions & 2 deletions packages/library/src/hooks/TransactionFeeHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "@proto-kit/protocol";
import { Field, Provable, PublicKey } from "o1js";
import { noop } from "@proto-kit/common";
import { RuntimeAnalyzerService } from "@proto-kit/sequencer";

import { UInt64 } from "../math/UInt64";
import { Balance, TokenId } from "../runtime/Balances";
Expand Down Expand Up @@ -53,7 +54,9 @@ export class TransactionFeeHook extends ProvableTransactionHook<TransactionFeeHo
public constructor(
// dependency on runtime, since balances are part of runtime logic
@inject("Runtime") public runtime: Runtime<RuntimeModulesRecord>,
@inject("Balances") public balances: Balances
@inject("Balances") public balances: Balances,
// TODO Check that the container is the right one here
public runtimeAnalyzerService: RuntimeAnalyzerService
) {
super();
}
Expand All @@ -79,7 +82,10 @@ export class TransactionFeeHook extends ProvableTransactionHook<TransactionFeeHo
}

public async start() {
this.persistedFeeAnalyzer = new RuntimeFeeAnalyzerService(this.runtime);
this.persistedFeeAnalyzer = new RuntimeFeeAnalyzerService(
this.runtimeAnalyzerService,
this.runtime
);
this.verifyConfig();
this.persistedFeeAnalyzer.config = this.config;
await this.persistedFeeAnalyzer.initializeFeeTree();
Expand Down
1 change: 1 addition & 0 deletions packages/module/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from "./state/InMemoryStateService";
export * from "./method/MethodParameterEncoder";
export * from "./runtime/MethodIdResolver";
export * from "./factories/MethodIdFactory";
export * from "./testing/TestingRuntime";
Loading