Skip to content

Commit d507b3f

Browse files
authored
feat(fortuna): Add tooling for load testing entropy (#1643)
* feat(fortuna): Add tooling for load testing entropy
1 parent 85dba51 commit d507b3f

File tree

5 files changed

+165
-3
lines changed

5 files changed

+165
-3
lines changed

contract_manager/scripts/common.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const COMMON_DEPLOY_OPTIONS = {
7979
chain: {
8080
type: "array",
8181
demandOption: true,
82-
desc: "Chain to upload the contract on. Can be one of the chains available in the store",
82+
desc: "Chains to upload the contract on. Must be one of the chains available in the store",
8383
},
8484
"deployment-type": {
8585
type: "string",

contract_manager/scripts/deploy_evm_contract.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@ const parser = yargs(hideBin(process.argv))
1919
desc: "Path to the standard JSON output of the contract (build artifact)",
2020
},
2121
"private-key": COMMON_DEPLOY_OPTIONS["private-key"],
22-
chain: COMMON_DEPLOY_OPTIONS["chain"],
22+
chain: {
23+
type: "string",
24+
demandOption: true,
25+
desc: "Chain to upload the contract on. Must be one of the chains available in the store",
26+
},
2327
"deploy-args": {
2428
type: "array",
25-
desc: "Arguments to pass to the contract constructor. Each argument must begin with 0x if it's a hex string",
29+
desc: "Arguments to pass to the contract constructor. They should not be prefixed with 0x.",
2630
},
2731
});
2832

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import yargs from "yargs";
2+
import { hideBin } from "yargs/helpers";
3+
import {
4+
DefaultStore,
5+
EvmEntropyContract,
6+
PrivateKey,
7+
toPrivateKey,
8+
} from "../src";
9+
import {
10+
COMMON_DEPLOY_OPTIONS,
11+
findEntropyContract,
12+
findEvmChain,
13+
} from "./common";
14+
import Web3 from "web3";
15+
16+
const parser = yargs(hideBin(process.argv))
17+
.usage(
18+
"Load tests the entropy contract using the EntropyTester contract with many requests in a single transaction\n" +
19+
"it does not monitor whether the callbacks are actually submitted or not.\n" +
20+
"Usage: $0 --private-key <private-key> --chain <chain-id> --tester-address <tester-address>"
21+
)
22+
.options({
23+
chain: {
24+
type: "string",
25+
demandOption: true,
26+
desc: "test latency for the contract on this chain",
27+
},
28+
"tester-address": {
29+
type: "string",
30+
demandOption: true,
31+
desc: "Tester contract address",
32+
},
33+
"success-count": {
34+
type: "number",
35+
default: 100,
36+
desc: "How many successful requests to make",
37+
},
38+
"revert-count": {
39+
type: "number",
40+
default: 0,
41+
desc: "How many requests to make where the callback should revert",
42+
},
43+
"private-key": COMMON_DEPLOY_OPTIONS["private-key"],
44+
});
45+
46+
const ABI = [
47+
{
48+
inputs: [
49+
{
50+
internalType: "address",
51+
name: "provider",
52+
type: "address",
53+
},
54+
{
55+
internalType: "uint64",
56+
name: "success",
57+
type: "uint64",
58+
},
59+
{
60+
internalType: "uint64",
61+
name: "fail",
62+
type: "uint64",
63+
},
64+
],
65+
name: "batchRequests",
66+
outputs: [],
67+
stateMutability: "nonpayable",
68+
type: "function",
69+
},
70+
] as any;
71+
72+
async function main() {
73+
const argv = await parser.argv;
74+
const privateKey = toPrivateKey(argv.privateKey);
75+
const chain = findEvmChain(argv.chain);
76+
const contract = findEntropyContract(chain);
77+
const provider = await contract.getDefaultProvider();
78+
const fee = await contract.getFee(provider);
79+
const web3 = new Web3(contract.chain.getRpcUrl());
80+
const testerContract = new web3.eth.Contract(ABI, argv.testerAddress);
81+
const { address } = web3.eth.accounts.wallet.add(privateKey);
82+
const transactionObject = testerContract.methods.batchRequests(
83+
provider,
84+
argv.successCount,
85+
argv.revertCount
86+
);
87+
const totalCount = argv.successCount + argv.revertCount;
88+
const result = await contract.chain.estiamteAndSendTransaction(
89+
transactionObject,
90+
{
91+
from: address,
92+
value: (fee * totalCount).toString(),
93+
}
94+
);
95+
console.log("Submitted transaction ", result.transactionHash);
96+
}
97+
98+
main();

contract_manager/src/contracts/evm.ts

+5
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,11 @@ export class EvmEntropyContract extends Storable {
708708
return web3.utils.randomHex(32);
709709
}
710710

711+
async getFee(provider: string): Promise<number> {
712+
const contract = this.getContract();
713+
return await contract.methods.getFee(provider).call();
714+
}
715+
711716
async requestRandomness(
712717
userRandomNumber: string,
713718
provider: string,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: Apache 2
2+
3+
pragma solidity ^0.8.0;
4+
5+
import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
6+
import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
7+
8+
// Dummy contract for testing Fortuna service under heavy load
9+
// This contract will request many random numbers from the Entropy contract in a single transaction
10+
// It also reverts on some of the callbacks for testing the retry mechanism
11+
contract EntropyTester is IEntropyConsumer {
12+
IEntropy entropy;
13+
mapping(uint64 => bool) public shouldRevert;
14+
15+
constructor(address entropyAddress) {
16+
entropy = IEntropy(entropyAddress);
17+
}
18+
19+
function batchRequests(
20+
address provider,
21+
uint64 successCount,
22+
uint64 revertCount
23+
) public payable {
24+
uint128 fee = entropy.getFee(provider);
25+
bytes32 zero;
26+
for (uint64 i = 0; i < successCount; i++) {
27+
uint64 seqNum = entropy.requestWithCallback{value: fee}(
28+
provider,
29+
zero
30+
);
31+
shouldRevert[seqNum] = false;
32+
}
33+
for (uint64 i = 0; i < revertCount; i++) {
34+
uint64 seqNum = entropy.requestWithCallback{value: fee}(
35+
provider,
36+
zero
37+
);
38+
shouldRevert[seqNum] = true;
39+
}
40+
}
41+
42+
function getEntropy() internal view override returns (address) {
43+
return address(entropy);
44+
}
45+
46+
function entropyCallback(
47+
uint64 sequence,
48+
address, //provider
49+
bytes32 //randomNumber
50+
) internal view override {
51+
if (shouldRevert[sequence]) {
52+
revert("Reverting");
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)