Explorar o código

feat(fortuna): Add tooling for load testing entropy (#1643)

* feat(fortuna): Add tooling for load testing entropy
Amin Moghaddam hai 1 ano
pai
achega
d507b3f20e

+ 1 - 1
contract_manager/scripts/common.ts

@@ -79,7 +79,7 @@ export const COMMON_DEPLOY_OPTIONS = {
   chain: {
     type: "array",
     demandOption: true,
-    desc: "Chain to upload the contract on. Can be one of the chains available in the store",
+    desc: "Chains to upload the contract on. Must be one of the chains available in the store",
   },
   "deployment-type": {
     type: "string",

+ 6 - 2
contract_manager/scripts/deploy_evm_contract.ts

@@ -19,10 +19,14 @@ const parser = yargs(hideBin(process.argv))
       desc: "Path to the standard JSON output of the contract (build artifact)",
     },
     "private-key": COMMON_DEPLOY_OPTIONS["private-key"],
-    chain: COMMON_DEPLOY_OPTIONS["chain"],
+    chain: {
+      type: "string",
+      demandOption: true,
+      desc: "Chain to upload the contract on. Must be one of the chains available in the store",
+    },
     "deploy-args": {
       type: "array",
-      desc: "Arguments to pass to the contract constructor. Each argument must begin with 0x if it's a hex string",
+      desc: "Arguments to pass to the contract constructor. They should not be prefixed with 0x.",
     },
   });
 

+ 98 - 0
contract_manager/scripts/load_test_entropy.ts

@@ -0,0 +1,98 @@
+import yargs from "yargs";
+import { hideBin } from "yargs/helpers";
+import {
+  DefaultStore,
+  EvmEntropyContract,
+  PrivateKey,
+  toPrivateKey,
+} from "../src";
+import {
+  COMMON_DEPLOY_OPTIONS,
+  findEntropyContract,
+  findEvmChain,
+} from "./common";
+import Web3 from "web3";
+
+const parser = yargs(hideBin(process.argv))
+  .usage(
+    "Load tests the entropy contract using the EntropyTester contract with many requests in a single transaction\n" +
+      "it does not monitor whether the callbacks are actually submitted or not.\n" +
+      "Usage: $0 --private-key <private-key> --chain <chain-id> --tester-address <tester-address>"
+  )
+  .options({
+    chain: {
+      type: "string",
+      demandOption: true,
+      desc: "test latency for the contract on this chain",
+    },
+    "tester-address": {
+      type: "string",
+      demandOption: true,
+      desc: "Tester contract address",
+    },
+    "success-count": {
+      type: "number",
+      default: 100,
+      desc: "How many successful requests to make",
+    },
+    "revert-count": {
+      type: "number",
+      default: 0,
+      desc: "How many requests to make where the callback should revert",
+    },
+    "private-key": COMMON_DEPLOY_OPTIONS["private-key"],
+  });
+
+const ABI = [
+  {
+    inputs: [
+      {
+        internalType: "address",
+        name: "provider",
+        type: "address",
+      },
+      {
+        internalType: "uint64",
+        name: "success",
+        type: "uint64",
+      },
+      {
+        internalType: "uint64",
+        name: "fail",
+        type: "uint64",
+      },
+    ],
+    name: "batchRequests",
+    outputs: [],
+    stateMutability: "nonpayable",
+    type: "function",
+  },
+] as any;
+
+async function main() {
+  const argv = await parser.argv;
+  const privateKey = toPrivateKey(argv.privateKey);
+  const chain = findEvmChain(argv.chain);
+  const contract = findEntropyContract(chain);
+  const provider = await contract.getDefaultProvider();
+  const fee = await contract.getFee(provider);
+  const web3 = new Web3(contract.chain.getRpcUrl());
+  const testerContract = new web3.eth.Contract(ABI, argv.testerAddress);
+  const { address } = web3.eth.accounts.wallet.add(privateKey);
+  const transactionObject = testerContract.methods.batchRequests(
+    provider,
+    argv.successCount,
+    argv.revertCount
+  );
+  const totalCount = argv.successCount + argv.revertCount;
+  const result = await contract.chain.estiamteAndSendTransaction(
+    transactionObject,
+    {
+      from: address,
+      value: (fee * totalCount).toString(),
+    }
+  );
+  console.log("Submitted transaction ", result.transactionHash);
+}
+
+main();

+ 5 - 0
contract_manager/src/contracts/evm.ts

@@ -708,6 +708,11 @@ export class EvmEntropyContract extends Storable {
     return web3.utils.randomHex(32);
   }
 
+  async getFee(provider: string): Promise<number> {
+    const contract = this.getContract();
+    return await contract.methods.getFee(provider).call();
+  }
+
   async requestRandomness(
     userRandomNumber: string,
     provider: string,

+ 55 - 0
target_chains/ethereum/contracts/contracts/entropy/EntropyTester.sol

@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: Apache 2
+
+pragma solidity ^0.8.0;
+
+import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
+import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
+
+// Dummy contract for testing Fortuna service under heavy load
+// This contract will request many random numbers from the Entropy contract in a single transaction
+// It also reverts on some of the callbacks for testing the retry mechanism
+contract EntropyTester is IEntropyConsumer {
+    IEntropy entropy;
+    mapping(uint64 => bool) public shouldRevert;
+
+    constructor(address entropyAddress) {
+        entropy = IEntropy(entropyAddress);
+    }
+
+    function batchRequests(
+        address provider,
+        uint64 successCount,
+        uint64 revertCount
+    ) public payable {
+        uint128 fee = entropy.getFee(provider);
+        bytes32 zero;
+        for (uint64 i = 0; i < successCount; i++) {
+            uint64 seqNum = entropy.requestWithCallback{value: fee}(
+                provider,
+                zero
+            );
+            shouldRevert[seqNum] = false;
+        }
+        for (uint64 i = 0; i < revertCount; i++) {
+            uint64 seqNum = entropy.requestWithCallback{value: fee}(
+                provider,
+                zero
+            );
+            shouldRevert[seqNum] = true;
+        }
+    }
+
+    function getEntropy() internal view override returns (address) {
+        return address(entropy);
+    }
+
+    function entropyCallback(
+        uint64 sequence,
+        address, //provider
+        bytes32 //randomNumber
+    ) internal view override {
+        if (shouldRevert[sequence]) {
+            revert("Reverting");
+        }
+    }
+}