-
Notifications
You must be signed in to change notification settings - Fork 261
chore(contract-manager) Entropy stress test script #2588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
import yargs from "yargs"; | ||
import { hideBin } from "yargs/helpers"; | ||
import { | ||
DefaultStore, | ||
EvmChain, | ||
EvmEntropyContract, | ||
PrivateKey, | ||
toPrivateKey, | ||
} from "../src"; | ||
import { COMMON_DEPLOY_OPTIONS, findEntropyContract } from "./common"; | ||
|
||
const parser = yargs(hideBin(process.argv)) | ||
.usage( | ||
"Requests random numbers from an entropy contract and measures the\n" + | ||
"latency between request submission and fulfillment by the Fortuna keeper service.\n" + | ||
"Usage: $0 --private-key <private-key> --chain <chain-id> | --all-chains <testnet|mainnet> --nrequests <number> --delay <delay>", | ||
) | ||
.options({ | ||
chain: { | ||
type: "string", | ||
desc: "test latency for the contract on this chain", | ||
conflicts: "all-chains", | ||
}, | ||
"all-chains": { | ||
type: "string", | ||
conflicts: "chain", | ||
choices: ["testnet", "mainnet"], | ||
desc: "test latency for all entropy contracts deployed either on mainnet or testnet", | ||
}, | ||
"private-key": COMMON_DEPLOY_OPTIONS["private-key"], | ||
"nrequests": { | ||
type: "number", | ||
desc: "number of requests to make", | ||
default: 1, | ||
}, | ||
"delay": { | ||
type: "number", | ||
desc: "delay between requests", | ||
default: 25, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems like a very specific number. any reason? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, just felt like it. 😅 |
||
}, | ||
}); | ||
|
||
async function sendRequest( | ||
contract: EvmEntropyContract, | ||
privateKey: PrivateKey, | ||
requestId: number, | ||
): Promise<{ EntropyRequestResponse: any; startTime: number }> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can't we use something better than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just the transaction turn object that we get from requestRandomness |
||
const fortunaProvider = await contract.getDefaultProvider(); | ||
const userRandomNumber = contract.generateUserRandomNumber(); | ||
const EntropyRequestResponse = await contract.requestRandomness( | ||
aditya520 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
userRandomNumber, | ||
fortunaProvider, | ||
privateKey, | ||
true, // with callback | ||
); | ||
const startTime = Date.now(); | ||
console.log(`[Request ${requestId}] Request tx hash : ${EntropyRequestResponse.transactionHash}`); | ||
return { EntropyRequestResponse: EntropyRequestResponse, startTime: startTime }; | ||
aditya520 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
async function waitForCallback( | ||
contract: EvmEntropyContract, | ||
EntropyRequestResponse: any, | ||
startTime: number, | ||
requestId: number, | ||
): Promise<{ success: boolean; latency?: number }> { | ||
const fromBlock = EntropyRequestResponse.blockNumber; | ||
const web3 = contract.chain.getWeb3(); | ||
const entropyContract = contract.getContract(); | ||
|
||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
await new Promise((resolve) => setTimeout(resolve, 1000)); | ||
aditya520 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const currentBlock = await web3.eth.getBlockNumber(); | ||
if (fromBlock > currentBlock) { | ||
continue; | ||
} | ||
|
||
const events = await entropyContract.getPastEvents("RevealedWithCallback", { | ||
fromBlock: fromBlock, | ||
toBlock: currentBlock, | ||
}); | ||
|
||
const event = events.find( | ||
(event) => event.returnValues.request[1] == EntropyRequestResponse.events.RequestedWithCallback.returnValues.sequenceNumber, | ||
); | ||
|
||
if (event !== undefined) { | ||
console.log(`[Request ${requestId}] Random number : ${event.returnValues.randomNumber}`); | ||
const eventBlockTimestamp = Number(await web3.eth.getBlock(event.blockNumber).then(block => block.timestamp)); | ||
const entropyRequestBlockTimestamp = Number(await web3.eth.getBlock(EntropyRequestResponse.blockNumber).then(block => block.timestamp)); | ||
const latency = eventBlockTimestamp - entropyRequestBlockTimestamp; | ||
console.log(`[Request ${requestId}] Fortuna Latency : ${latency}ms`); | ||
console.log( | ||
`[Request ${requestId}] Revealed after : ${ | ||
event.blockNumber - EntropyRequestResponse.blockNumber | ||
} blocks`, | ||
); | ||
return { success: true, latency }; | ||
} | ||
if (Date.now() - startTime > 60000) { | ||
console.log(`[Request ${requestId}] Timeout: 60s passed without the callback being called.`); | ||
return { success: false }; | ||
} | ||
} | ||
} | ||
|
||
async function testParallelLatency( | ||
contract: EvmEntropyContract, | ||
privateKey: PrivateKey, | ||
numRequests: number, | ||
delay: number, | ||
) { | ||
console.log(`Starting ${numRequests} requests...`); | ||
|
||
// First send all requests | ||
const requests: { EntropyRequestResponse: any; startTime: number; requestId: number }[] = []; | ||
for (let i = 0; i < numRequests; i++) { | ||
if (i > 0) { | ||
await new Promise(resolve => setTimeout(resolve, delay)); | ||
} | ||
const { EntropyRequestResponse, startTime } = await sendRequest(contract, privateKey, i + 1); | ||
requests.push({ EntropyRequestResponse, startTime, requestId: i + 1 }); | ||
} | ||
|
||
|
||
|
||
|
||
// Then wait for all callbacks | ||
// The response time won't be accurate here. | ||
const results: { success: boolean; latency?: number }[] = []; | ||
for (const request of requests) { | ||
const sequenceNumber = | ||
request.EntropyRequestResponse.events.RequestedWithCallback.returnValues.sequenceNumber; | ||
console.log(`[Request ${request.requestId}] sequence : ${sequenceNumber}`); | ||
results.push(await waitForCallback( | ||
contract, | ||
request.EntropyRequestResponse, | ||
request.startTime, | ||
request.requestId | ||
)); | ||
} | ||
|
||
// Calculate statistics | ||
const successfulRequests = results.filter(r => r.success).length; | ||
const failedRequests = numRequests - successfulRequests; | ||
const successRate = (successfulRequests / numRequests) * 100; | ||
|
||
// Calculate average latency for successful requests | ||
const successfulLatencies = results | ||
.filter((r): r is { success: true; latency: number } => r.success && r.latency !== undefined) | ||
.map(r => r.latency); | ||
const avgLatency = successfulLatencies.length > 0 | ||
? successfulLatencies.reduce((a, b) => a + b, 0) / successfulLatencies.length | ||
: 0; | ||
|
||
console.log("\n=== Test Results ==="); | ||
console.log(`Total Requests : ${numRequests}`); | ||
console.log(`Successful : ${successfulRequests}`); | ||
console.log(`Failed : ${failedRequests}`); | ||
console.log(`Success Rate : ${successRate.toFixed(2)}%`); | ||
if (successfulLatencies.length > 0) { | ||
console.log(`Average Latency : ${avgLatency.toFixed(2)}ms`); | ||
} | ||
console.log("==================="); | ||
} | ||
|
||
async function main() { | ||
const argv = await parser.argv; | ||
if (!argv.chain && !argv["all-chains"]) { | ||
throw new Error("Must specify either --chain or --all-chains"); | ||
} | ||
const privateKey = toPrivateKey(argv.privateKey); | ||
if (argv["all-chains"]) { | ||
for (const contract of Object.values(DefaultStore.entropy_contracts)) { | ||
if ( | ||
contract.getChain().isMainnet() === | ||
(argv["all-chains"] === "mainnet") | ||
) { | ||
console.log(`Testing latency for ${contract.getId()}...`); | ||
await testParallelLatency(contract, privateKey, argv["nrequests"], argv["delay"]); | ||
} | ||
} | ||
} else if (argv.chain) { | ||
const chain = DefaultStore.getChainOrThrow(argv.chain, EvmChain); | ||
const contract = findEntropyContract(chain); | ||
await testParallelLatency(contract, privateKey, argv["nrequests"], argv["delay"]); | ||
} | ||
} | ||
|
||
main(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mention that it is in seconds
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ms