@originals/sdk 1.1.0

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 (403) hide show
  1. package/.eslintrc.json +33 -0
  2. package/.turbo/turbo-build.log +1 -0
  3. package/.turbo/turbo-test.log +68353 -0
  4. package/dist/adapters/FeeOracleMock.d.ts +6 -0
  5. package/dist/adapters/FeeOracleMock.js +8 -0
  6. package/dist/adapters/index.d.ts +4 -0
  7. package/dist/adapters/index.js +4 -0
  8. package/dist/adapters/providers/OrdHttpProvider.d.ts +56 -0
  9. package/dist/adapters/providers/OrdHttpProvider.js +110 -0
  10. package/dist/adapters/providers/OrdMockProvider.d.ts +70 -0
  11. package/dist/adapters/providers/OrdMockProvider.js +75 -0
  12. package/dist/adapters/types.d.ts +71 -0
  13. package/dist/adapters/types.js +1 -0
  14. package/dist/bitcoin/BitcoinManager.d.ts +15 -0
  15. package/dist/bitcoin/BitcoinManager.js +262 -0
  16. package/dist/bitcoin/BroadcastClient.d.ts +30 -0
  17. package/dist/bitcoin/BroadcastClient.js +35 -0
  18. package/dist/bitcoin/OrdinalsClient.d.ts +21 -0
  19. package/dist/bitcoin/OrdinalsClient.js +105 -0
  20. package/dist/bitcoin/PSBTBuilder.d.ts +24 -0
  21. package/dist/bitcoin/PSBTBuilder.js +80 -0
  22. package/dist/bitcoin/fee-calculation.d.ts +14 -0
  23. package/dist/bitcoin/fee-calculation.js +31 -0
  24. package/dist/bitcoin/providers/OrdNodeProvider.d.ts +38 -0
  25. package/dist/bitcoin/providers/OrdNodeProvider.js +67 -0
  26. package/dist/bitcoin/providers/OrdinalsProvider.d.ts +33 -0
  27. package/dist/bitcoin/providers/OrdinalsProvider.js +50 -0
  28. package/dist/bitcoin/providers/types.d.ts +63 -0
  29. package/dist/bitcoin/providers/types.js +1 -0
  30. package/dist/bitcoin/transactions/commit.d.ts +89 -0
  31. package/dist/bitcoin/transactions/commit.js +311 -0
  32. package/dist/bitcoin/transactions/index.d.ts +7 -0
  33. package/dist/bitcoin/transactions/index.js +8 -0
  34. package/dist/bitcoin/transfer.d.ts +9 -0
  35. package/dist/bitcoin/transfer.js +26 -0
  36. package/dist/bitcoin/utxo-selection.d.ts +78 -0
  37. package/dist/bitcoin/utxo-selection.js +237 -0
  38. package/dist/bitcoin/utxo.d.ts +26 -0
  39. package/dist/bitcoin/utxo.js +78 -0
  40. package/dist/contexts/credentials-v1.json +195 -0
  41. package/dist/contexts/credentials-v2-examples.json +5 -0
  42. package/dist/contexts/credentials-v2.json +301 -0
  43. package/dist/contexts/credentials.json +195 -0
  44. package/dist/contexts/data-integrity-v2.json +81 -0
  45. package/dist/contexts/dids.json +57 -0
  46. package/dist/contexts/ed255192020.json +93 -0
  47. package/dist/contexts/ordinals-plus.json +23 -0
  48. package/dist/contexts/originals.json +22 -0
  49. package/dist/core/OriginalsSDK.d.ts +158 -0
  50. package/dist/core/OriginalsSDK.js +274 -0
  51. package/dist/crypto/Multikey.d.ts +30 -0
  52. package/dist/crypto/Multikey.js +149 -0
  53. package/dist/crypto/Signer.d.ts +21 -0
  54. package/dist/crypto/Signer.js +196 -0
  55. package/dist/crypto/noble-init.d.ts +18 -0
  56. package/dist/crypto/noble-init.js +106 -0
  57. package/dist/did/BtcoDidResolver.d.ts +57 -0
  58. package/dist/did/BtcoDidResolver.js +166 -0
  59. package/dist/did/DIDManager.d.ts +101 -0
  60. package/dist/did/DIDManager.js +493 -0
  61. package/dist/did/Ed25519Verifier.d.ts +30 -0
  62. package/dist/did/Ed25519Verifier.js +59 -0
  63. package/dist/did/KeyManager.d.ts +17 -0
  64. package/dist/did/KeyManager.js +207 -0
  65. package/dist/did/WebVHManager.d.ts +100 -0
  66. package/dist/did/WebVHManager.js +304 -0
  67. package/dist/did/createBtcoDidDocument.d.ts +10 -0
  68. package/dist/did/createBtcoDidDocument.js +42 -0
  69. package/dist/did/providers/OrdinalsClientProviderAdapter.d.ts +23 -0
  70. package/dist/did/providers/OrdinalsClientProviderAdapter.js +51 -0
  71. package/dist/events/EventEmitter.d.ts +115 -0
  72. package/dist/events/EventEmitter.js +198 -0
  73. package/dist/events/index.d.ts +7 -0
  74. package/dist/events/index.js +6 -0
  75. package/dist/events/types.d.ts +286 -0
  76. package/dist/events/types.js +9 -0
  77. package/dist/examples/basic-usage.d.ts +3 -0
  78. package/dist/examples/basic-usage.js +62 -0
  79. package/dist/examples/run.d.ts +1 -0
  80. package/dist/examples/run.js +4 -0
  81. package/dist/index.d.ts +39 -0
  82. package/dist/index.js +47 -0
  83. package/dist/lifecycle/BatchOperations.d.ts +147 -0
  84. package/dist/lifecycle/BatchOperations.js +251 -0
  85. package/dist/lifecycle/LifecycleManager.d.ts +116 -0
  86. package/dist/lifecycle/LifecycleManager.js +971 -0
  87. package/dist/lifecycle/OriginalsAsset.d.ts +164 -0
  88. package/dist/lifecycle/OriginalsAsset.js +380 -0
  89. package/dist/lifecycle/ProvenanceQuery.d.ts +126 -0
  90. package/dist/lifecycle/ProvenanceQuery.js +220 -0
  91. package/dist/lifecycle/ResourceVersioning.d.ts +73 -0
  92. package/dist/lifecycle/ResourceVersioning.js +127 -0
  93. package/dist/migration/MigrationManager.d.ts +86 -0
  94. package/dist/migration/MigrationManager.js +412 -0
  95. package/dist/migration/audit/AuditLogger.d.ts +51 -0
  96. package/dist/migration/audit/AuditLogger.js +156 -0
  97. package/dist/migration/checkpoint/CheckpointManager.d.ts +31 -0
  98. package/dist/migration/checkpoint/CheckpointManager.js +96 -0
  99. package/dist/migration/checkpoint/CheckpointStorage.d.ts +26 -0
  100. package/dist/migration/checkpoint/CheckpointStorage.js +89 -0
  101. package/dist/migration/index.d.ts +22 -0
  102. package/dist/migration/index.js +27 -0
  103. package/dist/migration/operations/BaseMigration.d.ts +48 -0
  104. package/dist/migration/operations/BaseMigration.js +83 -0
  105. package/dist/migration/operations/PeerToBtcoMigration.d.ts +25 -0
  106. package/dist/migration/operations/PeerToBtcoMigration.js +67 -0
  107. package/dist/migration/operations/PeerToWebvhMigration.d.ts +19 -0
  108. package/dist/migration/operations/PeerToWebvhMigration.js +46 -0
  109. package/dist/migration/operations/WebvhToBtcoMigration.d.ts +25 -0
  110. package/dist/migration/operations/WebvhToBtcoMigration.js +67 -0
  111. package/dist/migration/rollback/RollbackManager.d.ts +29 -0
  112. package/dist/migration/rollback/RollbackManager.js +146 -0
  113. package/dist/migration/state/StateMachine.d.ts +25 -0
  114. package/dist/migration/state/StateMachine.js +76 -0
  115. package/dist/migration/state/StateTracker.d.ts +36 -0
  116. package/dist/migration/state/StateTracker.js +123 -0
  117. package/dist/migration/types.d.ts +306 -0
  118. package/dist/migration/types.js +33 -0
  119. package/dist/migration/validation/BitcoinValidator.d.ts +13 -0
  120. package/dist/migration/validation/BitcoinValidator.js +83 -0
  121. package/dist/migration/validation/CredentialValidator.d.ts +13 -0
  122. package/dist/migration/validation/CredentialValidator.js +46 -0
  123. package/dist/migration/validation/DIDCompatibilityValidator.d.ts +16 -0
  124. package/dist/migration/validation/DIDCompatibilityValidator.js +127 -0
  125. package/dist/migration/validation/LifecycleValidator.d.ts +10 -0
  126. package/dist/migration/validation/LifecycleValidator.js +52 -0
  127. package/dist/migration/validation/StorageValidator.d.ts +10 -0
  128. package/dist/migration/validation/StorageValidator.js +65 -0
  129. package/dist/migration/validation/ValidationPipeline.d.ts +29 -0
  130. package/dist/migration/validation/ValidationPipeline.js +180 -0
  131. package/dist/storage/LocalStorageAdapter.d.ts +11 -0
  132. package/dist/storage/LocalStorageAdapter.js +53 -0
  133. package/dist/storage/MemoryStorageAdapter.d.ts +6 -0
  134. package/dist/storage/MemoryStorageAdapter.js +21 -0
  135. package/dist/storage/StorageAdapter.d.ts +16 -0
  136. package/dist/storage/StorageAdapter.js +1 -0
  137. package/dist/storage/index.d.ts +2 -0
  138. package/dist/storage/index.js +2 -0
  139. package/dist/types/bitcoin.d.ts +84 -0
  140. package/dist/types/bitcoin.js +1 -0
  141. package/dist/types/common.d.ts +82 -0
  142. package/dist/types/common.js +1 -0
  143. package/dist/types/credentials.d.ts +75 -0
  144. package/dist/types/credentials.js +1 -0
  145. package/dist/types/did.d.ts +26 -0
  146. package/dist/types/did.js +1 -0
  147. package/dist/types/index.d.ts +5 -0
  148. package/dist/types/index.js +5 -0
  149. package/dist/types/network.d.ts +78 -0
  150. package/dist/types/network.js +145 -0
  151. package/dist/utils/EventLogger.d.ts +71 -0
  152. package/dist/utils/EventLogger.js +232 -0
  153. package/dist/utils/Logger.d.ts +106 -0
  154. package/dist/utils/Logger.js +257 -0
  155. package/dist/utils/MetricsCollector.d.ts +110 -0
  156. package/dist/utils/MetricsCollector.js +264 -0
  157. package/dist/utils/bitcoin-address.d.ts +38 -0
  158. package/dist/utils/bitcoin-address.js +113 -0
  159. package/dist/utils/cbor.d.ts +2 -0
  160. package/dist/utils/cbor.js +9 -0
  161. package/dist/utils/encoding.d.ts +37 -0
  162. package/dist/utils/encoding.js +120 -0
  163. package/dist/utils/hash.d.ts +1 -0
  164. package/dist/utils/hash.js +5 -0
  165. package/dist/utils/retry.d.ts +10 -0
  166. package/dist/utils/retry.js +35 -0
  167. package/dist/utils/satoshi-validation.d.ts +60 -0
  168. package/dist/utils/satoshi-validation.js +156 -0
  169. package/dist/utils/serialization.d.ts +14 -0
  170. package/dist/utils/serialization.js +76 -0
  171. package/dist/utils/telemetry.d.ts +17 -0
  172. package/dist/utils/telemetry.js +24 -0
  173. package/dist/utils/validation.d.ts +5 -0
  174. package/dist/utils/validation.js +98 -0
  175. package/dist/vc/CredentialManager.d.ts +22 -0
  176. package/dist/vc/CredentialManager.js +227 -0
  177. package/dist/vc/Issuer.d.ts +27 -0
  178. package/dist/vc/Issuer.js +70 -0
  179. package/dist/vc/Verifier.d.ts +16 -0
  180. package/dist/vc/Verifier.js +50 -0
  181. package/dist/vc/cryptosuites/bbs.d.ts +44 -0
  182. package/dist/vc/cryptosuites/bbs.js +213 -0
  183. package/dist/vc/cryptosuites/bbsSimple.d.ts +9 -0
  184. package/dist/vc/cryptosuites/bbsSimple.js +12 -0
  185. package/dist/vc/cryptosuites/eddsa.d.ts +30 -0
  186. package/dist/vc/cryptosuites/eddsa.js +81 -0
  187. package/dist/vc/documentLoader.d.ts +16 -0
  188. package/dist/vc/documentLoader.js +59 -0
  189. package/dist/vc/proofs/data-integrity.d.ts +21 -0
  190. package/dist/vc/proofs/data-integrity.js +15 -0
  191. package/dist/vc/utils/jsonld.d.ts +2 -0
  192. package/dist/vc/utils/jsonld.js +15 -0
  193. package/package.json +79 -0
  194. package/src/adapters/FeeOracleMock.ts +9 -0
  195. package/src/adapters/index.ts +5 -0
  196. package/src/adapters/providers/OrdHttpProvider.ts +126 -0
  197. package/src/adapters/providers/OrdMockProvider.ts +101 -0
  198. package/src/adapters/types.ts +66 -0
  199. package/src/bitcoin/BitcoinManager.ts +330 -0
  200. package/src/bitcoin/BroadcastClient.ts +54 -0
  201. package/src/bitcoin/OrdinalsClient.ts +119 -0
  202. package/src/bitcoin/PSBTBuilder.ts +106 -0
  203. package/src/bitcoin/fee-calculation.ts +38 -0
  204. package/src/bitcoin/providers/OrdNodeProvider.ts +92 -0
  205. package/src/bitcoin/providers/OrdinalsProvider.ts +56 -0
  206. package/src/bitcoin/providers/types.ts +59 -0
  207. package/src/bitcoin/transactions/commit.ts +465 -0
  208. package/src/bitcoin/transactions/index.ts +13 -0
  209. package/src/bitcoin/transfer.ts +43 -0
  210. package/src/bitcoin/utxo-selection.ts +322 -0
  211. package/src/bitcoin/utxo.ts +113 -0
  212. package/src/contexts/credentials-v1.json +237 -0
  213. package/src/contexts/credentials-v2-examples.json +5 -0
  214. package/src/contexts/credentials-v2.json +340 -0
  215. package/src/contexts/credentials.json +237 -0
  216. package/src/contexts/data-integrity-v2.json +81 -0
  217. package/src/contexts/dids.json +58 -0
  218. package/src/contexts/ed255192020.json +93 -0
  219. package/src/contexts/ordinals-plus.json +23 -0
  220. package/src/contexts/originals.json +22 -0
  221. package/src/core/OriginalsSDK.ts +416 -0
  222. package/src/crypto/Multikey.ts +194 -0
  223. package/src/crypto/Signer.ts +254 -0
  224. package/src/crypto/noble-init.ts +121 -0
  225. package/src/did/BtcoDidResolver.ts +227 -0
  226. package/src/did/DIDManager.ts +694 -0
  227. package/src/did/Ed25519Verifier.ts +68 -0
  228. package/src/did/KeyManager.ts +236 -0
  229. package/src/did/WebVHManager.ts +489 -0
  230. package/src/did/createBtcoDidDocument.ts +59 -0
  231. package/src/did/providers/OrdinalsClientProviderAdapter.ts +68 -0
  232. package/src/events/EventEmitter.ts +222 -0
  233. package/src/events/index.ts +19 -0
  234. package/src/events/types.ts +331 -0
  235. package/src/examples/basic-usage.ts +78 -0
  236. package/src/examples/run.ts +5 -0
  237. package/src/index.ts +84 -0
  238. package/src/lifecycle/BatchOperations.ts +373 -0
  239. package/src/lifecycle/LifecycleManager.ts +1218 -0
  240. package/src/lifecycle/OriginalsAsset.ts +524 -0
  241. package/src/lifecycle/ProvenanceQuery.ts +280 -0
  242. package/src/lifecycle/ResourceVersioning.ts +163 -0
  243. package/src/migration/MigrationManager.ts +527 -0
  244. package/src/migration/audit/AuditLogger.ts +176 -0
  245. package/src/migration/checkpoint/CheckpointManager.ts +112 -0
  246. package/src/migration/checkpoint/CheckpointStorage.ts +101 -0
  247. package/src/migration/index.ts +33 -0
  248. package/src/migration/operations/BaseMigration.ts +126 -0
  249. package/src/migration/operations/PeerToBtcoMigration.ts +105 -0
  250. package/src/migration/operations/PeerToWebvhMigration.ts +62 -0
  251. package/src/migration/operations/WebvhToBtcoMigration.ts +105 -0
  252. package/src/migration/rollback/RollbackManager.ts +170 -0
  253. package/src/migration/state/StateMachine.ts +92 -0
  254. package/src/migration/state/StateTracker.ts +156 -0
  255. package/src/migration/types.ts +344 -0
  256. package/src/migration/validation/BitcoinValidator.ts +107 -0
  257. package/src/migration/validation/CredentialValidator.ts +62 -0
  258. package/src/migration/validation/DIDCompatibilityValidator.ts +151 -0
  259. package/src/migration/validation/LifecycleValidator.ts +64 -0
  260. package/src/migration/validation/StorageValidator.ts +79 -0
  261. package/src/migration/validation/ValidationPipeline.ts +213 -0
  262. package/src/storage/LocalStorageAdapter.ts +61 -0
  263. package/src/storage/MemoryStorageAdapter.ts +29 -0
  264. package/src/storage/StorageAdapter.ts +25 -0
  265. package/src/storage/index.ts +3 -0
  266. package/src/types/bitcoin.ts +98 -0
  267. package/src/types/common.ts +92 -0
  268. package/src/types/credentials.ts +88 -0
  269. package/src/types/did.ts +31 -0
  270. package/src/types/external-shims.d.ts +53 -0
  271. package/src/types/index.ts +7 -0
  272. package/src/types/network.ts +175 -0
  273. package/src/utils/EventLogger.ts +298 -0
  274. package/src/utils/Logger.ts +322 -0
  275. package/src/utils/MetricsCollector.ts +358 -0
  276. package/src/utils/bitcoin-address.ts +130 -0
  277. package/src/utils/cbor.ts +12 -0
  278. package/src/utils/encoding.ts +127 -0
  279. package/src/utils/hash.ts +6 -0
  280. package/src/utils/retry.ts +46 -0
  281. package/src/utils/satoshi-validation.ts +196 -0
  282. package/src/utils/serialization.ts +96 -0
  283. package/src/utils/telemetry.ts +40 -0
  284. package/src/utils/validation.ts +119 -0
  285. package/src/vc/CredentialManager.ts +273 -0
  286. package/src/vc/Issuer.ts +100 -0
  287. package/src/vc/Verifier.ts +47 -0
  288. package/src/vc/cryptosuites/bbs.ts +253 -0
  289. package/src/vc/cryptosuites/bbsSimple.ts +21 -0
  290. package/src/vc/cryptosuites/eddsa.ts +99 -0
  291. package/src/vc/documentLoader.ts +67 -0
  292. package/src/vc/proofs/data-integrity.ts +33 -0
  293. package/src/vc/utils/jsonld.ts +18 -0
  294. package/test/logs/did_webvh_QmQsRNhXxPSCSeLjpbKYcNMZj8b1kBQAoC6cZmkFAgmpHt_example_com.jsonl +1 -0
  295. package/test/logs/did_webvh_QmSQkpD58qxcqMWHYcEmDUn3wk7hHvJwzYTrZmhh6zjPQ8_example_com_users_alice123_profile.jsonl +1 -0
  296. package/test/logs/did_webvh_QmTMda6VW3cUPdKk5Yc3onnv1vdgEumvWWdP2noAYFSjeG_example_com.jsonl +1 -0
  297. package/test/logs/did_webvh_QmTkb8KnCYcsnKKDCY4eUQuKQdKJLrCinvhw13v3zETxpE_example_com_users_etc_passwd.jsonl +1 -0
  298. package/test/logs/did_webvh_QmTn9FdCfpXFDrxHH52pwB4iNrDFVvNDjJ5FQTcDbmM3Fg_example_com.jsonl +1 -0
  299. package/test/logs/did_webvh_QmUCQUi1xjtJjnSQ1XJZgKqcWgErx1v7E2dz4DAPraAyJP_example_com_etc_passwd.jsonl +1 -0
  300. package/test/logs/did_webvh_QmUENQJCDKBJVRS5BkL6zjaUvcRjkb9xHmy7foCgRjmv3W_example_com.jsonl +1 -0
  301. package/test/logs/did_webvh_QmUPdGyjYBEnQ3aQUkmqyyBKTyjvCP5RZQGiaEDeTtf6dc_example_com.jsonl +1 -0
  302. package/test/logs/did_webvh_QmUoHTuHMWzQM29ZFrE9VLtMxkZ5u869yqee8LwcCLN39M_example_com.jsonl +1 -0
  303. package/test/logs/did_webvh_QmUrnms8G65ggVKsr9oQeWrLUBuGChwQPPb2LCFvaoNxaw_example_com_users_alice.jsonl +1 -0
  304. package/test/logs/did_webvh_QmUwiw3eSXdHG1hPvoAGu3cuK5jF4aXRYDLBAjPXfv1qzb_example_com_level1_level2.jsonl +1 -0
  305. package/test/logs/did_webvh_QmW7bzKh6yFEKNAtmVsrPGvvsMHTUQdzJSNsTZkbuGFpbj_example_com_secret.jsonl +1 -0
  306. package/test/logs/did_webvh_QmXbFTFBBJ8zpjdz9WE1DNN44A2wprFmdvAubjSffeyoAG_example_com.jsonl +1 -0
  307. package/test/logs/did_webvh_QmXyVXFPCTffGb2mTUFDeMCsScjnpLWkyUkVkB6q6QoeBf_example_com_C_Windows_System32.jsonl +1 -0
  308. package/test/logs/did_webvh_QmZK9B81gxZtvo5fYHYKDtKt8zZfZZPhmCMhbujBJuRRzE_example_com_etc_passwd.jsonl +1 -0
  309. package/test/logs/did_webvh_QmbNLCVSdXSVLrwFBvCBQPAabjtRb1SGHjkGVyw3QUbfBL_example_com_users_etc_passwd.jsonl +1 -0
  310. package/test/logs/did_webvh_QmbeaicmGW3Q7Yzbqmftc8a9jLBngokveb5A2KVKfVGZRb_example_com_my_org_user_name_test_123.jsonl +1 -0
  311. package/test/logs/did_webvh_Qmdv7c7AjUreUfoKyvkN2UpAWTozxKsv99srQetPJMJEnp_example_com_users_etc_passwd.jsonl +1 -0
  312. package/test/logs/did_webvh_QmeioWY3uypYLkYpCXe9eCYnn4xBVruP9C1d79azMrTEHG_example_com.jsonl +1 -0
  313. package/test/logs/did_webvh_Qmf4QH5dsA6Ecr5HJ6KaJL9uJRyY8RxrQdqoRCM25DzvPi_example_com_users_alice.jsonl +1 -0
  314. package/tests/__mocks__/bbs-signatures.js +17 -0
  315. package/tests/__mocks__/mf-base58.js +24 -0
  316. package/tests/e2e/README.md +97 -0
  317. package/tests/e2e/example.spec.ts +78 -0
  318. package/tests/fixtures/did-documents.ts +247 -0
  319. package/tests/index.test.ts +21 -0
  320. package/tests/integration/BatchOperations.test.ts +531 -0
  321. package/tests/integration/CompleteLifecycle.e2e.test.ts +735 -0
  322. package/tests/integration/CredentialManager.test.ts +42 -0
  323. package/tests/integration/DIDManager.test.ts +41 -0
  324. package/tests/integration/DidPeerToWebVhFlow.test.ts +351 -0
  325. package/tests/integration/Events.test.ts +435 -0
  326. package/tests/integration/Lifecycle.transfer.btco.integration.test.ts +25 -0
  327. package/tests/integration/LifecycleManager.test.ts +21 -0
  328. package/tests/integration/MultikeyFlow.test.ts +52 -0
  329. package/tests/integration/TelemetryIntegration.test.ts +395 -0
  330. package/tests/integration/WebVhPublish.test.ts +48 -0
  331. package/tests/integration/migration/peer-to-webvh.test.ts +172 -0
  332. package/tests/manual/test-commit-creation.ts +323 -0
  333. package/tests/mocks/MockKeyStore.ts +38 -0
  334. package/tests/mocks/adapters/MemoryStorageAdapter.ts +24 -0
  335. package/tests/mocks/adapters/MockFeeOracle.ts +11 -0
  336. package/tests/mocks/adapters/MockOrdinalsProvider.ts +76 -0
  337. package/tests/mocks/adapters/OrdMockProvider.test.ts +176 -0
  338. package/tests/mocks/adapters/index.ts +6 -0
  339. package/tests/performance/BatchOperations.perf.test.ts +403 -0
  340. package/tests/performance/logging.perf.test.ts +336 -0
  341. package/tests/sdk.test.ts +43 -0
  342. package/tests/security/bitcoin-penetration-tests.test.ts +622 -0
  343. package/tests/setup.bun.ts +69 -0
  344. package/tests/setup.jest.ts +23 -0
  345. package/tests/stress/batch-operations-stress.test.ts +571 -0
  346. package/tests/unit/adapters/FeeOracleMock.test.ts +40 -0
  347. package/tests/unit/bitcoin/BitcoinManager.test.ts +293 -0
  348. package/tests/unit/bitcoin/BroadcastClient.test.ts +52 -0
  349. package/tests/unit/bitcoin/OrdNodeProvider.test.ts +53 -0
  350. package/tests/unit/bitcoin/OrdinalsClient.test.ts +381 -0
  351. package/tests/unit/bitcoin/OrdinalsClientProvider.test.ts +102 -0
  352. package/tests/unit/bitcoin/PSBTBuilder.test.ts +84 -0
  353. package/tests/unit/bitcoin/fee-calculation.test.ts +261 -0
  354. package/tests/unit/bitcoin/transactions/commit.test.ts +649 -0
  355. package/tests/unit/bitcoin/transfer.test.ts +31 -0
  356. package/tests/unit/bitcoin/utxo-selection-new.test.ts +502 -0
  357. package/tests/unit/bitcoin/utxo.more.test.ts +39 -0
  358. package/tests/unit/bitcoin/utxo.selection.test.ts +38 -0
  359. package/tests/unit/core/OriginalsSDK.test.ts +152 -0
  360. package/tests/unit/crypto/Multikey.test.ts +206 -0
  361. package/tests/unit/crypto/Signer.test.ts +408 -0
  362. package/tests/unit/did/BtcoDidResolver.test.ts +611 -0
  363. package/tests/unit/did/DIDManager.more.test.ts +43 -0
  364. package/tests/unit/did/DIDManager.test.ts +185 -0
  365. package/tests/unit/did/Ed25519Verifier.test.ts +160 -0
  366. package/tests/unit/did/KeyManager.test.ts +452 -0
  367. package/tests/unit/did/OrdinalsClientProviderAdapter.test.ts +45 -0
  368. package/tests/unit/did/WebVHManager.test.ts +435 -0
  369. package/tests/unit/did/createBtcoDidDocument.test.ts +67 -0
  370. package/tests/unit/did/providers/OrdinalsClientProviderAdapter.test.ts +159 -0
  371. package/tests/unit/events/EventEmitter.test.ts +407 -0
  372. package/tests/unit/lifecycle/BatchOperations.test.ts +527 -0
  373. package/tests/unit/lifecycle/LifecycleManager.keymanagement.test.ts +312 -0
  374. package/tests/unit/lifecycle/LifecycleManager.prov.test.ts +18 -0
  375. package/tests/unit/lifecycle/LifecycleManager.test.ts +213 -0
  376. package/tests/unit/lifecycle/LifecycleManager.transfer.unit.test.ts +30 -0
  377. package/tests/unit/lifecycle/OriginalsAsset.test.ts +176 -0
  378. package/tests/unit/lifecycle/ProvenanceQuery.test.ts +577 -0
  379. package/tests/unit/lifecycle/ResourceVersioning.test.ts +651 -0
  380. package/tests/unit/storage/MemoryStorageAdapter.test.ts +93 -0
  381. package/tests/unit/types/network.test.ts +255 -0
  382. package/tests/unit/utils/EventIntegration.test.ts +384 -0
  383. package/tests/unit/utils/Logger.test.ts +473 -0
  384. package/tests/unit/utils/MetricsCollector.test.ts +358 -0
  385. package/tests/unit/utils/bitcoin-address.test.ts +250 -0
  386. package/tests/unit/utils/cbor.test.ts +35 -0
  387. package/tests/unit/utils/encoding.test.ts +318 -0
  388. package/tests/unit/utils/hash.test.ts +12 -0
  389. package/tests/unit/utils/retry.test.ts +100 -0
  390. package/tests/unit/utils/satoshi-validation.test.ts +354 -0
  391. package/tests/unit/utils/serialization.test.ts +124 -0
  392. package/tests/unit/utils/telemetry.test.ts +52 -0
  393. package/tests/unit/utils/validation.test.ts +141 -0
  394. package/tests/unit/vc/CredentialManager.test.ts +487 -0
  395. package/tests/unit/vc/Issuer.test.ts +107 -0
  396. package/tests/unit/vc/Verifier.test.ts +525 -0
  397. package/tests/unit/vc/bbs.test.ts +282 -0
  398. package/tests/unit/vc/cryptosuites/eddsa.test.ts +398 -0
  399. package/tests/unit/vc/documentLoader.test.ts +121 -0
  400. package/tests/unit/vc/proofs/data-integrity.test.ts +24 -0
  401. package/tsconfig.json +32 -0
  402. package/tsconfig.test.json +15 -0
  403. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,649 @@
1
+ /**
2
+ * Tests for commit transaction creation
3
+ */
4
+ import { describe, test, expect, beforeEach } from 'bun:test';
5
+ import {
6
+ createCommitTransaction,
7
+ type CommitTransactionParams,
8
+ type CommitTransactionResult
9
+ } from '../../../../src/bitcoin/transactions/commit.js';
10
+ import type { Utxo } from '../../../../src/types/bitcoin.js';
11
+
12
+ // Helper to create test UTXOs
13
+ const createUtxo = (value: number, index: number = 0, network: 'mainnet' | 'testnet' | 'signet' | 'regtest' = 'mainnet'): Utxo => {
14
+ const addressMap = {
15
+ mainnet: 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4',
16
+ testnet: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx',
17
+ signet: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx',
18
+ regtest: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx' // Use testnet format for regtest compatibility with @scure/btc-signer
19
+ };
20
+
21
+ return {
22
+ txid: `${'a'.repeat(62)}${index.toString().padStart(2, '0')}`,
23
+ vout: index,
24
+ value,
25
+ scriptPubKey: '0014' + 'b'.repeat(40), // Mock P2WPKH scriptPubKey
26
+ address: addressMap[network]
27
+ };
28
+ };
29
+
30
+ // Helper to create basic commit params
31
+ const createCommitParams = (overrides: Partial<CommitTransactionParams> = {}): CommitTransactionParams => {
32
+ const network = overrides.network || 'mainnet';
33
+ const addressMap = {
34
+ mainnet: 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4',
35
+ testnet: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx',
36
+ signet: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx',
37
+ regtest: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx' // Use testnet format for regtest compatibility with @scure/btc-signer
38
+ };
39
+
40
+ return {
41
+ content: Buffer.from('Hello Ordinals'),
42
+ contentType: 'text/plain',
43
+ utxos: overrides.utxos || [createUtxo(10000, 0, network)],
44
+ changeAddress: addressMap[network],
45
+ feeRate: 10,
46
+ network,
47
+ ...overrides
48
+ };
49
+ };
50
+
51
+ describe('createCommitTransaction', () => {
52
+ describe('Basic Functionality', () => {
53
+ test('creates commit transaction with valid inputs', async () => {
54
+ const params = createCommitParams();
55
+ const result = await createCommitTransaction(params);
56
+
57
+ expect(result).toBeDefined();
58
+ expect(result.commitAddress).toBeDefined();
59
+ expect(result.commitPsbtBase64).toBeDefined();
60
+ expect(result.commitPsbt).toBeDefined();
61
+ expect(result.selectedUtxos).toHaveLength(1);
62
+ expect(result.fees.commit).toBeGreaterThan(0);
63
+ });
64
+
65
+ test('generates valid P2TR commit address', async () => {
66
+ const params = createCommitParams();
67
+ const result = await createCommitTransaction(params);
68
+
69
+ // P2TR addresses start with bc1p for mainnet
70
+ expect(result.commitAddress).toMatch(/^bc1p[a-z0-9]{58}$/);
71
+ });
72
+
73
+ test('generates reveal keypair', async () => {
74
+ const params = createCommitParams();
75
+ const result = await createCommitTransaction(params);
76
+
77
+ expect(result.revealPrivateKey).toBeDefined();
78
+ expect(result.revealPublicKey).toBeDefined();
79
+ expect(result.revealPrivateKey).toHaveLength(64); // 32 bytes in hex
80
+ expect(result.revealPublicKey).toHaveLength(64); // 32 bytes x-only pubkey in hex
81
+ });
82
+
83
+ test('includes inscription script data', async () => {
84
+ const params = createCommitParams();
85
+ const result = await createCommitTransaction(params);
86
+
87
+ expect(result.inscriptionScript).toBeDefined();
88
+ expect(result.inscriptionScript.script).toBeInstanceOf(Uint8Array);
89
+ expect(result.inscriptionScript.controlBlock).toBeInstanceOf(Uint8Array);
90
+ expect(result.inscriptionScript.leafVersion).toBe(0xc0);
91
+ });
92
+
93
+ test('returns correct commit amount', async () => {
94
+ const params = createCommitParams({ minimumCommitAmount: 1000 });
95
+ const result = await createCommitTransaction(params);
96
+
97
+ expect(result.commitAmount).toBeGreaterThanOrEqual(1000);
98
+ expect(result.commitAmount).toBeGreaterThanOrEqual(546); // Dust limit
99
+ });
100
+ });
101
+
102
+ describe('UTXO Selection', () => {
103
+ test('selects single UTXO when sufficient', async () => {
104
+ const params = createCommitParams({
105
+ utxos: [createUtxo(10000, 0)]
106
+ });
107
+ const result = await createCommitTransaction(params);
108
+
109
+ expect(result.selectedUtxos).toHaveLength(1);
110
+ expect(result.selectedUtxos[0].value).toBe(10000);
111
+ });
112
+
113
+ test('selects multiple UTXOs when needed', async () => {
114
+ const params = createCommitParams({
115
+ utxos: [
116
+ createUtxo(2000, 0),
117
+ createUtxo(2000, 1),
118
+ createUtxo(2000, 2)
119
+ ],
120
+ minimumCommitAmount: 2000
121
+ });
122
+ const result = await createCommitTransaction(params);
123
+
124
+ expect(result.selectedUtxos.length).toBeGreaterThan(1);
125
+ const totalValue = result.selectedUtxos.reduce((sum, utxo) => sum + utxo.value, 0);
126
+ expect(totalValue).toBeGreaterThanOrEqual(2000 + result.fees.commit);
127
+ });
128
+
129
+ test('throws when insufficient funds', async () => {
130
+ const params = createCommitParams({
131
+ utxos: [createUtxo(100, 0)], // Not enough for commit + fees
132
+ minimumCommitAmount: 546
133
+ });
134
+
135
+ await expect(createCommitTransaction(params)).rejects.toThrow(/Insufficient funds/);
136
+ });
137
+ });
138
+
139
+ describe('Fee Calculation', () => {
140
+ test('calculates correct fee for 1 input', async () => {
141
+ const feeRate = 10; // 10 sats/vB
142
+ const params = createCommitParams({
143
+ feeRate,
144
+ utxos: [createUtxo(10000, 0)]
145
+ });
146
+ const result = await createCommitTransaction(params);
147
+
148
+ // Fee should be reasonable for 1 input, 2 outputs (commit + change)
149
+ // Estimate: ~10.5 overhead + 68 input + 43 P2TR + 31 change = ~152 vB
150
+ // At 10 sats/vB = ~1520 sats
151
+ expect(result.fees.commit).toBeGreaterThan(1000);
152
+ expect(result.fees.commit).toBeLessThan(3000);
153
+ });
154
+
155
+ test('fee scales with fee rate', async () => {
156
+ const params1 = createCommitParams({ feeRate: 5 });
157
+ const params2 = createCommitParams({ feeRate: 50 });
158
+
159
+ const result1 = await createCommitTransaction(params1);
160
+ const result2 = await createCommitTransaction(params2);
161
+
162
+ // Fee should be ~10x higher for 10x fee rate
163
+ expect(result2.fees.commit).toBeGreaterThan(result1.fees.commit * 8);
164
+ expect(result2.fees.commit).toBeLessThan(result1.fees.commit * 12);
165
+ });
166
+
167
+ test('fee increases with multiple inputs', async () => {
168
+ const params1 = createCommitParams({
169
+ utxos: [createUtxo(10000, 0)]
170
+ });
171
+ const params2 = createCommitParams({
172
+ utxos: [
173
+ createUtxo(1500, 0),
174
+ createUtxo(1500, 1),
175
+ createUtxo(1500, 2),
176
+ createUtxo(1500, 3)
177
+ ],
178
+ minimumCommitAmount: 2000 // Force use of multiple UTXOs
179
+ });
180
+
181
+ const result1 = await createCommitTransaction(params1);
182
+ const result2 = await createCommitTransaction(params2);
183
+
184
+ // More inputs = higher fee
185
+ expect(result2.fees.commit).toBeGreaterThan(result1.fees.commit);
186
+ });
187
+ });
188
+
189
+ describe('PSBT Construction', () => {
190
+ test('PSBT has correct number of inputs', async () => {
191
+ const params = createCommitParams({
192
+ utxos: [createUtxo(10000, 0)]
193
+ });
194
+ const result = await createCommitTransaction(params);
195
+
196
+ expect(result.commitPsbt.inputsLength).toBe(1);
197
+ });
198
+
199
+ test('PSBT includes commit output', async () => {
200
+ const params = createCommitParams();
201
+ const result = await createCommitTransaction(params);
202
+
203
+ expect(result.commitPsbt.outputsLength).toBeGreaterThanOrEqual(1);
204
+ });
205
+
206
+ test('PSBT creates change output when needed', async () => {
207
+ const params = createCommitParams({
208
+ utxos: [createUtxo(100000, 0)], // Large UTXO
209
+ minimumCommitAmount: 546
210
+ });
211
+ const result = await createCommitTransaction(params);
212
+
213
+ // Should have commit output + change output
214
+ expect(result.commitPsbt.outputsLength).toBe(2);
215
+ });
216
+
217
+ test('PSBT omits change when below dust limit', async () => {
218
+ const params = createCommitParams({
219
+ utxos: [createUtxo(2500, 0)], // Enough for commit + fees with minimal change below dust
220
+ minimumCommitAmount: 546,
221
+ feeRate: 10
222
+ });
223
+ const result = await createCommitTransaction(params);
224
+
225
+ // Should have only commit output (change below dust limit)
226
+ expect(result.commitPsbt.outputsLength).toBe(1);
227
+ });
228
+
229
+ test('PSBT is valid base64', async () => {
230
+ const params = createCommitParams();
231
+ const result = await createCommitTransaction(params);
232
+
233
+ expect(result.commitPsbtBase64).toBeDefined();
234
+ expect(typeof result.commitPsbtBase64).toBe('string');
235
+
236
+ // Should be valid base64
237
+ const decoded = Buffer.from(result.commitPsbtBase64, 'base64');
238
+ expect(decoded.length).toBeGreaterThan(0);
239
+ });
240
+ });
241
+
242
+ describe('Inscription Content', () => {
243
+ test('handles text content', async () => {
244
+ const params = createCommitParams({
245
+ content: Buffer.from('Hello World'),
246
+ contentType: 'text/plain'
247
+ });
248
+ const result = await createCommitTransaction(params);
249
+
250
+ expect(result).toBeDefined();
251
+ expect(result.commitAddress).toBeDefined();
252
+ });
253
+
254
+ test('handles JSON content', async () => {
255
+ const params = createCommitParams({
256
+ content: Buffer.from(JSON.stringify({ test: 'data' })),
257
+ contentType: 'application/json'
258
+ });
259
+ const result = await createCommitTransaction(params);
260
+
261
+ expect(result).toBeDefined();
262
+ expect(result.commitAddress).toBeDefined();
263
+ });
264
+
265
+ test('handles binary content', async () => {
266
+ const params = createCommitParams({
267
+ content: Buffer.from([0x89, 0x50, 0x4E, 0x47]), // PNG header
268
+ contentType: 'image/png'
269
+ });
270
+ const result = await createCommitTransaction(params);
271
+
272
+ expect(result).toBeDefined();
273
+ expect(result.commitAddress).toBeDefined();
274
+ });
275
+
276
+ test('handles large content (1KB)', async () => {
277
+ const largeContent = Buffer.alloc(1024, 'a');
278
+ const params = createCommitParams({
279
+ content: largeContent,
280
+ contentType: 'text/plain',
281
+ utxos: [createUtxo(100000, 0)]
282
+ });
283
+ const result = await createCommitTransaction(params);
284
+
285
+ expect(result).toBeDefined();
286
+ expect(result.commitAddress).toBeDefined();
287
+ });
288
+
289
+ test('includes metadata when provided', async () => {
290
+ const params = createCommitParams({
291
+ metadata: { title: 'Test Inscription', author: 'Tester' }
292
+ });
293
+ const result = await createCommitTransaction(params);
294
+
295
+ expect(result).toBeDefined();
296
+ expect(result.commitAddress).toBeDefined();
297
+ });
298
+
299
+ test('handles missing pointer gracefully', async () => {
300
+ // Pointer is optional, test that it works without it
301
+ const params = createCommitParams();
302
+ const result = await createCommitTransaction(params);
303
+
304
+ expect(result).toBeDefined();
305
+ expect(result.commitAddress).toBeDefined();
306
+ });
307
+ });
308
+
309
+ describe('Network Support', () => {
310
+ test('creates mainnet commit address', async () => {
311
+ const params = createCommitParams({ network: 'mainnet' });
312
+ const result = await createCommitTransaction(params);
313
+
314
+ expect(result.commitAddress).toMatch(/^bc1p/);
315
+ });
316
+
317
+ test('creates testnet commit address', async () => {
318
+ const params = createCommitParams({ network: 'testnet' });
319
+ const result = await createCommitTransaction(params);
320
+
321
+ expect(result.commitAddress).toMatch(/^tb1p/);
322
+ });
323
+
324
+ test('creates signet commit address', async () => {
325
+ const params = createCommitParams({ network: 'signet' });
326
+ const result = await createCommitTransaction(params);
327
+
328
+ expect(result.commitAddress).toMatch(/^tb1p/);
329
+ });
330
+
331
+ test('creates regtest commit address', async () => {
332
+ const params = createCommitParams({ network: 'regtest' });
333
+ const result = await createCommitTransaction(params);
334
+
335
+ // Regtest uses TEST_NETWORK which generates tb1p addresses
336
+ expect(result.commitAddress).toMatch(/^tb1p/);
337
+ });
338
+ });
339
+
340
+ describe('Error Handling', () => {
341
+ test('throws when no UTXOs provided', async () => {
342
+ const params = createCommitParams({ utxos: [] });
343
+ await expect(createCommitTransaction(params)).rejects.toThrow(/No UTXOs provided/);
344
+ });
345
+
346
+ test('throws when content is empty', async () => {
347
+ const params = createCommitParams({ content: Buffer.from([]) });
348
+ await expect(createCommitTransaction(params)).rejects.toThrow(/missing content/);
349
+ });
350
+
351
+ test('throws when contentType is missing', async () => {
352
+ const params = createCommitParams({ contentType: '' });
353
+ await expect(createCommitTransaction(params)).rejects.toThrow(/missing content type/);
354
+ });
355
+
356
+ test('throws when changeAddress is missing', async () => {
357
+ const params = createCommitParams({ changeAddress: '' });
358
+ await expect(createCommitTransaction(params)).rejects.toThrow(/Change address is required/);
359
+ });
360
+
361
+ test('throws when feeRate is invalid', async () => {
362
+ const params = createCommitParams({ feeRate: 0 });
363
+ await expect(createCommitTransaction(params)).rejects.toThrow(/Invalid fee rate/);
364
+ });
365
+
366
+ test('throws when all UTXOs are missing scriptPubKey', async () => {
367
+ const invalidUtxo1: Utxo = {
368
+ txid: 'a'.repeat(64),
369
+ vout: 0,
370
+ value: 10000
371
+ // Missing scriptPubKey
372
+ };
373
+
374
+ const invalidUtxo2: Utxo = {
375
+ txid: 'b'.repeat(64),
376
+ vout: 1,
377
+ value: 10000
378
+ // Missing scriptPubKey
379
+ };
380
+
381
+ const params = createCommitParams({
382
+ utxos: [invalidUtxo1, invalidUtxo2]
383
+ });
384
+
385
+ // Should throw error because no valid UTXOs remain after filtering
386
+ await expect(createCommitTransaction(params)).rejects.toThrow(/No valid spendable UTXOs available/);
387
+ await expect(createCommitTransaction(params)).rejects.toThrow(/missing scriptPubKey/);
388
+ });
389
+
390
+ test('filters out invalid UTXOs and uses only valid ones', async () => {
391
+ const invalidUtxo: Utxo = {
392
+ txid: 'a'.repeat(64),
393
+ vout: 0,
394
+ value: 10000
395
+ // Missing scriptPubKey
396
+ };
397
+
398
+ const params = createCommitParams({
399
+ utxos: [invalidUtxo, createUtxo(50000, 1), createUtxo(50000, 2)]
400
+ });
401
+
402
+ // Should filter out the invalid UTXO and use only the valid ones
403
+ const result = await createCommitTransaction(params);
404
+ expect(result).toBeDefined();
405
+ // Should only use valid UTXOs
406
+ expect(result.selectedUtxos.every(u => u.scriptPubKey)).toBe(true);
407
+ });
408
+ });
409
+
410
+ describe('Dust Handling', () => {
411
+ test('respects minimum dust limit (546 sats)', async () => {
412
+ const params = createCommitParams({ minimumCommitAmount: 100 });
413
+ const result = await createCommitTransaction(params);
414
+
415
+ // Should use dust limit instead of 100
416
+ expect(result.commitAmount).toBeGreaterThanOrEqual(546);
417
+ });
418
+
419
+ test('uses custom minimum when above dust limit', async () => {
420
+ const params = createCommitParams({
421
+ minimumCommitAmount: 10000,
422
+ utxos: [createUtxo(15000, 0)] // Enough to cover 10000 + fees
423
+ });
424
+ const result = await createCommitTransaction(params);
425
+
426
+ expect(result.commitAmount).toBe(10000);
427
+ });
428
+
429
+ test('adds dust change to fee', async () => {
430
+ // Create scenario where change would be < 546 sats
431
+ const params = createCommitParams({
432
+ utxos: [createUtxo(1500, 0)],
433
+ minimumCommitAmount: 546,
434
+ feeRate: 5
435
+ });
436
+ const result = await createCommitTransaction(params);
437
+
438
+ // Should have only commit output (no change)
439
+ expect(result.commitPsbt.outputsLength).toBe(1);
440
+ });
441
+ });
442
+
443
+ describe('Consistency', () => {
444
+ test('generates different addresses for different content', async () => {
445
+ const params1 = createCommitParams({ content: Buffer.from('Content 1') });
446
+ const params2 = createCommitParams({ content: Buffer.from('Content 2') });
447
+
448
+ const result1 = await createCommitTransaction(params1);
449
+ const result2 = await createCommitTransaction(params2);
450
+
451
+ // Different content should generate different addresses
452
+ expect(result1.commitAddress).not.toBe(result2.commitAddress);
453
+ });
454
+
455
+ test('generates different addresses on each call (random keypair)', async () => {
456
+ const params = createCommitParams();
457
+
458
+ const result1 = await createCommitTransaction(params);
459
+ const result2 = await createCommitTransaction(params);
460
+
461
+ // Different reveal keypairs should generate different addresses
462
+ expect(result1.commitAddress).not.toBe(result2.commitAddress);
463
+ expect(result1.revealPrivateKey).not.toBe(result2.revealPrivateKey);
464
+ });
465
+
466
+ test('total value equals commit + change + fees', async () => {
467
+ const params = createCommitParams({
468
+ utxos: [createUtxo(100000, 0)]
469
+ });
470
+ const result = await createCommitTransaction(params);
471
+
472
+ const totalInput = result.selectedUtxos.reduce((sum, utxo) => sum + utxo.value, 0);
473
+ const totalOutput = result.commitAmount;
474
+ const fee = result.fees.commit;
475
+
476
+ // Get change amount from PSBT if it exists
477
+ let changeAmount = 0;
478
+ if (result.commitPsbt.outputsLength === 2) {
479
+ changeAmount = totalInput - totalOutput - fee;
480
+ }
481
+
482
+ expect(totalInput).toBe(totalOutput + changeAmount + fee);
483
+ });
484
+
485
+ test('inputs always cover outputs (no negative change)', async () => {
486
+ // Test with various fee rates to ensure iterative selection works
487
+ const feeRates = [1, 5, 10, 20, 50];
488
+
489
+ for (const feeRate of feeRates) {
490
+ const params = createCommitParams({
491
+ utxos: [createUtxo(10000, 0), createUtxo(20000, 1), createUtxo(30000, 2)],
492
+ feeRate
493
+ });
494
+
495
+ const result = await createCommitTransaction(params);
496
+
497
+ const totalInput = result.selectedUtxos.reduce((sum, utxo) => sum + utxo.value, 0);
498
+ const totalOutput = result.commitAmount + result.fees.commit;
499
+
500
+ // Critical check: inputs must always be >= outputs
501
+ expect(totalInput).toBeGreaterThanOrEqual(totalOutput);
502
+
503
+ // Calculate actual change
504
+ const actualChange = totalInput - totalOutput;
505
+ expect(actualChange).toBeGreaterThanOrEqual(0);
506
+ }
507
+ });
508
+ });
509
+
510
+ describe('Iterative UTXO Selection', () => {
511
+ test('reselects UTXOs when fee increases after accurate calculation', async () => {
512
+ // Create scenario where 1 UTXO isn't enough after fee recalculation
513
+ const params = createCommitParams({
514
+ utxos: [
515
+ createUtxo(1500, 0), // Not enough alone
516
+ createUtxo(2000, 1), // Will need this one too
517
+ createUtxo(5000, 2) // May need this as well
518
+ ],
519
+ minimumCommitAmount: 546,
520
+ feeRate: 10
521
+ });
522
+
523
+ const result = await createCommitTransaction(params);
524
+
525
+ // Should have selected enough UTXOs to cover commit + fees
526
+ const totalInput = result.selectedUtxos.reduce((sum, utxo) => sum + utxo.value, 0);
527
+ expect(totalInput).toBeGreaterThanOrEqual(result.commitAmount + result.fees.commit);
528
+ });
529
+
530
+ test('stops iteration when sufficient funds are found', async () => {
531
+ // Create scenario with plenty of UTXOs
532
+ const params = createCommitParams({
533
+ utxos: [
534
+ createUtxo(100000, 0), // This should be enough
535
+ createUtxo(100000, 1),
536
+ createUtxo(100000, 2)
537
+ ],
538
+ feeRate: 10
539
+ });
540
+
541
+ const result = await createCommitTransaction(params);
542
+
543
+ // Should not need all UTXOs since first one is sufficient
544
+ expect(result.selectedUtxos.length).toBeLessThan(3);
545
+ });
546
+
547
+ test('throws error if max iterations reached without sufficient funds', async () => {
548
+ // Create scenario where UTXOs are just barely insufficient
549
+ // This would require many iterations if the algorithm isn't working
550
+ const params = createCommitParams({
551
+ utxos: [
552
+ createUtxo(500, 0),
553
+ createUtxo(500, 1)
554
+ ],
555
+ minimumCommitAmount: 546,
556
+ feeRate: 50 // High fee rate makes it impossible
557
+ });
558
+
559
+ // Should throw after max iterations
560
+ await expect(createCommitTransaction(params)).rejects.toThrow(/Insufficient funds/);
561
+ });
562
+ });
563
+
564
+ describe('UTXO Validation', () => {
565
+ test('validates UTXO has txid', async () => {
566
+ const invalidUtxo: Utxo = {
567
+ txid: '', // Empty txid
568
+ vout: 0,
569
+ value: 10000,
570
+ scriptPubKey: '0014' + 'a'.repeat(40)
571
+ };
572
+
573
+ const params = createCommitParams({
574
+ utxos: [invalidUtxo]
575
+ });
576
+
577
+ await expect(createCommitTransaction(params)).rejects.toThrow(/No valid spendable UTXOs/);
578
+ });
579
+
580
+ test('validates UTXO has valid vout', async () => {
581
+ const invalidUtxo: any = {
582
+ txid: 'a'.repeat(64),
583
+ vout: 'invalid', // Invalid vout type
584
+ value: 10000,
585
+ scriptPubKey: '0014' + 'a'.repeat(40)
586
+ };
587
+
588
+ const params = createCommitParams({
589
+ utxos: [invalidUtxo]
590
+ });
591
+
592
+ await expect(createCommitTransaction(params)).rejects.toThrow(/No valid spendable UTXOs/);
593
+ });
594
+
595
+ test('validates UTXO has positive value', async () => {
596
+ const invalidUtxo: Utxo = {
597
+ txid: 'a'.repeat(64),
598
+ vout: 0,
599
+ value: 0, // Zero value
600
+ scriptPubKey: '0014' + 'a'.repeat(40)
601
+ };
602
+
603
+ const params = createCommitParams({
604
+ utxos: [invalidUtxo]
605
+ });
606
+
607
+ await expect(createCommitTransaction(params)).rejects.toThrow(/No valid spendable UTXOs/);
608
+ });
609
+
610
+ test('provides detailed error message for invalid UTXOs', async () => {
611
+ const invalidUtxos: Utxo[] = [
612
+ {
613
+ txid: 'a'.repeat(64),
614
+ vout: 0,
615
+ value: 10000
616
+ // Missing scriptPubKey
617
+ },
618
+ {
619
+ txid: '', // Missing txid
620
+ vout: 1,
621
+ value: 10000,
622
+ scriptPubKey: '0014' + 'a'.repeat(40)
623
+ },
624
+ {
625
+ txid: 'c'.repeat(64),
626
+ vout: 2,
627
+ value: 0, // Invalid value
628
+ scriptPubKey: '0014' + 'a'.repeat(40)
629
+ }
630
+ ];
631
+
632
+ const params = createCommitParams({
633
+ utxos: invalidUtxos
634
+ });
635
+
636
+ try {
637
+ await createCommitTransaction(params);
638
+ throw new Error('Should have thrown');
639
+ } catch (error) {
640
+ expect(error).toBeInstanceOf(Error);
641
+ if (error instanceof Error) {
642
+ // Should contain detailed information about what's wrong
643
+ expect(error.message).toMatch(/No valid spendable UTXOs available/);
644
+ expect(error.message).toMatch(/3 UTXO.*provided but all are invalid/);
645
+ }
646
+ }
647
+ });
648
+ });
649
+ });
@@ -0,0 +1,31 @@
1
+ import { describe, test, expect } from 'bun:test';
2
+ import { buildTransferTransaction } from '../../../src/bitcoin/transfer';
3
+ import { DUST_LIMIT_SATS, Utxo } from '../../../src/types';
4
+
5
+ describe('buildTransferTransaction', () => {
6
+ const utxo = (value: number, i: number = 0): Utxo => ({ txid: 't', vout: i, value });
7
+
8
+ test('creates tx with recipient output and change when above dust', () => {
9
+ const { tx, selection } = buildTransferTransaction([utxo(100_000)], 'bc1qto', 50_000, 1);
10
+ expect(tx.vout[0].address).toBe('bc1qto');
11
+ expect(tx.vout[0].value).toBe(50_000);
12
+ expect(selection.changeSats).toBeGreaterThanOrEqual(DUST_LIMIT_SATS);
13
+ // change output present when change >= dust
14
+ expect(tx.vout.length).toBe(2);
15
+ });
16
+
17
+ test('suppresses change output when below dust threshold', () => {
18
+ const { tx, selection } = buildTransferTransaction([utxo(800)], 'addr', DUST_LIMIT_SATS, 1);
19
+ expect(selection.changeSats).toBe(0);
20
+ // only recipient output
21
+ expect(tx.vout.length).toBe(1);
22
+ });
23
+
24
+ test('uses input address as default change address when provided', () => {
25
+ const inputWithAddr: Utxo = { txid: 't', vout: 0, value: 100000, address: 'bc1qchange' };
26
+ const { tx, selection } = buildTransferTransaction([inputWithAddr], 'bc1qto', 50000, 1);
27
+ expect(selection.changeSats).toBeGreaterThanOrEqual(DUST_LIMIT_SATS);
28
+ // change output address should be input address when not passed explicitly
29
+ expect(tx.vout[1].address).toBe('bc1qchange');
30
+ });
31
+ });