signing-performance.test.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /**
  2. * Performance Test for Signing Operations
  3. *
  4. * Validates that signing operations meet the <50ms performance requirement
  5. * across all platforms and under various load conditions.
  6. */
  7. import { CredentialManager } from '@/core/credential-manager/CredentialManager'
  8. import { UnifiedSigner } from '@/core/credential-manager/UnifiedSigner'
  9. import { Platform } from '@/types/credential'
  10. describe('Signing Performance Test', () => {
  11. let credentialManager: CredentialManager
  12. let unifiedSigner: UnifiedSigner
  13. beforeEach(async () => {
  14. const config = {
  15. accounts: [
  16. {
  17. id: 'perf-pacifica',
  18. name: 'Performance Test Pacifica',
  19. platform: Platform.PACIFICA,
  20. enabled: true,
  21. credentials: {
  22. type: 'ed25519' as const,
  23. privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
  24. }
  25. },
  26. {
  27. id: 'perf-aster',
  28. name: 'Performance Test Aster',
  29. platform: Platform.ASTER,
  30. enabled: true,
  31. credentials: {
  32. type: 'secp256k1' as const,
  33. privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
  34. }
  35. },
  36. {
  37. id: 'perf-binance',
  38. name: 'Performance Test Binance',
  39. platform: Platform.BINANCE,
  40. enabled: true,
  41. credentials: {
  42. type: 'hmac' as const,
  43. apiKey: 'test-api-key',
  44. secretKey: 'test-secret-key'
  45. }
  46. }
  47. ]
  48. }
  49. credentialManager = new CredentialManager()
  50. unifiedSigner = new UnifiedSigner()
  51. await credentialManager.loadConfiguration(config)
  52. unifiedSigner.setAccountGetter(async (accountId: string) => {
  53. return credentialManager.getAccount(accountId)
  54. })
  55. })
  56. afterEach(async () => {
  57. await credentialManager.shutdown()
  58. })
  59. describe('Single Operation Performance', () => {
  60. test('Pacifica signing should complete within 50ms', async () => {
  61. const message = new TextEncoder().encode('performance test message')
  62. const startTime = performance.now()
  63. const result = await credentialManager.sign('perf-pacifica', message)
  64. const duration = performance.now() - startTime
  65. expect(result.success).toBe(true)
  66. expect(duration).toBeLessThan(50)
  67. expect(result.metadata?.duration).toBeLessThan(50)
  68. })
  69. test('Aster signing should complete within 50ms', async () => {
  70. const message = new TextEncoder().encode('performance test message')
  71. const startTime = performance.now()
  72. const result = await credentialManager.sign('perf-aster', message)
  73. const duration = performance.now() - startTime
  74. expect(result.success).toBe(true)
  75. expect(duration).toBeLessThan(50)
  76. expect(result.metadata?.duration).toBeLessThan(50)
  77. })
  78. test('Binance signing should complete within 50ms', async () => {
  79. const message = new TextEncoder().encode('performance test message')
  80. const startTime = performance.now()
  81. const result = await credentialManager.sign('perf-binance', message)
  82. const duration = performance.now() - startTime
  83. expect(result.success).toBe(true)
  84. expect(duration).toBeLessThan(50)
  85. expect(result.metadata?.duration).toBeLessThan(50)
  86. })
  87. test('UnifiedSigner should meet performance requirements', async () => {
  88. const message = new TextEncoder().encode('unified signer test')
  89. const platforms = [
  90. { accountId: 'perf-pacifica', platform: Platform.PACIFICA },
  91. { accountId: 'perf-aster', platform: Platform.ASTER },
  92. { accountId: 'perf-binance', platform: Platform.BINANCE }
  93. ]
  94. for (const { accountId, platform } of platforms) {
  95. const startTime = performance.now()
  96. const result = await unifiedSigner.sign(accountId, message, {
  97. forcePlatform: platform,
  98. collectMetrics: true
  99. })
  100. const duration = performance.now() - startTime
  101. expect(result.success).toBe(true)
  102. expect(duration).toBeLessThan(50)
  103. expect(result.metadata?.duration).toBeLessThan(50)
  104. }
  105. })
  106. })
  107. describe('Batch Operation Performance', () => {
  108. test('should handle 10 concurrent signing operations within performance limits', async () => {
  109. const message = new TextEncoder().encode('concurrent test message')
  110. const operations = Array.from({ length: 10 }, (_, i) => ({
  111. accountId: ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3],
  112. message
  113. }))
  114. const startTime = performance.now()
  115. const results = await Promise.all(
  116. operations.map(op => credentialManager.sign(op.accountId, op.message))
  117. )
  118. const totalDuration = performance.now() - startTime
  119. expect(results).toHaveLength(10)
  120. expect(results.every(r => r.success)).toBe(true)
  121. // Total time for 10 concurrent operations should be reasonable
  122. expect(totalDuration).toBeLessThan(200)
  123. // Average time per operation
  124. const averageTime = totalDuration / 10
  125. expect(averageTime).toBeLessThan(20)
  126. // Each individual operation should meet requirement
  127. results.forEach(result => {
  128. expect(result.metadata?.duration).toBeLessThan(50)
  129. })
  130. })
  131. test('should handle batch signing with UnifiedSigner', async () => {
  132. const message = new TextEncoder().encode('batch test message')
  133. const batchRequests = [
  134. { accountId: 'perf-pacifica', message },
  135. { accountId: 'perf-aster', message },
  136. { accountId: 'perf-binance', message },
  137. { accountId: 'perf-pacifica', message },
  138. { accountId: 'perf-aster', message }
  139. ]
  140. const startTime = performance.now()
  141. const results = await unifiedSigner.signBatch(batchRequests)
  142. const totalDuration = performance.now() - startTime
  143. expect(results).toHaveLength(5)
  144. expect(results.every(r => r.success)).toBe(true)
  145. // Batch should complete efficiently
  146. expect(totalDuration).toBeLessThan(150)
  147. // Results should be in original order
  148. expect(results[0].accountId).toBe('perf-pacifica')
  149. expect(results[1].accountId).toBe('perf-aster')
  150. expect(results[2].accountId).toBe('perf-binance')
  151. })
  152. })
  153. describe('High-Load Performance', () => {
  154. test('should maintain performance under high load', async () => {
  155. const message = new TextEncoder().encode('high load test')
  156. const operationCount = 50
  157. const operations = Array.from({ length: operationCount }, (_, i) => ({
  158. accountId: ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3],
  159. message
  160. }))
  161. const startTime = performance.now()
  162. const results = await Promise.all(
  163. operations.map(op => credentialManager.sign(op.accountId, op.message))
  164. )
  165. const totalDuration = performance.now() - startTime
  166. expect(results).toHaveLength(operationCount)
  167. expect(results.every(r => r.success)).toBe(true)
  168. // Should handle 50 operations efficiently
  169. expect(totalDuration).toBeLessThan(1000) // 1 second for 50 operations
  170. // Calculate percentiles
  171. const durations = results
  172. .map(r => r.metadata?.duration || 0)
  173. .sort((a, b) => a - b)
  174. const p50 = durations[Math.floor(durations.length * 0.5)]
  175. const p95 = durations[Math.floor(durations.length * 0.95)]
  176. const p99 = durations[Math.floor(durations.length * 0.99)]
  177. expect(p50).toBeLessThan(30) // 50th percentile < 30ms
  178. expect(p95).toBeLessThan(50) // 95th percentile < 50ms (requirement)
  179. expect(p99).toBeLessThan(75) // 99th percentile < 75ms (allows for outliers)
  180. })
  181. test('should handle sustained load without performance degradation', async () => {
  182. const message = new TextEncoder().encode('sustained load test')
  183. const batchSize = 10
  184. const batchCount = 5
  185. const batchResults: number[] = []
  186. for (let batch = 0; batch < batchCount; batch++) {
  187. const operations = Array.from({ length: batchSize }, (_, i) => ({
  188. accountId: ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3],
  189. message
  190. }))
  191. const batchStartTime = performance.now()
  192. const results = await Promise.all(
  193. operations.map(op => credentialManager.sign(op.accountId, op.message))
  194. )
  195. const batchDuration = performance.now() - batchStartTime
  196. expect(results.every(r => r.success)).toBe(true)
  197. batchResults.push(batchDuration)
  198. // Small delay between batches
  199. await new Promise(resolve => setTimeout(resolve, 10))
  200. }
  201. // Performance should not degrade significantly across batches
  202. const firstBatch = batchResults[0]
  203. const lastBatch = batchResults[batchResults.length - 1]
  204. // Last batch should not be more than 50% slower than first batch
  205. expect(lastBatch).toBeLessThan(firstBatch * 1.5)
  206. // All batches should complete within reasonable time
  207. batchResults.forEach(duration => {
  208. expect(duration).toBeLessThan(200) // 10 operations in 200ms
  209. })
  210. })
  211. })
  212. describe('Memory and Resource Performance', () => {
  213. test('should not leak memory during repeated operations', async () => {
  214. const message = new TextEncoder().encode('memory test')
  215. const initialMemory = process.memoryUsage()
  216. // Perform many signing operations
  217. for (let i = 0; i < 100; i++) {
  218. const accountId = ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3]
  219. const result = await credentialManager.sign(accountId, message)
  220. expect(result.success).toBe(true)
  221. }
  222. // Force garbage collection if available
  223. if (global.gc) {
  224. global.gc()
  225. await new Promise(resolve => setTimeout(resolve, 100))
  226. }
  227. const finalMemory = process.memoryUsage()
  228. // Memory increase should be reasonable (less than 10MB)
  229. const heapIncrease = finalMemory.heapUsed - initialMemory.heapUsed
  230. expect(heapIncrease).toBeLessThan(10 * 1024 * 1024) // 10MB
  231. })
  232. test('should maintain consistent performance across different message sizes', async () => {
  233. const messageSizes = [100, 1000, 10000, 100000] // bytes
  234. const performanceResults: Record<number, number[]> = {}
  235. for (const size of messageSizes) {
  236. const message = new TextEncoder().encode('x'.repeat(size))
  237. const durations: number[] = []
  238. // Test each size multiple times
  239. for (let i = 0; i < 10; i++) {
  240. const startTime = performance.now()
  241. const result = await credentialManager.sign('perf-pacifica', message)
  242. const duration = performance.now() - startTime
  243. expect(result.success).toBe(true)
  244. durations.push(duration)
  245. }
  246. performanceResults[size] = durations
  247. }
  248. // All message sizes should meet performance requirements
  249. Object.entries(performanceResults).forEach(([size, durations]) => {
  250. const averageDuration = durations.reduce((a, b) => a + b, 0) / durations.length
  251. const maxDuration = Math.max(...durations)
  252. expect(averageDuration).toBeLessThan(50) // Average < 50ms
  253. expect(maxDuration).toBeLessThan(100) // Max < 100ms (allowing for larger messages)
  254. })
  255. // Performance should scale reasonably with message size
  256. const avg100 = performanceResults[100].reduce((a, b) => a + b, 0) / 10
  257. const avg100k = performanceResults[100000].reduce((a, b) => a + b, 0) / 10
  258. // 1000x larger message should not be more than 10x slower
  259. expect(avg100k).toBeLessThan(avg100 * 10)
  260. })
  261. })
  262. describe('Platform-Specific Performance', () => {
  263. test('Ed25519 (Pacifica) should be fastest', async () => {
  264. const message = new TextEncoder().encode('ed25519 performance test')
  265. const iterations = 20
  266. const durations: number[] = []
  267. for (let i = 0; i < iterations; i++) {
  268. const startTime = performance.now()
  269. const result = await credentialManager.sign('perf-pacifica', message)
  270. const duration = performance.now() - startTime
  271. expect(result.success).toBe(true)
  272. durations.push(duration)
  273. }
  274. const averageDuration = durations.reduce((a, b) => a + b, 0) / iterations
  275. const maxDuration = Math.max(...durations)
  276. // Ed25519 should be very fast
  277. expect(averageDuration).toBeLessThan(20)
  278. expect(maxDuration).toBeLessThan(50)
  279. })
  280. test('ECDSA (Aster) should meet performance requirements', async () => {
  281. const message = new TextEncoder().encode('ecdsa performance test')
  282. const iterations = 20
  283. const durations: number[] = []
  284. for (let i = 0; i < iterations; i++) {
  285. const startTime = performance.now()
  286. const result = await credentialManager.sign('perf-aster', message)
  287. const duration = performance.now() - startTime
  288. expect(result.success).toBe(true)
  289. durations.push(duration)
  290. }
  291. const averageDuration = durations.reduce((a, b) => a + b, 0) / iterations
  292. const maxDuration = Math.max(...durations)
  293. // ECDSA should meet requirements
  294. expect(averageDuration).toBeLessThan(40)
  295. expect(maxDuration).toBeLessThan(50)
  296. })
  297. test('HMAC (Binance) should be very fast', async () => {
  298. const message = new TextEncoder().encode('hmac performance test')
  299. const iterations = 20
  300. const durations: number[] = []
  301. for (let i = 0; i < iterations; i++) {
  302. const startTime = performance.now()
  303. const result = await credentialManager.sign('perf-binance', message)
  304. const duration = performance.now() - startTime
  305. expect(result.success).toBe(true)
  306. durations.push(duration)
  307. }
  308. const averageDuration = durations.reduce((a, b) => a + b, 0) / iterations
  309. const maxDuration = Math.max(...durations)
  310. // HMAC should be fastest
  311. expect(averageDuration).toBeLessThan(10)
  312. expect(maxDuration).toBeLessThan(25)
  313. })
  314. })
  315. describe('Performance Metrics Collection', () => {
  316. test('should collect accurate performance metrics', async () => {
  317. const message = new TextEncoder().encode('metrics test')
  318. // Reset metrics
  319. unifiedSigner.resetMetrics()
  320. // Perform signing operations
  321. await unifiedSigner.sign('perf-pacifica', message, { collectMetrics: true })
  322. await unifiedSigner.sign('perf-aster', message, { collectMetrics: true })
  323. await unifiedSigner.sign('perf-binance', message, { collectMetrics: true })
  324. const metrics = unifiedSigner.getMetrics()
  325. expect(metrics.totalOperations).toBe(3)
  326. expect(metrics.successfulOperations).toBe(3)
  327. expect(metrics.failedOperations).toBe(0)
  328. expect(metrics.averageSigningTime).toBeGreaterThan(0)
  329. expect(metrics.averageSigningTime).toBeLessThan(50)
  330. // Platform-specific metrics
  331. expect(metrics.platformBreakdown[Platform.PACIFICA].operations).toBe(1)
  332. expect(metrics.platformBreakdown[Platform.ASTER].operations).toBe(1)
  333. expect(metrics.platformBreakdown[Platform.BINANCE].operations).toBe(1)
  334. expect(metrics.platformBreakdown[Platform.PACIFICA].successRate).toBe(1)
  335. expect(metrics.platformBreakdown[Platform.ASTER].successRate).toBe(1)
  336. expect(metrics.platformBreakdown[Platform.BINANCE].successRate).toBe(1)
  337. })
  338. test('should track performance degradation', async () => {
  339. const message = new TextEncoder().encode('degradation test')
  340. unifiedSigner.resetMetrics()
  341. // Perform operations and check if performance remains consistent
  342. const operationCount = 50
  343. const checkpoints = [10, 25, 50]
  344. const performanceAtCheckpoints: number[] = []
  345. for (let i = 1; i <= operationCount; i++) {
  346. const accountId = ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3]
  347. await unifiedSigner.sign(accountId, message, { collectMetrics: true })
  348. if (checkpoints.includes(i)) {
  349. const metrics = unifiedSigner.getMetrics()
  350. performanceAtCheckpoints.push(metrics.averageSigningTime)
  351. }
  352. }
  353. // Performance should not degrade significantly
  354. const [perf10, perf25, perf50] = performanceAtCheckpoints
  355. expect(perf10).toBeLessThan(50)
  356. expect(perf25).toBeLessThan(50)
  357. expect(perf50).toBeLessThan(50)
  358. // Performance at 50 operations should not be more than 50% worse than at 10
  359. expect(perf50).toBeLessThan(perf10 * 1.5)
  360. })
  361. })
  362. })