Skip to content

Commit 96d460a

Browse files
authored
Merge pull request #216 from proto-kit/fix/fee-analyzer-service
Fix to avoid duplicate entries for Fee Analyzer Service
2 parents f16477c + 5ed58d5 commit 96d460a

File tree

2 files changed

+308
-3
lines changed

2 files changed

+308
-3
lines changed

packages/library/src/hooks/RuntimeFeeAnalyzerService.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class RuntimeFeeAnalyzerService extends ConfigurableModule<RuntimeFeeAnal
8181
});
8282

8383
container.resolve(RuntimeMethodExecutionContext).clear();
84-
84+
let methodCounter = 0;
8585
const [values, indexes] =
8686
await this.runtime.zkProgrammable.zkProgram.reduce<
8787
Promise<[FeeTreeValues, FeeIndexes]>
@@ -93,7 +93,7 @@ export class RuntimeFeeAnalyzerService extends ConfigurableModule<RuntimeFeeAnal
9393
[FeeTreeValues, FeeIndexes]
9494
>(
9595
// eslint-disable-next-line @typescript-eslint/no-shadow
96-
([values, indexes], combinedMethodName, index) => {
96+
([values, indexes], combinedMethodName) => {
9797
const { rows } = analyzedMethods[combinedMethodName];
9898
// const rows = 1000;
9999
const [moduleName, methodName] = combinedMethodName.split(".");
@@ -128,7 +128,8 @@ export class RuntimeFeeAnalyzerService extends ConfigurableModule<RuntimeFeeAnal
128128
},
129129
{
130130
...indexes,
131-
[methodId.toString()]: BigInt(index),
131+
// eslint-disable-next-line no-plusplus
132+
[methodId.toString()]: BigInt(methodCounter++),
132133
},
133134
];
134135
},
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
import "reflect-metadata";
2+
3+
import { runtimeMethod, runtimeModule, RuntimeModule } from "@proto-kit/module";
4+
import { PrivateKey } from "o1js";
5+
import { expectDefined, noop } from "@proto-kit/common";
6+
import { inject } from "tsyringe";
7+
import { Balance, Balances, BalancesKey, TokenId } from "@proto-kit/library";
8+
9+
import { TestingAppChain } from "../src";
10+
11+
// This test is designed to check what happens when we have multiple zkPrograms.
12+
// Currently, the hardcoded maximum for methods per zkProgram is 8 (see Runtime.ts).
13+
// We will create 20 runtime methods to ensure 3 zkPrograms are created.
14+
15+
@runtimeModule()
16+
class TestModule1 extends RuntimeModule<unknown> {
17+
@runtimeMethod()
18+
public async Method_1() {
19+
noop();
20+
}
21+
22+
@runtimeMethod()
23+
public async Method_2() {
24+
noop();
25+
}
26+
27+
@runtimeMethod()
28+
public async Method_3() {
29+
noop();
30+
}
31+
32+
@runtimeMethod()
33+
public async Method_4() {
34+
noop();
35+
}
36+
37+
@runtimeMethod()
38+
public async Method_5() {
39+
noop();
40+
}
41+
42+
@runtimeMethod()
43+
public async Method_6() {
44+
noop();
45+
}
46+
47+
@runtimeMethod()
48+
public async Method_7() {
49+
noop();
50+
}
51+
52+
@runtimeMethod()
53+
public async Method_8() {
54+
noop();
55+
}
56+
57+
@runtimeMethod()
58+
public async Method_9() {
59+
noop();
60+
}
61+
62+
@runtimeMethod()
63+
public async Method_10() {
64+
noop();
65+
}
66+
}
67+
68+
@runtimeModule()
69+
class TestModule2 extends RuntimeModule<unknown> {
70+
@runtimeMethod()
71+
public async Method_1() {
72+
noop();
73+
}
74+
75+
@runtimeMethod()
76+
public async Method_2() {
77+
noop();
78+
}
79+
80+
@runtimeMethod()
81+
public async Method_3() {
82+
noop();
83+
}
84+
85+
@runtimeMethod()
86+
public async Method_4() {
87+
noop();
88+
}
89+
90+
@runtimeMethod()
91+
public async Method_5() {
92+
noop();
93+
}
94+
95+
@runtimeMethod()
96+
public async Method_6() {
97+
noop();
98+
}
99+
100+
@runtimeMethod()
101+
public async Method_7() {
102+
noop();
103+
}
104+
105+
@runtimeMethod()
106+
public async Method_8() {
107+
noop();
108+
}
109+
110+
@runtimeMethod()
111+
public async Method_9() {
112+
noop();
113+
}
114+
115+
@runtimeMethod()
116+
public async Method_10() {
117+
noop();
118+
}
119+
}
120+
121+
@runtimeModule()
122+
class Faucet extends RuntimeModule<unknown> {
123+
public constructor(@inject("Balances") public balances: Balances) {
124+
super();
125+
}
126+
127+
@runtimeMethod()
128+
public async drip() {
129+
await this.balances.mint(
130+
TokenId.from(0),
131+
this.transaction.sender.value,
132+
Balance.from(1000)
133+
);
134+
}
135+
}
136+
137+
describe("check fee analyzer", () => {
138+
const feeRecipientKey = PrivateKey.random();
139+
const senderKey = PrivateKey.random();
140+
141+
const appChain = TestingAppChain.fromRuntime({
142+
TestModule1,
143+
TestModule2,
144+
Faucet,
145+
});
146+
147+
beforeAll(async () => {
148+
appChain.configurePartial({
149+
Runtime: {
150+
TestModule1,
151+
TestModule2,
152+
Faucet,
153+
Balances,
154+
},
155+
156+
Protocol: {
157+
...appChain.config.Protocol!,
158+
TransactionFee: {
159+
tokenId: 0n,
160+
feeRecipient: feeRecipientKey.toPublicKey().toBase58(),
161+
baseFee: 0n,
162+
perWeightUnitFee: 0n,
163+
methods: {
164+
"TestModule1.Method_1": {
165+
baseFee: 9n,
166+
weight: 0n,
167+
perWeightUnitFee: 0n,
168+
},
169+
"TestModule1.Method_10": {
170+
baseFee: 8n,
171+
weight: 0n,
172+
perWeightUnitFee: 0n,
173+
},
174+
"TestModule2.Method_4": {
175+
baseFee: 7n,
176+
weight: 0n,
177+
perWeightUnitFee: 0n,
178+
},
179+
"TestModule2.Method_7": {
180+
baseFee: 6n,
181+
weight: 0n,
182+
perWeightUnitFee: 0n,
183+
},
184+
},
185+
},
186+
},
187+
});
188+
189+
await appChain.start();
190+
appChain.setSigner(senderKey);
191+
});
192+
193+
it("with multiple zk programs", async () => {
194+
expect.assertions(12);
195+
const testModule1 = appChain.runtime.resolve("TestModule1");
196+
const testModule2 = appChain.runtime.resolve("TestModule2");
197+
const faucet = appChain.runtime.resolve("Faucet");
198+
const transactionFeeModule = appChain.protocol.resolve("TransactionFee");
199+
200+
const tx1 = await appChain.transaction(
201+
senderKey.toPublicKey(),
202+
async () => {
203+
await faucet.drip();
204+
},
205+
{ nonce: 0 }
206+
);
207+
208+
await tx1.sign();
209+
await tx1.send();
210+
211+
const tx2 = await appChain.transaction(
212+
senderKey.toPublicKey(),
213+
async () => {
214+
await testModule1.Method_1();
215+
},
216+
{ nonce: 4 }
217+
);
218+
219+
await tx2.sign();
220+
await tx2.send();
221+
const methodId2 = tx2.transaction?.methodId.toBigInt();
222+
expectDefined(methodId2);
223+
const transactionFeeConfig2 =
224+
transactionFeeModule.feeAnalyzer.getFeeConfig(methodId2);
225+
const transactionFee2 = transactionFeeModule.getFee(transactionFeeConfig2);
226+
expect(transactionFee2.toString()).toEqual("9");
227+
228+
const tx3 = await appChain.transaction(
229+
senderKey.toPublicKey(),
230+
async () => {
231+
await testModule2.Method_4();
232+
},
233+
{ nonce: 1 }
234+
);
235+
236+
await tx3.sign();
237+
await tx3.send();
238+
const methodId3 = tx3.transaction?.methodId.toBigInt();
239+
expectDefined(methodId3);
240+
const transactionFeeConfig3 =
241+
transactionFeeModule.feeAnalyzer.getFeeConfig(methodId3);
242+
const transactionFee3 = transactionFeeModule.getFee(transactionFeeConfig3);
243+
expect(transactionFee3.toString()).toEqual("7");
244+
245+
const tx4 = await appChain.transaction(
246+
senderKey.toPublicKey(),
247+
async () => {
248+
await testModule2.Method_7();
249+
},
250+
{ nonce: 2 }
251+
);
252+
253+
await tx4.sign();
254+
await tx4.send();
255+
256+
const methodId4 = tx4.transaction?.methodId.toBigInt();
257+
expectDefined(methodId4);
258+
const transactionFeeConfig4 =
259+
transactionFeeModule.feeAnalyzer.getFeeConfig(methodId4);
260+
const transactionFee4 = transactionFeeModule.getFee(transactionFeeConfig4);
261+
expect(transactionFee4.toString()).toEqual("6");
262+
263+
const tx5 = await appChain.transaction(
264+
senderKey.toPublicKey(),
265+
async () => {
266+
await testModule1.Method_10();
267+
},
268+
{ nonce: 3 }
269+
);
270+
271+
await tx5.sign();
272+
await tx5.send();
273+
274+
const methodId5 = tx5.transaction?.methodId.toBigInt();
275+
expectDefined(methodId5);
276+
const transactionFeeConfig5 =
277+
transactionFeeModule.feeAnalyzer.getFeeConfig(methodId5);
278+
const transactionFee5 = transactionFeeModule.getFee(transactionFeeConfig5);
279+
expect(transactionFee5.toString()).toEqual("8");
280+
281+
await appChain.produceBlock();
282+
283+
const senderBalance = await appChain.query.runtime.Balances.balances.get(
284+
new BalancesKey({
285+
tokenId: new TokenId(0),
286+
address: senderKey.toPublicKey(),
287+
})
288+
);
289+
290+
const feeRecipientBalance =
291+
await appChain.query.runtime.Balances.balances.get(
292+
new BalancesKey({
293+
tokenId: new TokenId(0),
294+
address: feeRecipientKey.toPublicKey(),
295+
})
296+
);
297+
298+
expectDefined(senderBalance);
299+
expect(senderBalance.toString()).toBe("970");
300+
301+
expectDefined(feeRecipientBalance);
302+
expect(feeRecipientBalance.toString()).toBe("30");
303+
});
304+
});

0 commit comments

Comments
 (0)