@@ -11,9 +11,9 @@ import { COMMON_DEPLOY_OPTIONS, findEntropyContract } from "./common";
1111
1212const parser = yargs ( hideBin ( process . argv ) )
1313 . usage (
14- "Requests a random number from an entropy contract and measures the\n" +
14+ "Requests random numbers from an entropy contract and measures the\n" +
1515 "latency between request submission and fulfillment by the Fortuna keeper service.\n" +
16- "Usage: $0 --private-key <private-key> --chain <chain-id> | --all-chains <testnet|mainnet>" ,
16+ "Usage: $0 --private-key <private-key> --chain <chain-id> | --all-chains <testnet|mainnet> --nrequests <number> --delay <delay> " ,
1717 )
1818 . options ( {
1919 chain : {
@@ -28,37 +28,50 @@ const parser = yargs(hideBin(process.argv))
2828 desc : "test latency for all entropy contracts deployed either on mainnet or testnet" ,
2929 } ,
3030 "private-key" : COMMON_DEPLOY_OPTIONS [ "private-key" ] ,
31+ "nrequests" : {
32+ type : "number" ,
33+ desc : "number of requests to make" ,
34+ default : 1 ,
35+ } ,
36+ "delay" : {
37+ type : "number" ,
38+ desc : "delay between requests in milliseconds" ,
39+ default : 25 ,
40+ } ,
3141 } ) ;
3242
33- async function testLatency (
43+ async function sendRequest (
3444 contract : EvmEntropyContract ,
3545 privateKey : PrivateKey ,
36- ) {
37- const provider = await contract . getDefaultProvider ( ) ;
46+ requestId : number ,
47+ ) : Promise < { entropyRequestResponse : any ; startTime : number } > {
48+ const fortunaProvider = await contract . getDefaultProvider ( ) ;
3849 const userRandomNumber = contract . generateUserRandomNumber ( ) ;
39- const requestResponse = await contract . requestRandomness (
50+ const entropyRequestResponse = await contract . requestRandomness (
4051 userRandomNumber ,
41- provider ,
52+ fortunaProvider ,
4253 privateKey ,
4354 true , // with callback
4455 ) ;
45- console . log ( `Request tx hash : ${ requestResponse . transactionHash } ` ) ;
46- // Read the sequence number for the request from the transaction events.
47- const sequenceNumber =
48- requestResponse . events . RequestedWithCallback . returnValues . sequenceNumber ;
49- console . log ( `sequence : ${ sequenceNumber } ` ) ;
50-
5156 const startTime = Date . now ( ) ;
57+ console . log ( `[Request ${ requestId } ] Request tx hash : ${ entropyRequestResponse . transactionHash } ` ) ;
58+ return { entropyRequestResponse, startTime } ;
59+ }
5260
53- const fromBlock = requestResponse . blockNumber ;
61+ async function waitForCallback (
62+ contract : EvmEntropyContract ,
63+ entropyRequestResponse : any ,
64+ startTime : number ,
65+ requestId : number ,
66+ ) : Promise < { success : boolean ; latency ?: number } > {
67+ const fromBlock = entropyRequestResponse . blockNumber ;
5468 const web3 = contract . chain . getWeb3 ( ) ;
5569 const entropyContract = contract . getContract ( ) ;
5670
5771 // eslint-disable-next-line no-constant-condition
5872 while ( true ) {
5973 await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
6074 const currentBlock = await web3 . eth . getBlockNumber ( ) ;
61-
6275 if ( fromBlock > currentBlock ) {
6376 continue ;
6477 }
@@ -69,25 +82,87 @@ async function testLatency(
6982 } ) ;
7083
7184 const event = events . find (
72- ( event ) => event . returnValues . request [ 1 ] == sequenceNumber ,
85+ ( event ) => event . returnValues . request [ 1 ] == entropyRequestResponse . events . RequestedWithCallback . returnValues . sequenceNumber ,
7386 ) ;
7487
7588 if ( event !== undefined ) {
76- console . log ( `Random number : ${ event . returnValues . randomNumber } ` ) ;
77- const endTime = Date . now ( ) ;
78- console . log ( `Fortuna Latency : ${ endTime - startTime } ms` ) ;
89+ console . log ( `[Request ${ requestId } ] Random number : ${ event . returnValues . randomNumber } ` ) ;
90+ const eventBlockTimestamp = Number ( await web3 . eth . getBlock ( event . blockNumber ) . then ( block => block . timestamp ) ) ;
91+ const entropyRequestBlockTimestamp = Number ( await web3 . eth . getBlock ( entropyRequestResponse . blockNumber ) . then ( block => block . timestamp ) ) ;
92+ const latency = eventBlockTimestamp - entropyRequestBlockTimestamp ;
93+ console . log ( `[Request ${ requestId } ] Fortuna Latency : ${ latency } ms` ) ;
7994 console . log (
80- `Revealed after : ${
81- currentBlock - requestResponse . blockNumber
95+ `[Request ${ requestId } ] Revealed after : ${
96+ event . blockNumber - entropyRequestResponse . blockNumber
8297 } blocks`,
8398 ) ;
84- break ;
99+ return { success : true , latency } ;
85100 }
86101 if ( Date . now ( ) - startTime > 60000 ) {
87- console . log ( "Timeout: 60s passed without the callback being called." ) ;
88- break ;
102+ console . log ( `[Request ${ requestId } ] Timeout: 60s passed without the callback being called.` ) ;
103+ return { success : false } ;
104+ }
105+ }
106+ }
107+
108+ async function testParallelLatency (
109+ contract : EvmEntropyContract ,
110+ privateKey : PrivateKey ,
111+ numRequests : number ,
112+ delay : number ,
113+ ) {
114+ console . log ( `Starting ${ numRequests } requests...` ) ;
115+
116+ // First send all requests
117+ const requests : { entropyRequestResponse : any ; startTime : number ; requestId : number } [ ] = [ ] ;
118+ for ( let i = 0 ; i < numRequests ; i ++ ) {
119+ if ( i > 0 ) {
120+ await new Promise ( resolve => setTimeout ( resolve , delay ) ) ;
89121 }
122+ const { entropyRequestResponse, startTime } = await sendRequest ( contract , privateKey , i + 1 ) ;
123+ requests . push ( { entropyRequestResponse, startTime, requestId : i + 1 } ) ;
124+ }
125+
126+
127+
128+
129+ // Then wait for all callbacks
130+ // The response time won't be accurate here.
131+ const results : { success : boolean ; latency ?: number } [ ] = [ ] ;
132+ for ( const request of requests ) {
133+ const sequenceNumber =
134+ request . entropyRequestResponse . events . RequestedWithCallback . returnValues . sequenceNumber ;
135+ console . log ( `[Request ${ request . requestId } ] sequence : ${ sequenceNumber } ` ) ;
136+ results . push ( await waitForCallback (
137+ contract ,
138+ request . entropyRequestResponse ,
139+ request . startTime ,
140+ request . requestId
141+ ) ) ;
142+ }
143+
144+ // Calculate statistics
145+ const successfulRequests = results . filter ( r => r . success ) . length ;
146+ const failedRequests = numRequests - successfulRequests ;
147+ const successRate = ( successfulRequests / numRequests ) * 100 ;
148+
149+ // Calculate average latency for successful requests
150+ const successfulLatencies = results
151+ . filter ( ( r ) : r is { success : true ; latency : number } => r . success && r . latency !== undefined )
152+ . map ( r => r . latency ) ;
153+ const avgLatency = successfulLatencies . length > 0
154+ ? successfulLatencies . reduce ( ( a , b ) => a + b , 0 ) / successfulLatencies . length
155+ : 0 ;
156+
157+ console . log ( "\n=== Test Results ===" ) ;
158+ console . log ( `Total Requests : ${ numRequests } ` ) ;
159+ console . log ( `Successful : ${ successfulRequests } ` ) ;
160+ console . log ( `Failed : ${ failedRequests } ` ) ;
161+ console . log ( `Success Rate : ${ successRate . toFixed ( 2 ) } %` ) ;
162+ if ( successfulLatencies . length > 0 ) {
163+ console . log ( `Average Latency : ${ avgLatency . toFixed ( 2 ) } ms` ) ;
90164 }
165+ console . log ( "===================" ) ;
91166}
92167
93168async function main ( ) {
@@ -103,13 +178,13 @@ async function main() {
103178 ( argv [ "all-chains" ] === "mainnet" )
104179 ) {
105180 console . log ( `Testing latency for ${ contract . getId ( ) } ...` ) ;
106- await testLatency ( contract , privateKey ) ;
181+ await testParallelLatency ( contract , privateKey , argv [ "nrequests" ] , argv [ "delay" ] ) ;
107182 }
108183 }
109184 } else if ( argv . chain ) {
110185 const chain = DefaultStore . getChainOrThrow ( argv . chain , EvmChain ) ;
111186 const contract = findEntropyContract ( chain ) ;
112- await testLatency ( contract , privateKey ) ;
187+ await testParallelLatency ( contract , privateKey , argv [ "nrequests" ] , argv [ "delay" ] ) ;
113188 }
114189}
115190
0 commit comments