@agirails/sdk 2.0.1-beta → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +116 -108
  3. package/bin/actp +10 -0
  4. package/dist/ACTPClient.d.ts +456 -33
  5. package/dist/ACTPClient.d.ts.map +1 -1
  6. package/dist/ACTPClient.js +477 -93
  7. package/dist/ACTPClient.js.map +1 -1
  8. package/dist/abi/AgentRegistry.json +782 -0
  9. package/dist/abi/EscrowVault.json +106 -38
  10. package/dist/abi/IdentityRegistry.json +316 -0
  11. package/dist/adapters/BaseAdapter.d.ts +231 -0
  12. package/dist/adapters/BaseAdapter.d.ts.map +1 -0
  13. package/dist/adapters/BaseAdapter.js +393 -0
  14. package/dist/adapters/BaseAdapter.js.map +1 -0
  15. package/dist/adapters/BeginnerAdapter.d.ts +152 -0
  16. package/dist/adapters/BeginnerAdapter.d.ts.map +1 -0
  17. package/dist/adapters/BeginnerAdapter.js +168 -0
  18. package/dist/adapters/BeginnerAdapter.js.map +1 -0
  19. package/dist/adapters/IntermediateAdapter.d.ts +211 -0
  20. package/dist/adapters/IntermediateAdapter.d.ts.map +1 -0
  21. package/dist/adapters/IntermediateAdapter.js +260 -0
  22. package/dist/adapters/IntermediateAdapter.js.map +1 -0
  23. package/dist/adapters/index.d.ts +15 -0
  24. package/dist/adapters/index.d.ts.map +1 -0
  25. package/dist/adapters/index.js +26 -0
  26. package/dist/adapters/index.js.map +1 -0
  27. package/dist/builders/DeliveryProofBuilder.d.ts +60 -1
  28. package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -1
  29. package/dist/builders/DeliveryProofBuilder.js +81 -5
  30. package/dist/builders/DeliveryProofBuilder.js.map +1 -1
  31. package/dist/builders/QuoteBuilder.d.ts +101 -0
  32. package/dist/builders/QuoteBuilder.d.ts.map +1 -1
  33. package/dist/builders/QuoteBuilder.js +120 -3
  34. package/dist/builders/QuoteBuilder.js.map +1 -1
  35. package/dist/builders/index.d.ts +4 -0
  36. package/dist/builders/index.d.ts.map +1 -1
  37. package/dist/builders/index.js +4 -0
  38. package/dist/builders/index.js.map +1 -1
  39. package/dist/cli/commands/balance.d.ts +13 -0
  40. package/dist/cli/commands/balance.d.ts.map +1 -0
  41. package/dist/cli/commands/balance.js +89 -0
  42. package/dist/cli/commands/balance.js.map +1 -0
  43. package/dist/cli/commands/batch.d.ts +24 -0
  44. package/dist/cli/commands/batch.d.ts.map +1 -0
  45. package/dist/cli/commands/batch.js +424 -0
  46. package/dist/cli/commands/batch.js.map +1 -0
  47. package/dist/cli/commands/config.d.ts +13 -0
  48. package/dist/cli/commands/config.d.ts.map +1 -0
  49. package/dist/cli/commands/config.js +192 -0
  50. package/dist/cli/commands/config.js.map +1 -0
  51. package/dist/cli/commands/init.d.ts +19 -0
  52. package/dist/cli/commands/init.d.ts.map +1 -0
  53. package/dist/cli/commands/init.js +143 -0
  54. package/dist/cli/commands/init.js.map +1 -0
  55. package/dist/cli/commands/mint.d.ts +13 -0
  56. package/dist/cli/commands/mint.d.ts.map +1 -0
  57. package/dist/cli/commands/mint.js +91 -0
  58. package/dist/cli/commands/mint.js.map +1 -0
  59. package/dist/cli/commands/pay.d.ts +18 -0
  60. package/dist/cli/commands/pay.d.ts.map +1 -0
  61. package/dist/cli/commands/pay.js +87 -0
  62. package/dist/cli/commands/pay.js.map +1 -0
  63. package/dist/cli/commands/simulate.d.ts +32 -0
  64. package/dist/cli/commands/simulate.d.ts.map +1 -0
  65. package/dist/cli/commands/simulate.js +290 -0
  66. package/dist/cli/commands/simulate.js.map +1 -0
  67. package/dist/cli/commands/time.d.ts +29 -0
  68. package/dist/cli/commands/time.d.ts.map +1 -0
  69. package/dist/cli/commands/time.js +252 -0
  70. package/dist/cli/commands/time.js.map +1 -0
  71. package/dist/cli/commands/tx.d.ts +16 -0
  72. package/dist/cli/commands/tx.d.ts.map +1 -0
  73. package/dist/cli/commands/tx.js +379 -0
  74. package/dist/cli/commands/tx.js.map +1 -0
  75. package/dist/cli/commands/watch.d.ts +20 -0
  76. package/dist/cli/commands/watch.d.ts.map +1 -0
  77. package/dist/cli/commands/watch.js +160 -0
  78. package/dist/cli/commands/watch.js.map +1 -0
  79. package/dist/cli/index.d.ts +17 -0
  80. package/dist/cli/index.d.ts.map +1 -0
  81. package/dist/cli/index.js +104 -0
  82. package/dist/cli/index.js.map +1 -0
  83. package/dist/cli/utils/client.d.ts +70 -0
  84. package/dist/cli/utils/client.d.ts.map +1 -0
  85. package/dist/cli/utils/client.js +240 -0
  86. package/dist/cli/utils/client.js.map +1 -0
  87. package/dist/cli/utils/config.d.ts +91 -0
  88. package/dist/cli/utils/config.d.ts.map +1 -0
  89. package/dist/cli/utils/config.js +240 -0
  90. package/dist/cli/utils/config.js.map +1 -0
  91. package/dist/cli/utils/output.d.ts +174 -0
  92. package/dist/cli/utils/output.d.ts.map +1 -0
  93. package/dist/cli/utils/output.js +380 -0
  94. package/dist/cli/utils/output.js.map +1 -0
  95. package/dist/config/networks.d.ts +28 -0
  96. package/dist/config/networks.d.ts.map +1 -1
  97. package/dist/config/networks.js +60 -12
  98. package/dist/config/networks.js.map +1 -1
  99. package/dist/errors/index.d.ts +165 -2
  100. package/dist/errors/index.d.ts.map +1 -1
  101. package/dist/errors/index.js +260 -2
  102. package/dist/errors/index.js.map +1 -1
  103. package/dist/index.d.ts +61 -13
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +141 -36
  106. package/dist/index.js.map +1 -1
  107. package/dist/level0/Provider.d.ts +106 -0
  108. package/dist/level0/Provider.d.ts.map +1 -0
  109. package/dist/level0/Provider.js +10 -0
  110. package/dist/level0/Provider.js.map +1 -0
  111. package/dist/level0/ServiceDirectory.d.ts +74 -0
  112. package/dist/level0/ServiceDirectory.d.ts.map +1 -0
  113. package/dist/level0/ServiceDirectory.js +122 -0
  114. package/dist/level0/ServiceDirectory.js.map +1 -0
  115. package/dist/level0/index.d.ts +10 -0
  116. package/dist/level0/index.d.ts.map +1 -0
  117. package/dist/level0/index.js +15 -0
  118. package/dist/level0/index.js.map +1 -0
  119. package/dist/level0/provide.d.ts +51 -0
  120. package/dist/level0/provide.d.ts.map +1 -0
  121. package/dist/level0/provide.js +113 -0
  122. package/dist/level0/provide.js.map +1 -0
  123. package/dist/level0/request.d.ts +53 -0
  124. package/dist/level0/request.d.ts.map +1 -0
  125. package/dist/level0/request.js +462 -0
  126. package/dist/level0/request.js.map +1 -0
  127. package/dist/level1/Agent.d.ts +472 -0
  128. package/dist/level1/Agent.d.ts.map +1 -0
  129. package/dist/level1/Agent.js +1091 -0
  130. package/dist/level1/Agent.js.map +1 -0
  131. package/dist/level1/index.d.ts +10 -0
  132. package/dist/level1/index.d.ts.map +1 -0
  133. package/dist/level1/index.js +30 -0
  134. package/dist/level1/index.js.map +1 -0
  135. package/dist/level1/pricing/PriceCalculator.d.ts +62 -0
  136. package/dist/level1/pricing/PriceCalculator.d.ts.map +1 -0
  137. package/dist/level1/pricing/PriceCalculator.js +237 -0
  138. package/dist/level1/pricing/PriceCalculator.js.map +1 -0
  139. package/dist/level1/pricing/PricingStrategy.d.ts +179 -0
  140. package/dist/level1/pricing/PricingStrategy.d.ts.map +1 -0
  141. package/dist/level1/pricing/PricingStrategy.js +11 -0
  142. package/dist/level1/pricing/PricingStrategy.js.map +1 -0
  143. package/dist/level1/types/Job.d.ts +166 -0
  144. package/dist/level1/types/Job.d.ts.map +1 -0
  145. package/dist/level1/types/Job.js +11 -0
  146. package/dist/level1/types/Job.js.map +1 -0
  147. package/dist/level1/types/Options.d.ts +258 -0
  148. package/dist/level1/types/Options.d.ts.map +1 -0
  149. package/dist/level1/types/Options.js +8 -0
  150. package/dist/level1/types/Options.js.map +1 -0
  151. package/dist/level1/types/index.d.ts +8 -0
  152. package/dist/level1/types/index.d.ts.map +1 -0
  153. package/dist/level1/types/index.js +8 -0
  154. package/dist/level1/types/index.js.map +1 -0
  155. package/dist/protocol/ACTPKernel.d.ts +229 -2
  156. package/dist/protocol/ACTPKernel.d.ts.map +1 -1
  157. package/dist/protocol/ACTPKernel.js +367 -33
  158. package/dist/protocol/ACTPKernel.js.map +1 -1
  159. package/dist/protocol/AgentRegistry.d.ts +177 -0
  160. package/dist/protocol/AgentRegistry.d.ts.map +1 -0
  161. package/dist/protocol/AgentRegistry.js +449 -0
  162. package/dist/protocol/AgentRegistry.js.map +1 -0
  163. package/dist/protocol/DIDManager.d.ts +289 -0
  164. package/dist/protocol/DIDManager.d.ts.map +1 -0
  165. package/dist/protocol/DIDManager.js +481 -0
  166. package/dist/protocol/DIDManager.js.map +1 -0
  167. package/dist/protocol/DIDResolver.d.ts +236 -0
  168. package/dist/protocol/DIDResolver.d.ts.map +1 -0
  169. package/dist/protocol/DIDResolver.js +495 -0
  170. package/dist/protocol/DIDResolver.js.map +1 -0
  171. package/dist/protocol/EASHelper.d.ts +57 -2
  172. package/dist/protocol/EASHelper.d.ts.map +1 -1
  173. package/dist/protocol/EASHelper.js +230 -37
  174. package/dist/protocol/EASHelper.js.map +1 -1
  175. package/dist/protocol/EscrowVault.d.ts +93 -2
  176. package/dist/protocol/EscrowVault.d.ts.map +1 -1
  177. package/dist/protocol/EscrowVault.js +122 -33
  178. package/dist/protocol/EscrowVault.js.map +1 -1
  179. package/dist/protocol/EventMonitor.d.ts +45 -1
  180. package/dist/protocol/EventMonitor.d.ts.map +1 -1
  181. package/dist/protocol/EventMonitor.js +64 -8
  182. package/dist/protocol/EventMonitor.js.map +1 -1
  183. package/dist/protocol/MessageSigner.d.ts +116 -2
  184. package/dist/protocol/MessageSigner.d.ts.map +1 -1
  185. package/dist/protocol/MessageSigner.js +215 -9
  186. package/dist/protocol/MessageSigner.js.map +1 -1
  187. package/dist/protocol/ProofGenerator.d.ts +93 -0
  188. package/dist/protocol/ProofGenerator.d.ts.map +1 -1
  189. package/dist/protocol/ProofGenerator.js +194 -9
  190. package/dist/protocol/ProofGenerator.js.map +1 -1
  191. package/dist/protocol/QuoteBuilder.d.ts +8 -0
  192. package/dist/protocol/QuoteBuilder.d.ts.map +1 -1
  193. package/dist/protocol/QuoteBuilder.js +8 -0
  194. package/dist/protocol/QuoteBuilder.js.map +1 -1
  195. package/dist/runtime/BlockchainRuntime.d.ts +360 -0
  196. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -0
  197. package/dist/runtime/BlockchainRuntime.js +767 -0
  198. package/dist/runtime/BlockchainRuntime.js.map +1 -0
  199. package/dist/runtime/IACTPRuntime.d.ts +271 -0
  200. package/dist/runtime/IACTPRuntime.d.ts.map +1 -0
  201. package/dist/runtime/IACTPRuntime.js +15 -0
  202. package/dist/runtime/IACTPRuntime.js.map +1 -0
  203. package/dist/runtime/MockRuntime.d.ts +445 -0
  204. package/dist/runtime/MockRuntime.d.ts.map +1 -0
  205. package/dist/runtime/MockRuntime.js +1065 -0
  206. package/dist/runtime/MockRuntime.js.map +1 -0
  207. package/dist/runtime/MockStateManager.d.ts +233 -0
  208. package/dist/runtime/MockStateManager.d.ts.map +1 -0
  209. package/dist/runtime/MockStateManager.js +533 -0
  210. package/dist/runtime/MockStateManager.js.map +1 -0
  211. package/dist/runtime/index.d.ts +14 -0
  212. package/dist/runtime/index.d.ts.map +1 -0
  213. package/dist/runtime/index.js +42 -0
  214. package/dist/runtime/index.js.map +1 -0
  215. package/dist/runtime/types/MockState.d.ts +167 -0
  216. package/dist/runtime/types/MockState.d.ts.map +1 -0
  217. package/dist/runtime/types/MockState.js +43 -0
  218. package/dist/runtime/types/MockState.js.map +1 -0
  219. package/dist/types/agent.d.ts +76 -0
  220. package/dist/types/agent.d.ts.map +1 -0
  221. package/dist/types/agent.js +8 -0
  222. package/dist/types/agent.js.map +1 -0
  223. package/dist/types/did.d.ts +192 -0
  224. package/dist/types/did.d.ts.map +1 -0
  225. package/dist/types/did.js +38 -0
  226. package/dist/types/did.js.map +1 -0
  227. package/dist/types/eip712.d.ts +34 -0
  228. package/dist/types/eip712.d.ts.map +1 -1
  229. package/dist/types/eip712.js +31 -5
  230. package/dist/types/eip712.js.map +1 -1
  231. package/dist/types/escrow.d.ts +17 -10
  232. package/dist/types/escrow.d.ts.map +1 -1
  233. package/dist/types/index.d.ts +5 -0
  234. package/dist/types/index.d.ts.map +1 -1
  235. package/dist/types/index.js +8 -0
  236. package/dist/types/index.js.map +1 -1
  237. package/dist/types/message.d.ts +32 -0
  238. package/dist/types/message.d.ts.map +1 -1
  239. package/dist/types/message.js +4 -0
  240. package/dist/types/message.js.map +1 -1
  241. package/dist/types/state.d.ts +28 -0
  242. package/dist/types/state.d.ts.map +1 -1
  243. package/dist/types/state.js +37 -6
  244. package/dist/types/state.js.map +1 -1
  245. package/dist/types/transaction.d.ts +17 -0
  246. package/dist/types/transaction.d.ts.map +1 -1
  247. package/dist/utils/ErrorRecoveryGuide.d.ts +125 -0
  248. package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -0
  249. package/dist/utils/ErrorRecoveryGuide.js +579 -0
  250. package/dist/utils/ErrorRecoveryGuide.js.map +1 -0
  251. package/dist/utils/Helpers.d.ts +453 -0
  252. package/dist/utils/Helpers.d.ts.map +1 -0
  253. package/dist/utils/Helpers.js +623 -0
  254. package/dist/utils/Helpers.js.map +1 -0
  255. package/dist/utils/IPFSClient.d.ts +113 -0
  256. package/dist/utils/IPFSClient.d.ts.map +1 -1
  257. package/dist/utils/IPFSClient.js +128 -7
  258. package/dist/utils/IPFSClient.js.map +1 -1
  259. package/dist/utils/Logger.d.ts +195 -0
  260. package/dist/utils/Logger.d.ts.map +1 -0
  261. package/dist/utils/Logger.js +382 -0
  262. package/dist/utils/Logger.js.map +1 -0
  263. package/dist/utils/NonceManager.d.ts +234 -1
  264. package/dist/utils/NonceManager.d.ts.map +1 -1
  265. package/dist/utils/NonceManager.js +372 -7
  266. package/dist/utils/NonceManager.js.map +1 -1
  267. package/dist/utils/RateLimiter.d.ts +253 -0
  268. package/dist/utils/RateLimiter.d.ts.map +1 -0
  269. package/dist/utils/RateLimiter.js +424 -0
  270. package/dist/utils/RateLimiter.js.map +1 -0
  271. package/dist/utils/ReceivedNonceTracker.d.ts +175 -0
  272. package/dist/utils/ReceivedNonceTracker.d.ts.map +1 -1
  273. package/dist/utils/ReceivedNonceTracker.js +261 -5
  274. package/dist/utils/ReceivedNonceTracker.js.map +1 -1
  275. package/dist/utils/SDKLifecycle.d.ts +156 -0
  276. package/dist/utils/SDKLifecycle.d.ts.map +1 -0
  277. package/dist/utils/SDKLifecycle.js +347 -0
  278. package/dist/utils/SDKLifecycle.js.map +1 -0
  279. package/dist/utils/SecureNonce.d.ts +57 -0
  280. package/dist/utils/SecureNonce.d.ts.map +1 -0
  281. package/dist/utils/SecureNonce.js +80 -0
  282. package/dist/utils/SecureNonce.js.map +1 -0
  283. package/dist/utils/Semaphore.d.ts +123 -0
  284. package/dist/utils/Semaphore.d.ts.map +1 -0
  285. package/dist/utils/Semaphore.js +247 -0
  286. package/dist/utils/Semaphore.js.map +1 -0
  287. package/dist/utils/UsedAttestationTracker.d.ts +167 -0
  288. package/dist/utils/UsedAttestationTracker.d.ts.map +1 -0
  289. package/dist/utils/UsedAttestationTracker.js +309 -0
  290. package/dist/utils/UsedAttestationTracker.js.map +1 -0
  291. package/dist/utils/canonicalJson.d.ts +22 -0
  292. package/dist/utils/canonicalJson.d.ts.map +1 -1
  293. package/dist/utils/canonicalJson.js +26 -3
  294. package/dist/utils/canonicalJson.js.map +1 -1
  295. package/dist/utils/computeTypeHash.d.ts +14 -0
  296. package/dist/utils/computeTypeHash.d.ts.map +1 -1
  297. package/dist/utils/computeTypeHash.js +19 -2
  298. package/dist/utils/computeTypeHash.js.map +1 -1
  299. package/dist/utils/fsSafe.d.ts +14 -0
  300. package/dist/utils/fsSafe.d.ts.map +1 -0
  301. package/dist/utils/fsSafe.js +89 -0
  302. package/dist/utils/fsSafe.js.map +1 -0
  303. package/dist/utils/index.d.ts +15 -0
  304. package/dist/utils/index.d.ts.map +1 -0
  305. package/dist/utils/index.js +51 -0
  306. package/dist/utils/index.js.map +1 -0
  307. package/dist/utils/security.d.ts +147 -0
  308. package/dist/utils/security.d.ts.map +1 -0
  309. package/dist/utils/security.js +391 -0
  310. package/dist/utils/security.js.map +1 -0
  311. package/dist/utils/validation.d.ts +40 -0
  312. package/dist/utils/validation.d.ts.map +1 -1
  313. package/dist/utils/validation.js +184 -7
  314. package/dist/utils/validation.js.map +1 -1
  315. package/package.json +54 -37
  316. package/src/ACTPClient.ts +692 -178
  317. package/src/abi/AgentRegistry.json +782 -0
  318. package/src/abi/EscrowVault.json +106 -38
  319. package/src/abi/IdentityRegistry.json +316 -0
  320. package/src/adapters/BaseAdapter.ts +473 -0
  321. package/src/adapters/BeginnerAdapter.ts +232 -0
  322. package/src/adapters/IntermediateAdapter.ts +316 -0
  323. package/src/adapters/index.ts +25 -0
  324. package/src/builders/DeliveryProofBuilder.ts +3 -2
  325. package/src/cli/commands/balance.ts +110 -0
  326. package/src/cli/commands/batch.ts +487 -0
  327. package/src/cli/commands/config.ts +231 -0
  328. package/src/cli/commands/init.ts +161 -0
  329. package/src/cli/commands/mint.ts +116 -0
  330. package/src/cli/commands/pay.ts +113 -0
  331. package/src/cli/commands/simulate.ts +345 -0
  332. package/src/cli/commands/time.ts +303 -0
  333. package/src/cli/commands/tx.ts +448 -0
  334. package/src/cli/commands/watch.ts +211 -0
  335. package/src/cli/index.ts +116 -0
  336. package/src/cli/utils/client.ts +249 -0
  337. package/src/cli/utils/config.ts +282 -0
  338. package/src/cli/utils/output.ts +465 -0
  339. package/src/config/networks.ts +32 -9
  340. package/src/errors/index.ts +298 -1
  341. package/src/index.ts +207 -71
  342. package/src/level0/Provider.ts +117 -0
  343. package/src/level0/ServiceDirectory.ts +131 -0
  344. package/src/level0/index.ts +10 -0
  345. package/src/level0/provide.ts +131 -0
  346. package/src/level0/request.ts +494 -0
  347. package/src/level1/Agent.ts +1432 -0
  348. package/src/level1/index.ts +10 -0
  349. package/src/level1/pricing/PriceCalculator.ts +255 -0
  350. package/src/level1/pricing/PricingStrategy.ts +198 -0
  351. package/src/level1/types/Job.ts +179 -0
  352. package/src/level1/types/Options.ts +291 -0
  353. package/src/level1/types/index.ts +8 -0
  354. package/src/protocol/ACTPKernel.ts +175 -23
  355. package/src/protocol/AgentRegistry.ts +559 -0
  356. package/src/protocol/DIDManager.ts +629 -0
  357. package/src/protocol/DIDResolver.ts +554 -0
  358. package/src/protocol/EASHelper.ts +230 -46
  359. package/src/protocol/EscrowVault.ts +68 -50
  360. package/src/protocol/EventMonitor.ts +44 -15
  361. package/src/protocol/MessageSigner.ts +193 -13
  362. package/src/protocol/ProofGenerator.ts +223 -4
  363. package/src/runtime/BlockchainRuntime.ts +993 -0
  364. package/src/runtime/IACTPRuntime.ts +284 -0
  365. package/src/runtime/MockRuntime.ts +1244 -0
  366. package/src/runtime/MockStateManager.ts +576 -0
  367. package/src/runtime/index.ts +25 -0
  368. package/src/runtime/types/MockState.ts +227 -0
  369. package/src/types/agent.ts +79 -0
  370. package/src/types/did.ts +223 -0
  371. package/src/types/escrow.ts +12 -11
  372. package/src/types/index.ts +5 -1
  373. package/src/types/state.ts +12 -3
  374. package/src/types/transaction.ts +4 -1
  375. package/src/utils/ErrorRecoveryGuide.ts +675 -0
  376. package/src/utils/Helpers.ts +688 -0
  377. package/src/utils/IPFSClient.ts +122 -5
  378. package/src/utils/Logger.ts +484 -0
  379. package/src/utils/NonceManager.ts +305 -8
  380. package/src/utils/RateLimiter.ts +534 -0
  381. package/src/utils/ReceivedNonceTracker.ts +170 -0
  382. package/src/utils/SDKLifecycle.ts +416 -0
  383. package/src/utils/SecureNonce.ts +78 -0
  384. package/src/utils/Semaphore.ts +276 -0
  385. package/src/utils/UsedAttestationTracker.ts +387 -0
  386. package/src/utils/fsSafe.ts +75 -0
  387. package/src/utils/index.ts +80 -0
  388. package/src/utils/security.ts +418 -0
  389. package/src/utils/validation.ts +164 -0
  390. package/src/__tests__/ProofGenerator.test.ts +0 -124
  391. package/src/__tests__/QuoteBuilder.test.ts +0 -516
  392. package/src/__tests__/StateMachine.test.ts +0 -82
  393. package/src/__tests__/builders/DeliveryProofBuilder.test.ts +0 -581
  394. package/src/__tests__/integration/ACTPClient.test.ts +0 -263
  395. package/src/__tests__/integration.test.ts +0 -289
  396. package/src/__tests__/protocol/EASHelper.test.ts +0 -472
  397. package/src/__tests__/protocol/EventMonitor.test.ts +0 -382
  398. package/src/__tests__/security/ACTPKernel.security.test.ts +0 -1167
  399. package/src/__tests__/security/EscrowVault.security.test.ts +0 -570
  400. package/src/__tests__/security/MessageSigner.security.test.ts +0 -286
  401. package/src/__tests__/security/NonceReplay.security.test.ts +0 -501
  402. package/src/__tests__/security/validation.security.test.ts +0 -376
  403. package/src/__tests__/utils/IPFSClient.test.ts +0 -262
  404. package/src/__tests__/utils/NonceManager.test.ts +0 -205
  405. package/src/__tests__/utils/canonicalJson.test.ts +0 -153
@@ -0,0 +1,1244 @@
1
+ /**
2
+ * MockRuntime - Core mock blockchain engine for ACTP protocol testing
3
+ *
4
+ * Provides a complete mock blockchain environment for testing ACTP transactions
5
+ * without real blockchain interactions. Implements the 8-state ACTP transaction
6
+ * lifecycle with strict state machine validation.
7
+ *
8
+ * Features:
9
+ * - 8-state transaction lifecycle (INITIATED -> QUOTED -> COMMITTED -> IN_PROGRESS -> DELIVERED -> SETTLED)
10
+ * - Time manipulation (advanceTime, setTime, advanceBlocks)
11
+ * - Balance management (mint, transfer)
12
+ * - Event recording and querying
13
+ * - Escrow operations (link, release, balance tracking)
14
+ *
15
+ * @module runtime/MockRuntime
16
+ * @see ADR-004 (Mock Blockchain Emulation Scope)
17
+ * @see MOCK_MODE_SPECIFICATION.md
18
+ */
19
+
20
+ import * as crypto from 'crypto';
21
+ import { MockStateManager } from './MockStateManager';
22
+ import {
23
+ MockState,
24
+ MockTransaction,
25
+ MockEscrow,
26
+ MockEvent,
27
+ TransactionState,
28
+ TransactionStateValue,
29
+ MOCK_STATE_DEFAULTS,
30
+ } from './types/MockState';
31
+ import { IACTPRuntime, CreateTransactionParams } from './IACTPRuntime';
32
+
33
+ // ============================================================================
34
+ // Custom Error Classes
35
+ // ============================================================================
36
+
37
+ /**
38
+ * Error thrown when a transaction is not found.
39
+ */
40
+ export class TransactionNotFoundError extends Error {
41
+ public readonly txId: string;
42
+
43
+ constructor(txId: string) {
44
+ super(`Transaction not found: ${txId}`);
45
+ this.name = 'TransactionNotFoundError';
46
+ this.txId = txId;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Error thrown when an invalid state transition is attempted.
52
+ */
53
+ export class InvalidStateTransitionError extends Error {
54
+ public readonly txId: string;
55
+ public readonly currentState: TransactionState;
56
+ public readonly targetState: TransactionState;
57
+
58
+ constructor(txId: string, currentState: TransactionState, targetState: TransactionState) {
59
+ super(
60
+ `Invalid state transition for transaction ${txId}: ${currentState} -> ${targetState}`
61
+ );
62
+ this.name = 'InvalidStateTransitionError';
63
+ this.txId = txId;
64
+ this.currentState = currentState;
65
+ this.targetState = targetState;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Error thrown when there are insufficient funds for an operation.
71
+ */
72
+ export class InsufficientBalanceError extends Error {
73
+ public readonly address: string;
74
+ public readonly required: string;
75
+ public readonly available: string;
76
+
77
+ constructor(address: string, required: string, available: string) {
78
+ super(
79
+ `Insufficient balance for ${address}: required ${required}, available ${available}`
80
+ );
81
+ this.name = 'InsufficientBalanceError';
82
+ this.address = address;
83
+ this.required = required;
84
+ this.available = available;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Error thrown when an escrow is not found.
90
+ */
91
+ export class EscrowNotFoundError extends Error {
92
+ public readonly escrowId: string;
93
+
94
+ constructor(escrowId: string) {
95
+ super(`Escrow not found: ${escrowId}`);
96
+ this.name = 'EscrowNotFoundError';
97
+ this.escrowId = escrowId;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Error thrown when the deadline has passed.
103
+ */
104
+ export class DeadlinePassedError extends Error {
105
+ public readonly txId: string;
106
+ public readonly deadline: number;
107
+ public readonly currentTime: number;
108
+
109
+ constructor(txId: string, deadline: number, currentTime: number) {
110
+ super(
111
+ `Deadline passed for transaction ${txId}: deadline ${deadline}, current time ${currentTime}`
112
+ );
113
+ this.name = 'DeadlinePassedError';
114
+ this.txId = txId;
115
+ this.deadline = deadline;
116
+ this.currentTime = currentTime;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Error thrown when the contract is paused.
122
+ */
123
+ export class ContractPausedError extends Error {
124
+ constructor() {
125
+ super('Contract is paused');
126
+ this.name = 'ContractPausedError';
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Error thrown when an invalid amount is provided.
132
+ *
133
+ * Thrown when zero or negative amounts are passed to transaction
134
+ * or escrow operations.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * // This will throw InvalidAmountError
139
+ * await runtime.createTransaction({
140
+ * provider: '0x...',
141
+ * requester: '0x...',
142
+ * amount: '0', // Invalid - must be positive
143
+ * deadline: Date.now() + 86400
144
+ * });
145
+ * ```
146
+ */
147
+ export class InvalidAmountError extends Error {
148
+ public readonly amount: string;
149
+ public readonly reason: string;
150
+
151
+ constructor(amount: string, reason: string) {
152
+ super(`Invalid amount "${amount}": ${reason}`);
153
+ this.name = 'InvalidAmountError';
154
+ this.amount = amount;
155
+ this.reason = reason;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Error thrown when dispute window is still active.
161
+ *
162
+ * Thrown when attempting to release escrow funds before the dispute
163
+ * window has expired. Use `runtime.time.advanceTime()` in tests to
164
+ * simulate waiting for the window to close.
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * // Transaction delivered, but dispute window still active
169
+ * await runtime.releaseEscrow(escrowId);
170
+ * // Throws: DisputeWindowActiveError
171
+ *
172
+ * // Solution: advance time past the dispute window
173
+ * runtime.time.advanceTime(tx.disputeWindow + 1);
174
+ * await runtime.releaseEscrow(escrowId); // Now works
175
+ * ```
176
+ */
177
+ export class DisputeWindowActiveError extends Error {
178
+ public readonly txId: string;
179
+ public readonly windowEnd: number;
180
+ public readonly currentTime: number;
181
+
182
+ constructor(txId: string, windowEnd: number, currentTime: number) {
183
+ super(
184
+ `Dispute window still active for transaction ${txId}. ` +
185
+ `Window ends at ${windowEnd}, current time is ${currentTime}. ` +
186
+ `Use time.advanceTime() to simulate waiting.`
187
+ );
188
+ this.name = 'DisputeWindowActiveError';
189
+ this.txId = txId;
190
+ this.windowEnd = windowEnd;
191
+ this.currentTime = currentTime;
192
+ }
193
+ }
194
+
195
+ // ============================================================================
196
+ // Types
197
+ // ============================================================================
198
+
199
+ // CreateTransactionParams now imported from IACTPRuntime interface
200
+
201
+ /**
202
+ * Valid state transitions for the ACTP 8-state machine.
203
+ *
204
+ * State machine:
205
+ * - INITIATED -> QUOTED (optional), COMMITTED, CANCELLED
206
+ * - QUOTED -> COMMITTED, CANCELLED
207
+ * - COMMITTED -> IN_PROGRESS (optional), DELIVERED, CANCELLED
208
+ * - IN_PROGRESS -> DELIVERED, CANCELLED
209
+ * - DELIVERED -> SETTLED, DISPUTED
210
+ * - DISPUTED -> SETTLED
211
+ * - SETTLED (terminal)
212
+ * - CANCELLED (terminal)
213
+ */
214
+ const VALID_TRANSITIONS: Record<TransactionState, TransactionState[]> = {
215
+ INITIATED: ['QUOTED', 'COMMITTED', 'CANCELLED'],
216
+ QUOTED: ['COMMITTED', 'CANCELLED'],
217
+ COMMITTED: ['IN_PROGRESS', 'DELIVERED', 'CANCELLED'],
218
+ IN_PROGRESS: ['DELIVERED', 'CANCELLED'],
219
+ DELIVERED: ['SETTLED', 'DISPUTED'],
220
+ DISPUTED: ['SETTLED'],
221
+ SETTLED: [], // Terminal state
222
+ CANCELLED: [], // Terminal state
223
+ };
224
+
225
+ /**
226
+ * States from which cancellation is allowed.
227
+ */
228
+ const CANCELLABLE_STATES: TransactionState[] = [
229
+ 'INITIATED',
230
+ 'QUOTED',
231
+ 'COMMITTED',
232
+ 'IN_PROGRESS',
233
+ ];
234
+
235
+ // ============================================================================
236
+ // MockRuntime Class
237
+ // ============================================================================
238
+
239
+ /**
240
+ * MockRuntime - Core mock blockchain engine for ACTP protocol testing.
241
+ *
242
+ * Implements the IACTPRuntime interface for mock/testing mode.
243
+ * Provides a complete mock blockchain environment with:
244
+ * - Transaction state machine (8 states)
245
+ * - Time manipulation
246
+ * - Balance management
247
+ * - Event recording
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * const runtime = new MockRuntime();
252
+ *
253
+ * // Mint initial funds
254
+ * await runtime.mintTokens('0xRequester', '10000000000'); // 10,000 USDC
255
+ *
256
+ * // Create transaction
257
+ * const txId = await runtime.createTransaction({
258
+ * provider: '0xProvider',
259
+ * requester: '0xRequester',
260
+ * amount: '1000000', // 1 USDC
261
+ * deadline: runtime.time.now() + 86400,
262
+ * });
263
+ *
264
+ * // Advance time and process
265
+ * runtime.time.advanceTime(3600);
266
+ * ```
267
+ */
268
+ export class MockRuntime implements IACTPRuntime {
269
+ private stateManager: MockStateManager;
270
+ /**
271
+ * In-memory event log, also persisted to state file.
272
+ *
273
+ * SECURITY FIX (L-4): Events are now persisted to the state file
274
+ * so they survive across CLI invocations.
275
+ */
276
+ private eventLog: MockEvent[] = [];
277
+
278
+ /**
279
+ * Time management interface.
280
+ *
281
+ * SECURITY NOTE: All time-modifying operations are async and use
282
+ * file locking to prevent race conditions.
283
+ */
284
+ public readonly time: {
285
+ /** Get current mock timestamp (seconds) */
286
+ now: () => number;
287
+ /** Advance time by specified seconds (async for locking) */
288
+ advanceTime: (seconds: number) => Promise<void>;
289
+ /** Advance time by specified blocks (block time * blocks) (async for locking) */
290
+ advanceBlocks: (blocks: number) => Promise<void>;
291
+ /** Set exact timestamp (must be >= current time) (async for locking) */
292
+ setTime: (timestamp: number) => Promise<void>;
293
+ };
294
+
295
+ /**
296
+ * Event access interface.
297
+ */
298
+ public readonly events: {
299
+ /** Get all recorded events */
300
+ getAll: () => MockEvent[];
301
+ /** Get events filtered by type */
302
+ getByType: (type: string) => MockEvent[];
303
+ /** Get events for a specific transaction */
304
+ getByTransaction: (txId: string) => MockEvent[];
305
+ /** Clear all events */
306
+ clear: () => void;
307
+ };
308
+
309
+ /**
310
+ * Creates a new MockRuntime instance.
311
+ *
312
+ * @param stateManager - Optional custom state manager (default: creates new one)
313
+ */
314
+ constructor(stateManager?: MockStateManager) {
315
+ this.stateManager = stateManager ?? new MockStateManager();
316
+
317
+ // SECURITY FIX (L-4): Load persisted events from state file
318
+ this.loadPersistedEvents();
319
+
320
+ // Initialize time interface
321
+ // SECURITY FIX: Time operations now use withLock to prevent race conditions
322
+ this.time = {
323
+ now: () => this.getCurrentTime(),
324
+ advanceTime: (seconds: number) => this.advanceTimeWithLock(seconds),
325
+ advanceBlocks: (blocks: number) => this.advanceBlocksWithLock(blocks),
326
+ setTime: (timestamp: number) => this.setTimeWithLock(timestamp),
327
+ };
328
+
329
+ // Initialize events interface
330
+ this.events = {
331
+ getAll: () => [...this.eventLog],
332
+ getByType: (type: string) => this.eventLog.filter((e) => e.type === type),
333
+ getByTransaction: (txId: string) => this.getEventsByTransaction(txId),
334
+ clear: () => {
335
+ this.eventLog = [];
336
+ // Also clear persisted events
337
+ this.clearPersistedEvents();
338
+ },
339
+ };
340
+ }
341
+
342
+ /**
343
+ * Load events from persisted state file.
344
+ *
345
+ * SECURITY FIX (L-4): Events survive across CLI invocations.
346
+ */
347
+ private loadPersistedEvents(): void {
348
+ try {
349
+ const state = this.stateManager.loadState();
350
+ this.eventLog = state.events ?? [];
351
+ } catch {
352
+ // If state doesn't exist yet, start with empty events
353
+ this.eventLog = [];
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Clear persisted events from state file.
359
+ */
360
+ private clearPersistedEvents(): void {
361
+ try {
362
+ const state = this.stateManager.loadState();
363
+ state.events = [];
364
+ this.stateManager.saveState(state);
365
+ } catch {
366
+ // Ignore errors during clear
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Persist an event to both in-memory log and state file.
372
+ *
373
+ * SECURITY FIX (L-4): Events are persisted for audit trail.
374
+ *
375
+ * @param event - Event to persist
376
+ * @param state - Current state (to avoid re-loading)
377
+ */
378
+ private persistEvent(event: MockEvent, state: MockState): void {
379
+ this.eventLog.push(event);
380
+ // Ensure events array exists
381
+ if (!state.events) {
382
+ state.events = [];
383
+ }
384
+ state.events.push(event);
385
+ }
386
+
387
+ // ============================================================================
388
+ // Transaction Operations
389
+ // ============================================================================
390
+
391
+ /**
392
+ * Creates a new transaction.
393
+ *
394
+ * @param params - Transaction creation parameters
395
+ * @returns Promise resolving to the transaction ID (bytes32 hex string)
396
+ *
397
+ * @throws {DeadlinePassedError} If deadline is in the past
398
+ *
399
+ * @example
400
+ * ```typescript
401
+ * const txId = await runtime.createTransaction({
402
+ * provider: '0xProvider',
403
+ * requester: '0xRequester',
404
+ * amount: '1000000', // 1 USDC
405
+ * deadline: runtime.time.now() + 86400,
406
+ * });
407
+ * ```
408
+ */
409
+ async createTransaction(params: CreateTransactionParams): Promise<string> {
410
+ return this.stateManager.withLock(async (state) => {
411
+ const currentTime = state.blockchain.currentTime;
412
+ const blockNumber = state.blockchain.blockNumber;
413
+
414
+ // Validate amount (Issue #2 fix)
415
+ const amountBigInt = BigInt(params.amount);
416
+ if (amountBigInt <= 0n) {
417
+ throw new InvalidAmountError(params.amount, 'Amount must be positive');
418
+ }
419
+
420
+ // Validate deadline
421
+ if (params.deadline <= currentTime) {
422
+ throw new DeadlinePassedError('new', params.deadline, currentTime);
423
+ }
424
+
425
+ // SECURITY FIX (M-4): Generate transaction ID with collision check
426
+ const txId = this.generateTransactionIdWithCollisionCheck(state);
427
+
428
+ // Create transaction
429
+ const transaction: MockTransaction = {
430
+ id: txId,
431
+ requester: params.requester,
432
+ provider: params.provider,
433
+ amount: params.amount,
434
+ state: 'INITIATED',
435
+ createdAt: currentTime,
436
+ updatedAt: currentTime,
437
+ deadline: params.deadline,
438
+ disputeWindow: params.disputeWindow ?? 172800, // Default 2 days
439
+ completedAt: null,
440
+ escrowId: null,
441
+ serviceDescription: params.serviceDescription ?? '',
442
+ deliveryProof: null,
443
+ events: [],
444
+ };
445
+
446
+ // Record event
447
+ const event: MockEvent = {
448
+ type: 'TransactionCreated',
449
+ timestamp: currentTime,
450
+ blockNumber,
451
+ data: {
452
+ txId,
453
+ requester: params.requester,
454
+ provider: params.provider,
455
+ amount: params.amount,
456
+ deadline: params.deadline,
457
+ },
458
+ };
459
+
460
+ transaction.events.push(event);
461
+ state.transactions[txId] = transaction;
462
+
463
+ // SECURITY FIX (L-4): Persist event to state file
464
+ this.persistEvent(event, state);
465
+
466
+ return txId;
467
+ });
468
+ }
469
+
470
+ /**
471
+ * Gets a transaction by ID.
472
+ *
473
+ * @param txId - Transaction ID
474
+ * @returns Promise resolving to the transaction or null if not found
475
+ */
476
+ async getTransaction(txId: string): Promise<MockTransaction | null> {
477
+ const state = this.stateManager.loadState();
478
+ return state.transactions[txId] ?? null;
479
+ }
480
+
481
+ /**
482
+ * Gets all transactions.
483
+ *
484
+ * @returns Promise resolving to array of all transactions
485
+ */
486
+ async getAllTransactions(): Promise<MockTransaction[]> {
487
+ const state = this.stateManager.loadState();
488
+ return Object.values(state.transactions);
489
+ }
490
+
491
+ /**
492
+ * Gets transactions filtered by provider address and optionally state.
493
+ *
494
+ * SECURITY FIX (H-1): Filtered query to prevent DoS via memory exhaustion.
495
+ * Instead of loading all transactions and filtering client-side, we filter
496
+ * server-side and limit the result set.
497
+ *
498
+ * @param provider - Provider address to filter by
499
+ * @param state - Optional state filter (e.g., 'INITIATED', 'DELIVERED')
500
+ * @param limit - Maximum number of transactions to return (default 100)
501
+ * @returns Promise resolving to filtered transactions
502
+ *
503
+ * @example
504
+ * ```typescript
505
+ * // Get up to 100 INITIATED transactions for this provider
506
+ * const pending = await runtime.getTransactionsByProvider(
507
+ * providerAddress,
508
+ * 'INITIATED',
509
+ * 100
510
+ * );
511
+ * ```
512
+ */
513
+ async getTransactionsByProvider(
514
+ provider: string,
515
+ state?: TransactionState,
516
+ limit: number = 100
517
+ ): Promise<MockTransaction[]> {
518
+ return this.stateManager.withLock(async (s) => {
519
+ let txs = Object.values(s.transactions).filter(
520
+ (tx) => tx.provider === provider
521
+ );
522
+
523
+ if (state) {
524
+ txs = txs.filter((tx) => tx.state === state);
525
+ }
526
+
527
+ if (limit > 0) {
528
+ txs = txs.slice(0, limit);
529
+ }
530
+
531
+ return txs;
532
+ });
533
+ }
534
+
535
+ /**
536
+ * Transitions a transaction to a new state.
537
+ *
538
+ * Validates the transition against the ACTP 8-state machine:
539
+ * - INITIATED -> QUOTED, COMMITTED, CANCELLED
540
+ * - QUOTED -> COMMITTED, CANCELLED
541
+ * - COMMITTED -> IN_PROGRESS, DELIVERED, CANCELLED
542
+ * - IN_PROGRESS -> DELIVERED, CANCELLED
543
+ * - DELIVERED -> SETTLED, DISPUTED
544
+ * - DISPUTED -> SETTLED
545
+ *
546
+ * @param txId - Transaction ID
547
+ * @param newState - Target state
548
+ *
549
+ * @throws {TransactionNotFoundError} If transaction doesn't exist
550
+ * @throws {InvalidStateTransitionError} If transition is not valid
551
+ * @throws {DeadlinePassedError} If deadline passed (for CANCELLED transition)
552
+ *
553
+ * @example
554
+ * ```typescript
555
+ * // Transition to DELIVERED state
556
+ * await runtime.transitionState(txId, 'DELIVERED');
557
+ *
558
+ * // With delivery proof
559
+ * await runtime.transitionState(txId, 'DELIVERED', deliveryProofBytes);
560
+ * ```
561
+ */
562
+ async transitionState(txId: string, newState: TransactionState, proof?: string): Promise<void> {
563
+ // SECURITY FIX (PROOF-PARAM): Accept optional proof parameter for interface compliance
564
+ // In MockRuntime, proof is stored but not validated (no on-chain verification)
565
+ // This allows testing delivery proof flows without actual blockchain interaction
566
+ return this.stateManager.withLock(async (state) => {
567
+ const tx = state.transactions[txId];
568
+ if (!tx) {
569
+ throw new TransactionNotFoundError(txId);
570
+ }
571
+
572
+ const currentState = tx.state;
573
+ const currentTime = state.blockchain.currentTime;
574
+ const blockNumber = state.blockchain.blockNumber;
575
+
576
+ // Validate transition
577
+ const validTargets = VALID_TRANSITIONS[currentState];
578
+ if (!validTargets.includes(newState)) {
579
+ throw new InvalidStateTransitionError(txId, currentState, newState);
580
+ }
581
+
582
+ // For cancellation before deadline, check deadline hasn't passed
583
+ if (newState === 'CANCELLED' && CANCELLABLE_STATES.includes(currentState)) {
584
+ // Allow cancellation if deadline passed OR if before deadline
585
+ // (This matches real contract behavior - can cancel anytime before DELIVERED)
586
+ }
587
+
588
+ // Update state
589
+ const oldState = tx.state;
590
+ tx.state = newState;
591
+ tx.updatedAt = currentTime;
592
+
593
+ // Record completion time for DELIVERED state
594
+ if (newState === 'DELIVERED') {
595
+ tx.completedAt = currentTime;
596
+ // SECURITY FIX (PROOF-PARAM): Store delivery proof if provided
597
+ if (proof) {
598
+ tx.deliveryProof = proof;
599
+ }
600
+ }
601
+
602
+ // Handle escrow refund on CANCELLED state (Issue #1 fix)
603
+ if (newState === 'CANCELLED' && tx.escrowId !== null) {
604
+ const escrow = state.escrows[tx.escrowId];
605
+ if (escrow && BigInt(escrow.balance) > 0n) {
606
+ const refundAmount = BigInt(escrow.balance);
607
+
608
+ // Create requester account if doesn't exist
609
+ if (!state.accounts[tx.requester]) {
610
+ state.accounts[tx.requester] = {
611
+ address: tx.requester,
612
+ usdcBalance: '0',
613
+ };
614
+ }
615
+
616
+ // Return funds to requester
617
+ const requesterBalance = BigInt(state.accounts[tx.requester].usdcBalance);
618
+ state.accounts[tx.requester].usdcBalance = (requesterBalance + refundAmount).toString();
619
+
620
+ // Clear escrow balance
621
+ escrow.balance = '0';
622
+ escrow.locked = false;
623
+
624
+ // Record EscrowRefunded event
625
+ const refundEvent: MockEvent = {
626
+ type: 'EscrowRefunded',
627
+ timestamp: currentTime,
628
+ blockNumber,
629
+ data: {
630
+ txId,
631
+ escrowId: tx.escrowId,
632
+ requester: tx.requester,
633
+ amount: refundAmount.toString(),
634
+ },
635
+ };
636
+
637
+ tx.events.push(refundEvent);
638
+ // SECURITY FIX (L-4): Persist event
639
+ this.persistEvent(refundEvent, state);
640
+ }
641
+ }
642
+
643
+ // Record event
644
+ const event: MockEvent = {
645
+ type: 'StateTransitioned',
646
+ timestamp: currentTime,
647
+ blockNumber,
648
+ data: {
649
+ txId,
650
+ oldState,
651
+ newState,
652
+ },
653
+ };
654
+
655
+ tx.events.push(event);
656
+ // SECURITY FIX (L-4): Persist event
657
+ this.persistEvent(event, state);
658
+ });
659
+ }
660
+
661
+ // ============================================================================
662
+ // Escrow Operations
663
+ // ============================================================================
664
+
665
+ /**
666
+ * Links an escrow to a transaction and locks funds.
667
+ *
668
+ * Automatically transitions INITIATED or QUOTED -> COMMITTED (per ACTP spec).
669
+ * Deducts funds from requester and adds to escrow balance.
670
+ *
671
+ * @param txId - Transaction ID
672
+ * @param amount - Amount to lock (must match transaction amount)
673
+ * @returns Promise resolving to the escrow ID
674
+ *
675
+ * @throws {TransactionNotFoundError} If transaction doesn't exist
676
+ * @throws {InvalidStateTransitionError} If not in INITIATED or QUOTED state
677
+ * @throws {InsufficientBalanceError} If requester has insufficient funds
678
+ *
679
+ * @example
680
+ * ```typescript
681
+ * // Ensure requester has funds
682
+ * await runtime.mintTokens(requester, '10000000');
683
+ *
684
+ * // Link escrow
685
+ * const escrowId = await runtime.linkEscrow(txId, '1000000');
686
+ * ```
687
+ */
688
+ async linkEscrow(txId: string, amount: string): Promise<string> {
689
+ return this.stateManager.withLock(async (state) => {
690
+ // Validate amount (Issue #2 fix)
691
+ const amountBigInt = BigInt(amount);
692
+ if (amountBigInt <= 0n) {
693
+ throw new InvalidAmountError(amount, 'Amount must be positive');
694
+ }
695
+
696
+ const tx = state.transactions[txId];
697
+ if (!tx) {
698
+ throw new TransactionNotFoundError(txId);
699
+ }
700
+
701
+ // Validate state - can only link from INITIATED or QUOTED
702
+ if (tx.state !== 'INITIATED' && tx.state !== 'QUOTED') {
703
+ throw new InvalidStateTransitionError(txId, tx.state, 'COMMITTED');
704
+ }
705
+
706
+ // Check deadline
707
+ const currentTime = state.blockchain.currentTime;
708
+ if (currentTime > tx.deadline) {
709
+ throw new DeadlinePassedError(txId, tx.deadline, currentTime);
710
+ }
711
+
712
+ // Check requester balance
713
+ const requesterAccount = state.accounts[tx.requester];
714
+ const requesterBalance = BigInt(requesterAccount?.usdcBalance ?? '0');
715
+
716
+ if (requesterBalance < amountBigInt) {
717
+ throw new InsufficientBalanceError(
718
+ tx.requester,
719
+ amount,
720
+ requesterBalance.toString()
721
+ );
722
+ }
723
+
724
+ // Generate escrow ID
725
+ const escrowId = this.generateEscrowId();
726
+
727
+ // Create or update requester account
728
+ if (!state.accounts[tx.requester]) {
729
+ state.accounts[tx.requester] = {
730
+ address: tx.requester,
731
+ usdcBalance: '0',
732
+ };
733
+ }
734
+
735
+ // Deduct from requester
736
+ state.accounts[tx.requester].usdcBalance = (
737
+ requesterBalance - amountBigInt
738
+ ).toString();
739
+
740
+ // Create escrow
741
+ const escrow: MockEscrow = {
742
+ id: escrowId,
743
+ balance: amount,
744
+ locked: true,
745
+ transactions: [txId],
746
+ createdAt: currentTime,
747
+ };
748
+
749
+ state.escrows[escrowId] = escrow;
750
+
751
+ // Link to transaction and auto-transition to COMMITTED
752
+ const oldState = tx.state;
753
+ tx.escrowId = escrowId;
754
+ tx.state = 'COMMITTED';
755
+ tx.updatedAt = currentTime;
756
+
757
+ // Record events
758
+ const blockNumber = state.blockchain.blockNumber;
759
+
760
+ const linkEvent: MockEvent = {
761
+ type: 'EscrowLinked',
762
+ timestamp: currentTime,
763
+ blockNumber,
764
+ data: {
765
+ txId,
766
+ escrowId,
767
+ amount,
768
+ },
769
+ };
770
+
771
+ const transitionEvent: MockEvent = {
772
+ type: 'StateTransitioned',
773
+ timestamp: currentTime,
774
+ blockNumber,
775
+ data: {
776
+ txId,
777
+ oldState,
778
+ newState: 'COMMITTED',
779
+ },
780
+ };
781
+
782
+ tx.events.push(linkEvent, transitionEvent);
783
+ // SECURITY FIX (L-4): Persist events
784
+ this.persistEvent(linkEvent, state);
785
+ this.persistEvent(transitionEvent, state);
786
+
787
+ return escrowId;
788
+ });
789
+ }
790
+
791
+ /**
792
+ * Releases escrow funds to the provider and settles the transaction.
793
+ *
794
+ * Can only be called when transaction is in DELIVERED state.
795
+ *
796
+ * @param escrowId - Escrow ID
797
+ *
798
+ * @throws {EscrowNotFoundError} If escrow doesn't exist
799
+ * @throws {TransactionNotFoundError} If linked transaction doesn't exist
800
+ * @throws {InvalidStateTransitionError} If transaction not in DELIVERED state
801
+ *
802
+ * @example
803
+ * ```typescript
804
+ * // After delivery is confirmed
805
+ * await runtime.releaseEscrow(escrowId);
806
+ *
807
+ * // Provider now has funds
808
+ * const balance = await runtime.getBalance(provider);
809
+ * ```
810
+ */
811
+ async releaseEscrow(escrowId: string, _attestationUID?: string): Promise<void> {
812
+ return this.stateManager.withLock(async (state) => {
813
+ const escrow = state.escrows[escrowId];
814
+ if (!escrow) {
815
+ throw new EscrowNotFoundError(escrowId);
816
+ }
817
+
818
+ // Get linked transaction (assume single transaction per escrow for now)
819
+ const txId = escrow.transactions[0];
820
+ const tx = state.transactions[txId];
821
+ if (!tx) {
822
+ throw new TransactionNotFoundError(txId);
823
+ }
824
+
825
+ // Validate state
826
+ if (tx.state !== 'DELIVERED') {
827
+ throw new InvalidStateTransitionError(txId, tx.state, 'SETTLED');
828
+ }
829
+
830
+ const currentTime = state.blockchain.currentTime;
831
+ const blockNumber = state.blockchain.blockNumber;
832
+
833
+ // Enforce dispute window (Issue #3 fix)
834
+ if (tx.completedAt !== null) {
835
+ const disputeWindowEnd = tx.completedAt + tx.disputeWindow;
836
+ if (currentTime < disputeWindowEnd) {
837
+ throw new DisputeWindowActiveError(txId, disputeWindowEnd, currentTime);
838
+ }
839
+ }
840
+
841
+ const amount = BigInt(escrow.balance);
842
+
843
+ // Create or update provider account
844
+ if (!state.accounts[tx.provider]) {
845
+ state.accounts[tx.provider] = {
846
+ address: tx.provider,
847
+ usdcBalance: '0',
848
+ };
849
+ }
850
+
851
+ // Transfer to provider
852
+ const providerBalance = BigInt(state.accounts[tx.provider].usdcBalance);
853
+ state.accounts[tx.provider].usdcBalance = (providerBalance + amount).toString();
854
+
855
+ // Clear escrow balance
856
+ escrow.balance = '0';
857
+ escrow.locked = false;
858
+
859
+ // Transition to SETTLED
860
+ const oldState = tx.state;
861
+ tx.state = 'SETTLED';
862
+ tx.updatedAt = currentTime;
863
+
864
+ // Record events
865
+ const releaseEvent: MockEvent = {
866
+ type: 'EscrowReleased',
867
+ timestamp: currentTime,
868
+ blockNumber,
869
+ data: {
870
+ txId,
871
+ escrowId,
872
+ provider: tx.provider,
873
+ amount: amount.toString(),
874
+ },
875
+ };
876
+
877
+ const transitionEvent: MockEvent = {
878
+ type: 'StateTransitioned',
879
+ timestamp: currentTime,
880
+ blockNumber,
881
+ data: {
882
+ txId,
883
+ oldState,
884
+ newState: 'SETTLED',
885
+ },
886
+ };
887
+
888
+ tx.events.push(releaseEvent, transitionEvent);
889
+ // SECURITY FIX (L-4): Persist events
890
+ this.persistEvent(releaseEvent, state);
891
+ this.persistEvent(transitionEvent, state);
892
+ });
893
+ }
894
+
895
+ /**
896
+ * Gets the balance of an escrow.
897
+ *
898
+ * @param escrowId - Escrow ID
899
+ * @returns Promise resolving to the balance as string
900
+ *
901
+ * @throws {EscrowNotFoundError} If escrow doesn't exist
902
+ */
903
+ async getEscrowBalance(escrowId: string): Promise<string> {
904
+ const state = this.stateManager.loadState();
905
+ const escrow = state.escrows[escrowId];
906
+ if (!escrow) {
907
+ throw new EscrowNotFoundError(escrowId);
908
+ }
909
+ return escrow.balance;
910
+ }
911
+
912
+ // ============================================================================
913
+ // Account Operations
914
+ // ============================================================================
915
+
916
+ /**
917
+ * Gets the USDC balance of an address.
918
+ *
919
+ * @param address - Ethereum address
920
+ * @returns Promise resolving to the balance as string (0 if account doesn't exist)
921
+ */
922
+ async getBalance(address: string): Promise<string> {
923
+ const state = this.stateManager.loadState();
924
+ return state.accounts[address]?.usdcBalance ?? '0';
925
+ }
926
+
927
+ /**
928
+ * Mints USDC tokens to an address (testing only).
929
+ *
930
+ * @param address - Ethereum address to receive tokens
931
+ * @param amount - Amount to mint in USDC wei
932
+ *
933
+ * @example
934
+ * ```typescript
935
+ * // Mint 10,000 USDC (6 decimals)
936
+ * await runtime.mintTokens('0xRequester', '10000000000');
937
+ * ```
938
+ */
939
+ async mintTokens(address: string, amount: string): Promise<void> {
940
+ return this.stateManager.withLock(async (state) => {
941
+ // Create account if doesn't exist
942
+ if (!state.accounts[address]) {
943
+ state.accounts[address] = {
944
+ address,
945
+ usdcBalance: '0',
946
+ };
947
+ }
948
+
949
+ const currentBalance = BigInt(state.accounts[address].usdcBalance);
950
+ const mintAmount = BigInt(amount);
951
+ state.accounts[address].usdcBalance = (currentBalance + mintAmount).toString();
952
+
953
+ // Record event
954
+ const event: MockEvent = {
955
+ type: 'TokensMinted',
956
+ timestamp: state.blockchain.currentTime,
957
+ blockNumber: state.blockchain.blockNumber,
958
+ data: {
959
+ address,
960
+ amount,
961
+ newBalance: state.accounts[address].usdcBalance,
962
+ },
963
+ };
964
+
965
+ // SECURITY FIX (L-4): Persist event
966
+ this.persistEvent(event, state);
967
+ });
968
+ }
969
+
970
+ /**
971
+ * Transfers USDC tokens between addresses.
972
+ *
973
+ * @param from - Sender address
974
+ * @param to - Recipient address
975
+ * @param amount - Amount to transfer in USDC wei
976
+ *
977
+ * @throws {InsufficientBalanceError} If sender has insufficient funds
978
+ *
979
+ * @example
980
+ * ```typescript
981
+ * await runtime.transfer('0xFrom', '0xTo', '1000000');
982
+ * ```
983
+ */
984
+ async transfer(from: string, to: string, amount: string): Promise<void> {
985
+ return this.stateManager.withLock(async (state) => {
986
+ // Check sender balance
987
+ const fromAccount = state.accounts[from];
988
+ const fromBalance = BigInt(fromAccount?.usdcBalance ?? '0');
989
+ const transferAmount = BigInt(amount);
990
+
991
+ if (fromBalance < transferAmount) {
992
+ throw new InsufficientBalanceError(from, amount, fromBalance.toString());
993
+ }
994
+
995
+ // Create receiver account if doesn't exist
996
+ if (!state.accounts[to]) {
997
+ state.accounts[to] = {
998
+ address: to,
999
+ usdcBalance: '0',
1000
+ };
1001
+ }
1002
+
1003
+ // Create sender account if doesn't exist (shouldn't happen but be safe)
1004
+ if (!state.accounts[from]) {
1005
+ state.accounts[from] = {
1006
+ address: from,
1007
+ usdcBalance: '0',
1008
+ };
1009
+ }
1010
+
1011
+ // Transfer
1012
+ state.accounts[from].usdcBalance = (fromBalance - transferAmount).toString();
1013
+ const toBalance = BigInt(state.accounts[to].usdcBalance);
1014
+ state.accounts[to].usdcBalance = (toBalance + transferAmount).toString();
1015
+
1016
+ // Record event
1017
+ const event: MockEvent = {
1018
+ type: 'Transfer',
1019
+ timestamp: state.blockchain.currentTime,
1020
+ blockNumber: state.blockchain.blockNumber,
1021
+ data: {
1022
+ from,
1023
+ to,
1024
+ amount,
1025
+ },
1026
+ };
1027
+
1028
+ // SECURITY FIX (L-4): Persist event
1029
+ this.persistEvent(event, state);
1030
+ });
1031
+ }
1032
+
1033
+ // ============================================================================
1034
+ // State Management
1035
+ // ============================================================================
1036
+
1037
+ /**
1038
+ * Resets the runtime to initial state.
1039
+ *
1040
+ * Clears all transactions, escrows, accounts, and events.
1041
+ */
1042
+ async reset(): Promise<void> {
1043
+ this.stateManager.reset();
1044
+ this.eventLog = [];
1045
+ // Note: reset() on stateManager will also clear persisted events via getDefaultState()
1046
+ }
1047
+
1048
+ /**
1049
+ * Gets the complete mock state.
1050
+ *
1051
+ * @returns Current mock state snapshot
1052
+ */
1053
+ getState(): MockState {
1054
+ return this.stateManager.loadState();
1055
+ }
1056
+
1057
+ // ============================================================================
1058
+ // Private Methods - Time Management
1059
+ // ============================================================================
1060
+
1061
+ /**
1062
+ * Gets the current mock timestamp.
1063
+ */
1064
+ private getCurrentTime(): number {
1065
+ const state = this.stateManager.loadState();
1066
+ return state.blockchain.currentTime;
1067
+ }
1068
+
1069
+ /**
1070
+ * Advances time by specified seconds (with file locking).
1071
+ *
1072
+ * SECURITY FIX: Uses withLock to prevent race conditions when
1073
+ * multiple processes access the state file concurrently.
1074
+ */
1075
+ private async advanceTimeWithLock(seconds: number): Promise<void> {
1076
+ if (seconds < 0) {
1077
+ throw new Error('Cannot advance time by negative amount');
1078
+ }
1079
+
1080
+ return this.stateManager.withLock(async (state) => {
1081
+ const blockTimeSeconds = state.blockchain.blockTime;
1082
+
1083
+ state.blockchain.currentTime += seconds;
1084
+ state.blockchain.blockNumber += Math.floor(seconds / blockTimeSeconds);
1085
+
1086
+ // Record event
1087
+ const event: MockEvent = {
1088
+ type: 'TimeAdvanced',
1089
+ timestamp: state.blockchain.currentTime,
1090
+ blockNumber: state.blockchain.blockNumber,
1091
+ data: {
1092
+ seconds,
1093
+ newTime: state.blockchain.currentTime,
1094
+ newBlock: state.blockchain.blockNumber,
1095
+ },
1096
+ };
1097
+
1098
+ // SECURITY FIX (L-4): Persist event
1099
+ this.persistEvent(event, state);
1100
+ });
1101
+ }
1102
+
1103
+ /**
1104
+ * Advances time by specified blocks (with file locking).
1105
+ *
1106
+ * SECURITY FIX: Uses withLock to prevent race conditions.
1107
+ */
1108
+ private async advanceBlocksWithLock(blocks: number): Promise<void> {
1109
+ if (blocks < 0) {
1110
+ throw new Error('Cannot advance blocks by negative amount');
1111
+ }
1112
+
1113
+ return this.stateManager.withLock(async (state) => {
1114
+ const blockTimeSeconds = state.blockchain.blockTime;
1115
+ const secondsToAdvance = blocks * blockTimeSeconds;
1116
+
1117
+ state.blockchain.blockNumber += blocks;
1118
+ state.blockchain.currentTime += secondsToAdvance;
1119
+
1120
+ // Record event
1121
+ const event: MockEvent = {
1122
+ type: 'BlocksAdvanced',
1123
+ timestamp: state.blockchain.currentTime,
1124
+ blockNumber: state.blockchain.blockNumber,
1125
+ data: {
1126
+ blocks,
1127
+ newTime: state.blockchain.currentTime,
1128
+ newBlock: state.blockchain.blockNumber,
1129
+ },
1130
+ };
1131
+
1132
+ // SECURITY FIX (L-4): Persist event
1133
+ this.persistEvent(event, state);
1134
+ });
1135
+ }
1136
+
1137
+ /**
1138
+ * Sets exact timestamp (must be >= current time) (with file locking).
1139
+ *
1140
+ * SECURITY FIX: Uses withLock to prevent race conditions.
1141
+ */
1142
+ private async setTimeWithLock(timestamp: number): Promise<void> {
1143
+ return this.stateManager.withLock(async (state) => {
1144
+ if (timestamp < state.blockchain.currentTime) {
1145
+ throw new Error(
1146
+ `Cannot move time backwards: ${timestamp} < ${state.blockchain.currentTime}`
1147
+ );
1148
+ }
1149
+
1150
+ const timeDiff = timestamp - state.blockchain.currentTime;
1151
+ const blockTimeSeconds = state.blockchain.blockTime;
1152
+
1153
+ state.blockchain.currentTime = timestamp;
1154
+ state.blockchain.blockNumber += Math.floor(timeDiff / blockTimeSeconds);
1155
+
1156
+ // Record event
1157
+ const event: MockEvent = {
1158
+ type: 'TimeSet',
1159
+ timestamp: state.blockchain.currentTime,
1160
+ blockNumber: state.blockchain.blockNumber,
1161
+ data: {
1162
+ newTime: timestamp,
1163
+ newBlock: state.blockchain.blockNumber,
1164
+ },
1165
+ };
1166
+
1167
+ // SECURITY FIX (L-4): Persist event
1168
+ this.persistEvent(event, state);
1169
+ });
1170
+ }
1171
+
1172
+ // ============================================================================
1173
+ // Private Methods - Event Management
1174
+ // ============================================================================
1175
+
1176
+ /**
1177
+ * Gets events for a specific transaction from both global log and transaction events.
1178
+ */
1179
+ private getEventsByTransaction(txId: string): MockEvent[] {
1180
+ const state = this.stateManager.loadState();
1181
+ const tx = state.transactions[txId];
1182
+
1183
+ if (!tx) {
1184
+ return [];
1185
+ }
1186
+
1187
+ // Return transaction-specific events
1188
+ return [...tx.events];
1189
+ }
1190
+
1191
+ // ============================================================================
1192
+ // Private Methods - ID Generation
1193
+ // ============================================================================
1194
+
1195
+ /**
1196
+ * Generates a unique transaction ID (bytes32 hex string).
1197
+ *
1198
+ * SECURITY FIX (M-4): Now checks for collisions against existing transactions.
1199
+ * Uses cryptographically secure random bytes (32 bytes = 256 bits of entropy).
1200
+ *
1201
+ * @param state - Current mock state to check for collisions
1202
+ * @returns Unique transaction ID
1203
+ */
1204
+ private generateTransactionIdWithCollisionCheck(state: MockState): string {
1205
+ const maxAttempts = 10;
1206
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1207
+ const txId = `0x${crypto.randomBytes(32).toString('hex')}`;
1208
+
1209
+ // Check for collision (extremely unlikely with 256 bits of randomness)
1210
+ if (!state.transactions[txId]) {
1211
+ return txId;
1212
+ }
1213
+ }
1214
+
1215
+ // This should never happen with 256-bit IDs, but fail safely
1216
+ throw new Error(
1217
+ 'Failed to generate unique transaction ID after multiple attempts. ' +
1218
+ 'This indicates a critical system issue.'
1219
+ );
1220
+ }
1221
+
1222
+ /**
1223
+ * Generates a unique transaction ID (bytes32 hex string).
1224
+ *
1225
+ * Note: This version is used when state is not available.
1226
+ * Prefer generateTransactionIdWithCollisionCheck when possible.
1227
+ */
1228
+ private generateTransactionId(): string {
1229
+ return `0x${crypto.randomBytes(32).toString('hex')}`;
1230
+ }
1231
+
1232
+ /**
1233
+ * Generates a unique escrow ID with improved randomness.
1234
+ *
1235
+ * SECURITY FIX (L-5): Uses 16 bytes (128 bits) of cryptographic randomness
1236
+ * instead of just 4 bytes. Removes timestamp to prevent predictability.
1237
+ *
1238
+ * @returns Unique escrow ID
1239
+ */
1240
+ private generateEscrowId(): string {
1241
+ // Use 16 bytes of cryptographic randomness (128 bits of entropy)
1242
+ return `escrow-${crypto.randomBytes(16).toString('hex')}`;
1243
+ }
1244
+ }