@bsv/sdk 1.1.32 → 1.2.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 (229) hide show
  1. package/dist/cjs/mod.js +4 -0
  2. package/dist/cjs/mod.js.map +1 -1
  3. package/dist/cjs/package.json +4 -3
  4. package/dist/cjs/src/auth/Certificate.js +163 -0
  5. package/dist/cjs/src/auth/Certificate.js.map +1 -0
  6. package/dist/cjs/src/auth/index.js +9 -0
  7. package/dist/cjs/src/auth/index.js.map +1 -0
  8. package/dist/cjs/src/compat/BSM.js +17 -7
  9. package/dist/cjs/src/compat/BSM.js.map +1 -1
  10. package/dist/cjs/src/compat/ECIES.js +17 -7
  11. package/dist/cjs/src/compat/ECIES.js.map +1 -1
  12. package/dist/cjs/src/compat/HD.js +17 -7
  13. package/dist/cjs/src/compat/HD.js.map +1 -1
  14. package/dist/cjs/src/compat/Mnemonic.js +17 -7
  15. package/dist/cjs/src/compat/Mnemonic.js.map +1 -1
  16. package/dist/cjs/src/compat/index.js +17 -7
  17. package/dist/cjs/src/compat/index.js.map +1 -1
  18. package/dist/cjs/src/messages/index.js +17 -7
  19. package/dist/cjs/src/messages/index.js.map +1 -1
  20. package/dist/cjs/src/overlay-tools/LookupResolver.js +170 -0
  21. package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -0
  22. package/dist/cjs/src/overlay-tools/OverlayAdminTokenTemplate.js +69 -0
  23. package/dist/cjs/src/overlay-tools/OverlayAdminTokenTemplate.js.map +1 -0
  24. package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js +336 -0
  25. package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js.map +1 -0
  26. package/dist/cjs/src/overlay-tools/index.js +29 -0
  27. package/dist/cjs/src/overlay-tools/index.js.map +1 -0
  28. package/dist/cjs/src/primitives/PrivateKey.js +17 -7
  29. package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
  30. package/dist/cjs/src/primitives/TransactionSignature.js +17 -7
  31. package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
  32. package/dist/cjs/src/primitives/index.js +17 -7
  33. package/dist/cjs/src/primitives/index.js.map +1 -1
  34. package/dist/cjs/src/script/Spend.js +17 -7
  35. package/dist/cjs/src/script/Spend.js.map +1 -1
  36. package/dist/cjs/src/script/templates/PushDrop.js +218 -0
  37. package/dist/cjs/src/script/templates/PushDrop.js.map +1 -0
  38. package/dist/cjs/src/script/templates/index.js +3 -1
  39. package/dist/cjs/src/script/templates/index.js.map +1 -1
  40. package/dist/cjs/src/transaction/Beef.js +35 -6
  41. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  42. package/dist/cjs/src/transaction/Transaction.js +13 -4
  43. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  44. package/dist/cjs/src/transaction/http/DefaultHttpClient.js +1 -1
  45. package/dist/cjs/src/transaction/http/DefaultHttpClient.js.map +1 -1
  46. package/dist/cjs/src/wallet/CachedKeyDeriver.js +177 -0
  47. package/dist/cjs/src/wallet/CachedKeyDeriver.js.map +1 -0
  48. package/dist/cjs/src/wallet/KeyDeriver.js +174 -0
  49. package/dist/cjs/src/wallet/KeyDeriver.js.map +1 -0
  50. package/dist/cjs/src/wallet/ProtoWallet.js +245 -0
  51. package/dist/cjs/src/wallet/ProtoWallet.js.map +1 -0
  52. package/dist/cjs/src/wallet/Wallet.interfaces.js +3 -0
  53. package/dist/cjs/src/wallet/Wallet.interfaces.js.map +1 -0
  54. package/dist/cjs/src/wallet/WalletClient.js +181 -0
  55. package/dist/cjs/src/wallet/WalletClient.js.map +1 -0
  56. package/dist/cjs/src/wallet/WalletError.js +28 -0
  57. package/dist/cjs/src/wallet/WalletError.js.map +1 -0
  58. package/dist/cjs/src/wallet/index.js +34 -0
  59. package/dist/cjs/src/wallet/index.js.map +1 -0
  60. package/dist/cjs/src/wallet/substrates/HTTPWalletWire.js +45 -0
  61. package/dist/cjs/src/wallet/substrates/HTTPWalletWire.js.map +1 -0
  62. package/dist/cjs/src/wallet/substrates/WalletWire.js +3 -0
  63. package/dist/cjs/src/wallet/substrates/WalletWire.js.map +1 -0
  64. package/dist/cjs/src/wallet/substrates/WalletWireCalls.js +36 -0
  65. package/dist/cjs/src/wallet/substrates/WalletWireCalls.js.map +1 -0
  66. package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js +1821 -0
  67. package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js.map +1 -0
  68. package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js +1305 -0
  69. package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js.map +1 -0
  70. package/dist/cjs/src/wallet/substrates/XDM.js +130 -0
  71. package/dist/cjs/src/wallet/substrates/XDM.js.map +1 -0
  72. package/dist/cjs/src/wallet/substrates/index.js +33 -0
  73. package/dist/cjs/src/wallet/substrates/index.js.map +1 -0
  74. package/dist/cjs/src/wallet/substrates/window.CWI.js +102 -0
  75. package/dist/cjs/src/wallet/substrates/window.CWI.js.map +1 -0
  76. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  77. package/dist/esm/mod.js +4 -0
  78. package/dist/esm/mod.js.map +1 -1
  79. package/dist/esm/src/auth/Certificate.js +185 -0
  80. package/dist/esm/src/auth/Certificate.js.map +1 -0
  81. package/dist/esm/src/auth/index.js +2 -0
  82. package/dist/esm/src/auth/index.js.map +1 -0
  83. package/dist/esm/src/overlay-tools/LookupResolver.js +167 -0
  84. package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -0
  85. package/dist/esm/src/overlay-tools/OverlayAdminTokenTemplate.js +64 -0
  86. package/dist/esm/src/overlay-tools/OverlayAdminTokenTemplate.js.map +1 -0
  87. package/dist/esm/src/overlay-tools/SHIPBroadcaster.js +335 -0
  88. package/dist/esm/src/overlay-tools/SHIPBroadcaster.js.map +1 -0
  89. package/dist/esm/src/overlay-tools/index.js +6 -0
  90. package/dist/esm/src/overlay-tools/index.js.map +1 -0
  91. package/dist/esm/src/script/templates/PushDrop.js +215 -0
  92. package/dist/esm/src/script/templates/PushDrop.js.map +1 -0
  93. package/dist/esm/src/script/templates/index.js +1 -0
  94. package/dist/esm/src/script/templates/index.js.map +1 -1
  95. package/dist/esm/src/transaction/Beef.js +35 -6
  96. package/dist/esm/src/transaction/Beef.js.map +1 -1
  97. package/dist/esm/src/transaction/Transaction.js +13 -4
  98. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  99. package/dist/esm/src/transaction/http/DefaultHttpClient.js +1 -1
  100. package/dist/esm/src/transaction/http/DefaultHttpClient.js.map +1 -1
  101. package/dist/esm/src/wallet/CachedKeyDeriver.js +174 -0
  102. package/dist/esm/src/wallet/CachedKeyDeriver.js.map +1 -0
  103. package/dist/esm/src/wallet/KeyDeriver.js +172 -0
  104. package/dist/esm/src/wallet/KeyDeriver.js.map +1 -0
  105. package/dist/esm/src/wallet/ProtoWallet.js +207 -0
  106. package/dist/esm/src/wallet/ProtoWallet.js.map +1 -0
  107. package/dist/esm/src/wallet/Wallet.interfaces.js +2 -0
  108. package/dist/esm/src/wallet/Wallet.interfaces.js.map +1 -0
  109. package/dist/esm/src/wallet/WalletClient.js +177 -0
  110. package/dist/esm/src/wallet/WalletClient.js.map +1 -0
  111. package/dist/esm/src/wallet/WalletError.js +25 -0
  112. package/dist/esm/src/wallet/WalletError.js.map +1 -0
  113. package/dist/esm/src/wallet/index.js +9 -0
  114. package/dist/esm/src/wallet/index.js.map +1 -0
  115. package/dist/esm/src/wallet/substrates/HTTPWalletWire.js +42 -0
  116. package/dist/esm/src/wallet/substrates/HTTPWalletWire.js.map +1 -0
  117. package/dist/esm/src/wallet/substrates/WalletWire.js +2 -0
  118. package/dist/esm/src/wallet/substrates/WalletWire.js.map +1 -0
  119. package/dist/esm/src/wallet/substrates/WalletWireCalls.js +34 -0
  120. package/dist/esm/src/wallet/substrates/WalletWireCalls.js.map +1 -0
  121. package/dist/esm/src/wallet/substrates/WalletWireProcessor.js +1816 -0
  122. package/dist/esm/src/wallet/substrates/WalletWireProcessor.js.map +1 -0
  123. package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js +1300 -0
  124. package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js.map +1 -0
  125. package/dist/esm/src/wallet/substrates/XDM.js +128 -0
  126. package/dist/esm/src/wallet/substrates/XDM.js.map +1 -0
  127. package/dist/esm/src/wallet/substrates/index.js +8 -0
  128. package/dist/esm/src/wallet/substrates/index.js.map +1 -0
  129. package/dist/esm/src/wallet/substrates/window.CWI.js +100 -0
  130. package/dist/esm/src/wallet/substrates/window.CWI.js.map +1 -0
  131. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  132. package/dist/types/mod.d.ts +4 -0
  133. package/dist/types/mod.d.ts.map +1 -1
  134. package/dist/types/src/auth/Certificate.d.ts +76 -0
  135. package/dist/types/src/auth/Certificate.d.ts.map +1 -0
  136. package/dist/types/src/auth/index.d.ts +2 -0
  137. package/dist/types/src/auth/index.d.ts.map +1 -0
  138. package/dist/types/src/overlay-tools/LookupResolver.d.ts +71 -0
  139. package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -0
  140. package/dist/types/src/overlay-tools/OverlayAdminTokenTemplate.d.ts +44 -0
  141. package/dist/types/src/overlay-tools/OverlayAdminTokenTemplate.d.ts.map +1 -0
  142. package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts +90 -0
  143. package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts.map +1 -0
  144. package/dist/types/src/overlay-tools/index.d.ts +6 -0
  145. package/dist/types/src/overlay-tools/index.d.ts.map +1 -0
  146. package/dist/types/src/script/templates/PushDrop.d.ts +53 -0
  147. package/dist/types/src/script/templates/PushDrop.d.ts.map +1 -0
  148. package/dist/types/src/script/templates/index.d.ts +1 -0
  149. package/dist/types/src/script/templates/index.d.ts.map +1 -1
  150. package/dist/types/src/transaction/Beef.d.ts +16 -1
  151. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  152. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  153. package/dist/types/src/wallet/CachedKeyDeriver.d.ts +92 -0
  154. package/dist/types/src/wallet/CachedKeyDeriver.d.ts.map +1 -0
  155. package/dist/types/src/wallet/KeyDeriver.d.ts +72 -0
  156. package/dist/types/src/wallet/KeyDeriver.d.ts.map +1 -0
  157. package/dist/types/src/wallet/ProtoWallet.d.ts +415 -0
  158. package/dist/types/src/wallet/ProtoWallet.d.ts.map +1 -0
  159. package/dist/types/src/wallet/Wallet.interfaces.d.ts +996 -0
  160. package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -0
  161. package/dist/types/src/wallet/WalletClient.d.ts +182 -0
  162. package/dist/types/src/wallet/WalletClient.d.ts.map +1 -0
  163. package/dist/types/src/wallet/WalletError.d.ts +14 -0
  164. package/dist/types/src/wallet/WalletError.d.ts.map +1 -0
  165. package/dist/types/src/wallet/index.d.ts +9 -0
  166. package/dist/types/src/wallet/index.d.ts.map +1 -0
  167. package/dist/types/src/wallet/substrates/HTTPWalletWire.d.ts +9 -0
  168. package/dist/types/src/wallet/substrates/HTTPWalletWire.d.ts.map +1 -0
  169. package/dist/types/src/wallet/substrates/WalletWire.d.ts +7 -0
  170. package/dist/types/src/wallet/substrates/WalletWire.d.ts.map +1 -0
  171. package/dist/types/src/wallet/substrates/WalletWireCalls.d.ts +33 -0
  172. package/dist/types/src/wallet/substrates/WalletWireCalls.d.ts.map +1 -0
  173. package/dist/types/src/wallet/substrates/WalletWireProcessor.d.ts +18 -0
  174. package/dist/types/src/wallet/substrates/WalletWireProcessor.d.ts.map +1 -0
  175. package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts +196 -0
  176. package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts.map +1 -0
  177. package/dist/types/src/wallet/substrates/XDM.d.ts +412 -0
  178. package/dist/types/src/wallet/substrates/XDM.d.ts.map +1 -0
  179. package/dist/types/src/wallet/substrates/index.d.ts +8 -0
  180. package/dist/types/src/wallet/substrates/index.d.ts.map +1 -0
  181. package/dist/types/src/wallet/substrates/window.CWI.d.ts +410 -0
  182. package/dist/types/src/wallet/substrates/window.CWI.d.ts.map +1 -0
  183. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  184. package/dist/umd/bundle.js +1 -1
  185. package/docs/overlay-tools.md +551 -0
  186. package/docs/script.md +135 -0
  187. package/docs/totp.md +119 -0
  188. package/docs/transaction.md +25 -0
  189. package/docs/wallet-substrates.md +10 -0
  190. package/docs/wallet.md +4182 -0
  191. package/mod.ts +5 -1
  192. package/package.json +44 -3
  193. package/src/auth/Certificate.ts +233 -0
  194. package/src/auth/__tests/Certificate.test.ts +282 -0
  195. package/src/auth/index.ts +1 -0
  196. package/src/overlay-tools/LookupResolver.ts +228 -0
  197. package/src/overlay-tools/OverlayAdminTokenTemplate.ts +79 -0
  198. package/src/overlay-tools/SHIPBroadcaster.ts +405 -0
  199. package/src/overlay-tools/__tests/LookupResolver.test.ts +1403 -0
  200. package/src/overlay-tools/__tests/OverlayAdminTokenTemplate.test.ts +69 -0
  201. package/src/overlay-tools/__tests/SHIPBroadcaster.test.ts +904 -0
  202. package/src/overlay-tools/index.ts +5 -0
  203. package/src/script/templates/PushDrop.ts +246 -0
  204. package/src/script/templates/__tests/PushDrop.test.ts +158 -0
  205. package/src/script/templates/index.ts +1 -0
  206. package/src/transaction/Beef.ts +36 -6
  207. package/src/transaction/Transaction.ts +13 -4
  208. package/src/transaction/__tests/Beef.test.ts +20 -6
  209. package/src/transaction/http/DefaultHttpClient.ts +1 -1
  210. package/src/wallet/CachedKeyDeriver.ts +193 -0
  211. package/src/wallet/KeyDeriver.ts +178 -0
  212. package/src/wallet/ProtoWallet.ts +732 -0
  213. package/src/wallet/Wallet.interfaces.ts +1170 -0
  214. package/src/wallet/WalletClient.ts +201 -0
  215. package/src/wallet/WalletError.ts +27 -0
  216. package/src/wallet/__tests/CachedKeyDeriver.test.ts +322 -0
  217. package/src/wallet/__tests/KeyDeriver.test.ts +118 -0
  218. package/src/wallet/__tests/ProtoWallet.test.ts +543 -0
  219. package/src/wallet/index.ts +8 -0
  220. package/src/wallet/substrates/HTTPWalletWire.ts +47 -0
  221. package/src/wallet/substrates/WalletWire.ts +6 -0
  222. package/src/wallet/substrates/WalletWireCalls.ts +34 -0
  223. package/src/wallet/substrates/WalletWireProcessor.ts +2046 -0
  224. package/src/wallet/substrates/WalletWireTransceiver.ts +1454 -0
  225. package/src/wallet/substrates/XDM.ts +157 -0
  226. package/src/wallet/substrates/__tests/WalletWire.integration.test.ts +2194 -0
  227. package/src/wallet/substrates/__tests/XDM.test.ts +659 -0
  228. package/src/wallet/substrates/index.ts +7 -0
  229. package/src/wallet/substrates/window.CWI.ts +133 -0
@@ -0,0 +1,2194 @@
1
+ import ProtoWallet from '../../../../dist/cjs/src/wallet/ProtoWallet.js'
2
+ import { Utils, PrivateKey, Hash } from '../../../../dist/cjs/src/primitives/index.js'
3
+ import WalletWireTransceiver from '../../../../dist/cjs/src/wallet/substrates/WalletWireTransceiver.js'
4
+ import WalletWireProcessor from '../../../../dist/cjs/src/wallet/substrates/WalletWireProcessor.js'
5
+
6
+ const sampleData = [3, 1, 4, 1, 5, 9]
7
+
8
+ describe('WalletWire Integration Tests', () => {
9
+ /**
10
+ * This is a copy of the test suite for ProtoWallet, but instead of using a ProtoWallet directly, we're using it over the WalletWire.
11
+ * This serves as an imperfect but still useful way to ensure that the WalletWire doesn't contain serialization or deserialization issues.
12
+ */
13
+ describe('ProtoWallet Over Wallet Wire', () => {
14
+ it('Throws when functions are not supported', async () => {
15
+ const wallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet('anyone')))
16
+ await expect(() => {
17
+ return (wallet as any).createAction()
18
+ }).rejects.toThrow()
19
+ await expect(() => {
20
+ return (wallet as any).abortAction()
21
+ }).rejects.toThrow()
22
+ await expect(() => {
23
+ return (wallet as any).signAction()
24
+ }).rejects.toThrow()
25
+ await expect(() => {
26
+ return (wallet as any).listOutputs()
27
+ }).rejects.toThrow()
28
+ await expect(() => {
29
+ return (wallet as any).relinquishOutput()
30
+ }).rejects.toThrow()
31
+ await expect(() => {
32
+ return (wallet as any).listActions()
33
+ }).rejects.toThrow()
34
+ await expect(() => {
35
+ return (wallet as any).internalizeAction()
36
+ }).rejects.toThrow()
37
+ await expect(() => {
38
+ return (wallet as any).acquireCertificate()
39
+ }).rejects.toThrow()
40
+ await expect(() => {
41
+ return (wallet as any).proveCertificate()
42
+ }).rejects.toThrow()
43
+ await expect(() => {
44
+ return (wallet as any).listCertificates()
45
+ }).rejects.toThrow()
46
+ await expect(() => {
47
+ return (wallet as any).relinquishCertificate()
48
+ }).rejects.toThrow()
49
+ await expect(() => {
50
+ return (wallet as any).getHeight()
51
+ }).rejects.toThrow()
52
+ await expect(() => {
53
+ return (wallet as any).getHeaderForHeight()
54
+ }).rejects.toThrow()
55
+ // TODO: Remove these two from the throw list once they are implemented.
56
+ await expect(() => {
57
+ return (wallet as any).discoverByIdentityKey()
58
+ }).rejects.toThrow()
59
+ await expect(() => {
60
+ return (wallet as any).discoverByAttributes()
61
+ }).rejects.toThrow()
62
+ })
63
+ it('Throws the privileged error when the privileged flag is set', async () => {
64
+ const wallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet('anyone')))
65
+ const privilegedError = 'ProtoWallet is a single-keyring wallet, operating without context about whether its configured keyring is privileged.'
66
+ const saneParams = {
67
+ protocolID: [0, 'hello world'],
68
+ keyID: '123123123',
69
+ plaintext: [0, 1, 3],
70
+ ciphertext: [3, 4, 5],
71
+ data: [1, 3, 5],
72
+ hmac: [1, 3, 5],
73
+ signature: [1, 3, 5],
74
+ counterparty: 'self'
75
+ }
76
+ await expect(() => {
77
+ return (wallet as any).encrypt({ ...saneParams, privileged: true })
78
+ }).rejects.toThrow(new Error(privilegedError))
79
+ await expect(() => {
80
+ return (wallet as any).decrypt({ ...saneParams, privileged: true })
81
+ }).rejects.toThrow(new Error(privilegedError))
82
+ await expect(() => {
83
+ return (wallet as any).createSignature({ ...saneParams, privileged: true })
84
+ }).rejects.toThrow(new Error(privilegedError))
85
+ await expect(() => {
86
+ return (wallet as any).verifySignature({ ...saneParams, privileged: true })
87
+ }).rejects.toThrow(new Error(privilegedError))
88
+ await expect(() => {
89
+ return (wallet as any).createHmac({ ...saneParams, privileged: true })
90
+ }).rejects.toThrow(new Error(privilegedError))
91
+ await expect(() => {
92
+ return (wallet as any).verifyHmac({ ...saneParams, privileged: true })
93
+ }).rejects.toThrow(new Error(privilegedError))
94
+ await expect(() => {
95
+ return (wallet as any).getPublicKey({ ...saneParams, privileged: true })
96
+ }).rejects.toThrow(new Error(privilegedError))
97
+ await expect(() => {
98
+ return (wallet as any).revealCounterpartyKeyLinkage({ ...saneParams, privileged: true })
99
+ }).rejects.toThrow(new Error(privilegedError))
100
+ await expect(() => {
101
+ return (wallet as any).revealSpecificKeyLinkage({ ...saneParams, privileged: true })
102
+ }).rejects.toThrow(new Error(privilegedError))
103
+ })
104
+ it('Validates the BRC-3 compliance vector', async () => {
105
+ const wallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet('anyone')))
106
+ const { valid } = await wallet.verifySignature({
107
+ data: Utils.toArray('BRC-3 Compliance Validated!', 'utf8'),
108
+ signature: [48, 68, 2, 32, 43, 34, 58, 156, 219, 32, 50, 70, 29, 240, 155, 137, 88, 60, 200, 95, 243, 198, 201, 21, 56, 82, 141, 112, 69, 196, 170, 73, 156, 6, 44, 48, 2, 32, 118, 125, 254, 201, 44, 87, 177, 170, 93, 11, 193, 134, 18, 70, 9, 31, 234, 27, 170, 177, 54, 96, 181, 140, 166, 196, 144, 14, 230, 118, 106, 105],
109
+ protocolID: [2, 'BRC3 Test'],
110
+ keyID: '42',
111
+ counterparty: '0294c479f762f6baa97fbcd4393564c1d7bd8336ebd15928135bbcf575cd1a71a1'
112
+ })
113
+ expect(valid).toBe(true)
114
+ })
115
+ it('Validates the BRC-2 HMAC compliance vector', async () => {
116
+ const wallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(new PrivateKey('6a2991c9de20e38b31d7ea147bf55f5039e4bbc073160f5e0d541d1f17e321b8', 'hex'))))
117
+ const { valid } = await wallet.verifyHmac({
118
+ data: Utils.toArray('BRC-2 HMAC Compliance Validated!', 'utf8'),
119
+ hmac: [81, 240, 18, 153, 163, 45, 174, 85, 9, 246, 142, 125, 209, 133, 82, 76, 254, 103, 46, 182, 86, 59, 219, 61, 126, 30, 176, 232, 233, 100, 234, 14],
120
+ protocolID: [2, 'BRC2 Test'],
121
+ keyID: '42',
122
+ counterparty: '0294c479f762f6baa97fbcd4393564c1d7bd8336ebd15928135bbcf575cd1a71a1'
123
+ })
124
+ expect(valid).toBe(true)
125
+ })
126
+ it('Validates the BRC-2 Encryption compliance vector', async () => {
127
+ const wallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(new PrivateKey('6a2991c9de20e38b31d7ea147bf55f5039e4bbc073160f5e0d541d1f17e321b8', 'hex'))))
128
+ const { plaintext } = await wallet.decrypt({
129
+ ciphertext: [252, 203, 216, 184, 29, 161, 223, 212, 16, 193, 94, 99, 31, 140, 99, 43, 61, 236, 184, 67, 54, 105, 199, 47, 11, 19, 184, 127, 2, 165, 125, 9, 188, 195, 196, 39, 120, 130, 213, 95, 186, 89, 64, 28, 1, 80, 20, 213, 159, 133, 98, 253, 128, 105, 113, 247, 197, 152, 236, 64, 166, 207, 113, 134, 65, 38, 58, 24, 127, 145, 140, 206, 47, 70, 146, 84, 186, 72, 95, 35, 154, 112, 178, 55, 72, 124],
130
+ protocolID: [2, 'BRC2 Test'],
131
+ keyID: '42',
132
+ counterparty: '0294c479f762f6baa97fbcd4393564c1d7bd8336ebd15928135bbcf575cd1a71a1'
133
+ })
134
+ expect(Utils.toUTF8(plaintext)).toEqual('BRC-2 Encryption Compliance Validated!')
135
+ })
136
+ it('Encrypts messages decryptable by the counterparty', async () => {
137
+ const userKey = PrivateKey.fromRandom()
138
+ const counterpartyKey = PrivateKey.fromRandom()
139
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
140
+ const counterparty = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(counterpartyKey)))
141
+ const { ciphertext } = await user.encrypt({
142
+ plaintext: sampleData,
143
+ protocolID: [2, 'tests'],
144
+ keyID: '4',
145
+ counterparty: counterpartyKey.toPublicKey().toString()
146
+ })
147
+ const { plaintext } = await counterparty.decrypt({
148
+ ciphertext,
149
+ protocolID: [2, 'tests'],
150
+ keyID: '4',
151
+ counterparty: userKey.toPublicKey().toString()
152
+ })
153
+ expect(plaintext).toEqual(sampleData)
154
+ expect(ciphertext).not.toEqual(plaintext)
155
+ })
156
+ it('Fails to decryupt messages for the wrong protocol, key, and counterparty', async () => {
157
+ const userKey = PrivateKey.fromRandom()
158
+ const counterpartyKey = PrivateKey.fromRandom()
159
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
160
+ const counterparty = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(counterpartyKey)))
161
+ const { ciphertext } = await user.encrypt({
162
+ plaintext: sampleData,
163
+ protocolID: [2, 'tests'],
164
+ keyID: '4',
165
+ counterparty: counterpartyKey.toPublicKey().toString()
166
+ })
167
+ await expect(async () => await counterparty.decrypt({
168
+ ciphertext,
169
+ protocolID: [1, 'tests'],
170
+ keyID: '4',
171
+ counterparty: userKey.toPublicKey().toString()
172
+ })).rejects.toThrow()
173
+ await expect(async () => await counterparty.decrypt({
174
+ ciphertext,
175
+ protocolID: [2, 'tests'],
176
+ keyID: '5',
177
+ counterparty: userKey.toPublicKey().toString()
178
+ })).rejects.toThrow()
179
+ await expect(async () => await counterparty.decrypt({
180
+ ciphertext,
181
+ protocolID: [2, 'tests'],
182
+ keyID: '4',
183
+ counterparty: counterpartyKey.toPublicKey().toString()
184
+ })).rejects.toThrow()
185
+ })
186
+ it('Correctly derives keys for a counterparty', async () => {
187
+ const userKey = PrivateKey.fromRandom()
188
+ const counterpartyKey = PrivateKey.fromRandom()
189
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
190
+ const counterparty = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(counterpartyKey)))
191
+ const { publicKey: identityKey } = await user.getPublicKey({
192
+ identityKey: true
193
+ })
194
+ expect(identityKey).toEqual(userKey.toPublicKey().toString())
195
+ const { publicKey: derivedForCounterparty } = await user.getPublicKey({
196
+ protocolID: [2, 'tests'],
197
+ keyID: '4',
198
+ counterparty: counterpartyKey.toPublicKey().toString()
199
+ })
200
+ const { publicKey: derivedByCounterparty } = await counterparty.getPublicKey({
201
+ protocolID: [2, 'tests'],
202
+ keyID: '4',
203
+ counterparty: userKey.toPublicKey().toString(),
204
+ forSelf: true
205
+ })
206
+ expect(derivedForCounterparty).toEqual(derivedByCounterparty)
207
+ })
208
+ it('Signs messages verifiable by the counterparty', async () => {
209
+ const userKey = PrivateKey.fromRandom()
210
+ const counterpartyKey = PrivateKey.fromRandom()
211
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
212
+ const counterparty = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(counterpartyKey)))
213
+ const { signature } = await user.createSignature({
214
+ data: sampleData,
215
+ protocolID: [2, 'tests'],
216
+ keyID: '4',
217
+ counterparty: counterpartyKey.toPublicKey().toString()
218
+ })
219
+ const { valid } = await counterparty.verifySignature({
220
+ signature,
221
+ data: sampleData,
222
+ protocolID: [2, 'tests'],
223
+ keyID: '4',
224
+ counterparty: userKey.toPublicKey().toString()
225
+ })
226
+ expect(valid).toEqual(true)
227
+ expect(signature.length).not.toEqual(0)
228
+ })
229
+ it('Directly signs hash of message verifiable by the counterparty', async () => {
230
+ const userKey = PrivateKey.fromRandom()
231
+ const counterpartyKey = PrivateKey.fromRandom()
232
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
233
+ const counterparty = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(counterpartyKey)))
234
+ const { signature } = await user.createSignature({
235
+ hashToDirectlySign: Hash.sha256(sampleData),
236
+ protocolID: [2, 'tests'],
237
+ keyID: '4',
238
+ counterparty: counterpartyKey.toPublicKey().toString()
239
+ })
240
+ const { valid } = await counterparty.verifySignature({
241
+ signature,
242
+ data: sampleData,
243
+ protocolID: [2, 'tests'],
244
+ keyID: '4',
245
+ counterparty: userKey.toPublicKey().toString()
246
+ })
247
+ expect(valid).toEqual(true)
248
+ const { valid: hashValid } = await counterparty.verifySignature({
249
+ signature,
250
+ hashToDirectlyVerify: Hash.sha256(sampleData),
251
+ protocolID: [2, 'tests'],
252
+ keyID: '4',
253
+ counterparty: userKey.toPublicKey().toString()
254
+ })
255
+ expect(hashValid).toEqual(true)
256
+ expect(signature.length).not.toEqual(0)
257
+ })
258
+ it('Fails to verify signature for the wrong data, protocol, key, and counterparty', async () => {
259
+ const userKey = PrivateKey.fromRandom()
260
+ const counterpartyKey = PrivateKey.fromRandom()
261
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
262
+ const counterparty = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(counterpartyKey)))
263
+ const { signature } = await user.createSignature({
264
+ data: sampleData,
265
+ protocolID: [2, 'tests'],
266
+ keyID: '4',
267
+ counterparty: counterpartyKey.toPublicKey().toString()
268
+ })
269
+ await expect(async () => await counterparty.verifySignature({
270
+ signature,
271
+ data: [0, ...sampleData],
272
+ protocolID: [2, 'tests'],
273
+ keyID: '4',
274
+ counterparty: userKey.toPublicKey().toString()
275
+ })).rejects.toThrow()
276
+ await expect(async () => await counterparty.verifySignature({
277
+ signature,
278
+ data: sampleData,
279
+ protocolID: [2, 'wrong'],
280
+ keyID: '4',
281
+ counterparty: userKey.toPublicKey().toString()
282
+ })).rejects.toThrow()
283
+ await expect(async () => await counterparty.verifySignature({
284
+ signature,
285
+ data: sampleData,
286
+ protocolID: [2, 'tests'],
287
+ keyID: '2',
288
+ counterparty: userKey.toPublicKey().toString()
289
+ })).rejects.toThrow()
290
+ await expect(async () => await counterparty.verifySignature({
291
+ signature,
292
+ data: sampleData,
293
+ protocolID: [2, 'tests'],
294
+ keyID: '4',
295
+ counterparty: counterpartyKey.toPublicKey().toString()
296
+ })).rejects.toThrow()
297
+ })
298
+ it('Computes HMAC over messages verifiable by the counterparty', async () => {
299
+ const userKey = PrivateKey.fromRandom()
300
+ const counterpartyKey = PrivateKey.fromRandom()
301
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
302
+ const counterparty = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(counterpartyKey)))
303
+ const { hmac } = await user.createHmac({
304
+ data: sampleData,
305
+ protocolID: [2, 'tests'],
306
+ keyID: '4',
307
+ counterparty: counterpartyKey.toPublicKey().toString()
308
+ })
309
+ const { valid } = await counterparty.verifyHmac({
310
+ hmac,
311
+ data: sampleData,
312
+ protocolID: [2, 'tests'],
313
+ keyID: '4',
314
+ counterparty: userKey.toPublicKey().toString()
315
+ })
316
+ expect(valid).toEqual(true)
317
+ expect(hmac.length).toEqual(32)
318
+ })
319
+ it('Fails to verify HMAC for the wrong data, protocol, key, and counterparty', async () => {
320
+ const userKey = PrivateKey.fromRandom()
321
+ const counterpartyKey = PrivateKey.fromRandom()
322
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
323
+ const counterparty = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(counterpartyKey)))
324
+ const { hmac } = await user.createHmac({
325
+ data: sampleData,
326
+ protocolID: [2, 'tests'],
327
+ keyID: '4',
328
+ counterparty: counterpartyKey.toPublicKey().toString()
329
+ })
330
+ await expect(async () => await counterparty.verifyHmac({
331
+ hmac,
332
+ data: [0, ...sampleData],
333
+ protocolID: [2, 'tests'],
334
+ keyID: '4',
335
+ counterparty: userKey.toPublicKey().toString()
336
+ })).rejects.toThrow()
337
+ await expect(async () => await counterparty.verifyHmac({
338
+ hmac,
339
+ data: sampleData,
340
+ protocolID: [2, 'wrong'],
341
+ keyID: '4',
342
+ counterparty: userKey.toPublicKey().toString()
343
+ })).rejects.toThrow()
344
+ await expect(async () => await counterparty.verifyHmac({
345
+ hmac,
346
+ data: sampleData,
347
+ protocolID: [2, 'tests'],
348
+ keyID: '2',
349
+ counterparty: userKey.toPublicKey().toString()
350
+ })).rejects.toThrow()
351
+ await expect(async () => await counterparty.verifyHmac({
352
+ hmac,
353
+ data: sampleData,
354
+ protocolID: [2, 'tests'],
355
+ keyID: '4',
356
+ counterparty: counterpartyKey.toPublicKey().toString()
357
+ })).rejects.toThrow()
358
+ })
359
+ it('Returns the expected version, network, and authentication status', async () => {
360
+ const wallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet('anyone')))
361
+ expect(await wallet.getVersion({})).toEqual({ version: 'proto-1.0.0' })
362
+ expect(await wallet.getNetwork({})).toEqual({ network: 'mainnet' })
363
+ expect(await wallet.isAuthenticated({})).toEqual({ authenticated: true })
364
+ expect(await wallet.waitForAuthentication({})).toEqual({ authenticated: true })
365
+ })
366
+ it('Uses anyone for creating signatures and self for other operations if no counterparty is provided', async () => {
367
+ const userKey = PrivateKey.fromRandom()
368
+ const user = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(userKey)))
369
+ const { hmac } = await user.createHmac({
370
+ data: sampleData,
371
+ protocolID: [2, 'tests'],
372
+ keyID: '4'
373
+ })
374
+ const { valid: hmacValid } = await user.verifyHmac({
375
+ hmac,
376
+ data: sampleData,
377
+ protocolID: [2, 'tests'],
378
+ keyID: '4'
379
+ })
380
+ expect(hmacValid).toEqual(true)
381
+ const { valid: explicitSelfHmacValid } = await user.verifyHmac({
382
+ hmac,
383
+ data: sampleData,
384
+ protocolID: [2, 'tests'],
385
+ keyID: '4',
386
+ counterparty: 'self'
387
+ })
388
+ expect(explicitSelfHmacValid).toEqual(true)
389
+ expect(hmac.length).toEqual(32)
390
+ const { signature: anyoneSig } = await user.createSignature({
391
+ data: sampleData,
392
+ protocolID: [2, 'tests'],
393
+ keyID: '4'
394
+ // counterparty=anyone is implicit for creating signatures
395
+ })
396
+ const anyone = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet('anyone')))
397
+ const { valid: anyoneSigValid } = await anyone.verifySignature({
398
+ signature: anyoneSig,
399
+ data: sampleData,
400
+ protocolID: [2, 'tests'],
401
+ keyID: '4',
402
+ counterparty: userKey.toPublicKey().toString()
403
+ })
404
+ expect(anyoneSigValid).toEqual(true)
405
+ const { signature: selfSig } = await user.createSignature({
406
+ data: sampleData,
407
+ protocolID: [2, 'tests'],
408
+ keyID: '4',
409
+ counterparty: 'self'
410
+ })
411
+ const { valid: selfSigValid } = await user.verifySignature({
412
+ signature: selfSig,
413
+ data: sampleData,
414
+ protocolID: [2, 'tests'],
415
+ keyID: '4'
416
+ // Self is implicit when verifying signatures
417
+ })
418
+ expect(selfSigValid).toEqual(true)
419
+ const { valid: explicitSelfSigValid } = await user.verifySignature({
420
+ signature: selfSig,
421
+ data: sampleData,
422
+ protocolID: [2, 'tests'],
423
+ keyID: '4',
424
+ counterparty: 'self'
425
+ })
426
+ expect(explicitSelfSigValid).toEqual(true)
427
+ const { publicKey } = await user.getPublicKey({
428
+ protocolID: [2, 'tests'],
429
+ keyID: '4'
430
+ })
431
+ const { publicKey: explicitSelfPublicKey } = await user.getPublicKey({
432
+ protocolID: [2, 'tests'],
433
+ keyID: '4',
434
+ counterparty: 'self'
435
+ })
436
+ expect(publicKey).toEqual(explicitSelfPublicKey)
437
+ const { ciphertext } = await user.encrypt({
438
+ plaintext: sampleData,
439
+ protocolID: [2, 'tests'],
440
+ keyID: '4'
441
+ })
442
+ const { plaintext } = await user.decrypt({
443
+ ciphertext,
444
+ protocolID: [2, 'tests'],
445
+ keyID: '4'
446
+ })
447
+ const { plaintext: explicitSelfPlaintext } = await user.decrypt({
448
+ ciphertext,
449
+ protocolID: [2, 'tests'],
450
+ keyID: '4',
451
+ counterparty: 'self'
452
+ })
453
+ expect(plaintext).toEqual(explicitSelfPlaintext)
454
+ expect(plaintext).toEqual(sampleData)
455
+ })
456
+ it('Validates the revealCounterpartyKeyLinkage function', async () => {
457
+ // Initialize keys
458
+ const proverKey = PrivateKey.fromRandom()
459
+ const counterpartyKey = PrivateKey.fromRandom()
460
+ const verifierKey = PrivateKey.fromRandom()
461
+
462
+ // Initialize wallets
463
+ const proverWallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(proverKey)))
464
+ const verifierWallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(verifierKey)))
465
+
466
+ // Prover reveals counterparty key linkage
467
+ const revelation = await proverWallet.revealCounterpartyKeyLinkage({
468
+ counterparty: counterpartyKey.toPublicKey().toString(),
469
+ verifier: verifierKey.toPublicKey().toString()
470
+ })
471
+
472
+ // Verifier decrypts the encrypted linkage
473
+ const { plaintext: linkage } = await verifierWallet.decrypt({
474
+ ciphertext: revelation.encryptedLinkage,
475
+ protocolID: [2, 'counterparty linkage revelation'],
476
+ keyID: revelation.revelationTime,
477
+ counterparty: proverKey.toPublicKey().toString()
478
+ })
479
+
480
+ // Compute expected linkage
481
+ const expectedLinkage = proverKey.deriveSharedSecret(counterpartyKey.toPublicKey()).encode(true)
482
+
483
+ // Compare linkage and expectedLinkage
484
+ expect(linkage).toEqual(expectedLinkage)
485
+ })
486
+
487
+ it('Validates the revealSpecificKeyLinkage function', async () => {
488
+ // Initialize keys
489
+ const proverKey = PrivateKey.fromRandom()
490
+ const counterpartyKey = PrivateKey.fromRandom()
491
+ const verifierKey = PrivateKey.fromRandom()
492
+
493
+ // Initialize wallets
494
+ const proverWallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(proverKey)))
495
+ const verifierWallet = new WalletWireTransceiver(new WalletWireProcessor(new ProtoWallet(verifierKey)))
496
+
497
+ const protocolID: [0 | 1 | 2, string] = [0, 'tests']
498
+ const keyID = 'test key id'
499
+
500
+ // Prover reveals specific key linkage
501
+ const revelation = await proverWallet.revealSpecificKeyLinkage({
502
+ counterparty: counterpartyKey.toPublicKey().toString(),
503
+ verifier: verifierKey.toPublicKey().toString(),
504
+ protocolID,
505
+ keyID
506
+ })
507
+ expect(revelation.encryptedLinkageProof).toBeDefined()
508
+ expect(revelation.proofType).toBeDefined()
509
+
510
+ // Verifier decrypts the encrypted linkage
511
+ const { plaintext: linkage } = await verifierWallet.decrypt({
512
+ ciphertext: revelation.encryptedLinkage,
513
+ protocolID: [2, `specific linkage revelation ${protocolID[0]} ${protocolID[1]}`],
514
+ keyID,
515
+ counterparty: proverKey.toPublicKey().toString()
516
+ })
517
+
518
+ // Compute expected linkage
519
+ const sharedSecret = proverKey.deriveSharedSecret(counterpartyKey.toPublicKey()).encode(true)
520
+
521
+ // Function to compute the invoice number
522
+ const computeInvoiceNumber = function (protocolID, keyID) {
523
+ const securityLevel = protocolID[0]
524
+ if (!Number.isInteger(securityLevel) || securityLevel < 0 || securityLevel > 2) {
525
+ throw new Error('Protocol security level must be 0, 1, or 2')
526
+ }
527
+ const protocolName = protocolID[1].toLowerCase().trim()
528
+ if (keyID.length > 800) {
529
+ throw new Error('Key IDs must be 800 characters or less')
530
+ }
531
+ if (keyID.length < 1) {
532
+ throw new Error('Key IDs must be 1 character or more')
533
+ }
534
+ if (protocolName.length > 400) {
535
+ throw new Error('Protocol names must be 400 characters or less')
536
+ }
537
+ if (protocolName.length < 5) {
538
+ throw new Error('Protocol names must be 5 characters or more')
539
+ }
540
+ if (protocolName.includes(' ')) {
541
+ throw new Error('Protocol names cannot contain multiple consecutive spaces (" ")')
542
+ }
543
+ if (!/^[a-z0-9 ]+$/g.test(protocolName)) {
544
+ throw new Error('Protocol names can only contain letters, numbers and spaces')
545
+ }
546
+ if (protocolName.endsWith(' protocol')) {
547
+ throw new Error('No need to end your protocol name with " protocol"')
548
+ }
549
+ return `${securityLevel}-${protocolName}-${keyID}`
550
+ }
551
+ const invoiceNumber = computeInvoiceNumber(protocolID, keyID)
552
+ const invoiceNumberBin = Utils.toArray(invoiceNumber, 'utf8')
553
+
554
+ // Compute expected linkage
555
+ const expectedLinkage = Hash.sha256hmac(sharedSecret, invoiceNumberBin)
556
+
557
+ // Compare linkage and expectedLinkage
558
+ expect(linkage).toEqual(expectedLinkage)
559
+ })
560
+ })
561
+ // Helper function to create a test wallet wire setup
562
+ const createTestWalletWire = (wallet: ProtoWallet) => {
563
+ const processor = new WalletWireProcessor(wallet)
564
+ const transceiver = new WalletWireTransceiver(processor)
565
+ return transceiver
566
+ }
567
+
568
+ // Mock implementation for methods not supported by ProtoWallet
569
+ const mockUnsupportedMethods = (methods: Partial<ProtoWallet>): ProtoWallet => {
570
+ return {
571
+ ...methods
572
+ } as ProtoWallet
573
+ }
574
+
575
+ describe('createAction', () => {
576
+ it('should create an action with valid inputs', async () => {
577
+ // Mock the createAction method
578
+ const createActionMock = jest.fn().mockResolvedValue({
579
+ txid: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806',
580
+ tx: [1, 2, 3, 4]
581
+ })
582
+ const wallet = createTestWalletWire(
583
+ mockUnsupportedMethods({
584
+ createAction: createActionMock
585
+ })
586
+ )
587
+
588
+ const args = {
589
+ description: 'Test action description',
590
+ outputs: [
591
+ {
592
+ lockingScript: '00', // Sample locking script
593
+ satoshis: 1000,
594
+ outputDescription: 'Test output',
595
+ basket: 'test-basket',
596
+ customInstructions: 'Test instructions',
597
+ tags: ['test-tag']
598
+ }
599
+ ],
600
+ labels: ['test-label']
601
+ }
602
+ const result = await wallet.createAction(args)
603
+ expect(result).toHaveProperty('txid')
604
+ expect(result).toHaveProperty('tx')
605
+ expect(result.tx).toBeInstanceOf(Array)
606
+ expect(createActionMock).toHaveBeenCalledWith(args, '')
607
+ })
608
+
609
+ it('should create an action with minimal inputs (only description)', async () => {
610
+ // Mock the createAction method
611
+ const createActionMock = jest.fn().mockResolvedValue({
612
+ txid: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806'
613
+ })
614
+ const wallet = createTestWalletWire(
615
+ mockUnsupportedMethods({
616
+ createAction: createActionMock
617
+ })
618
+ )
619
+
620
+ const args = {
621
+ description: 'Minimal action description'
622
+ }
623
+ const result = await wallet.createAction(args)
624
+ expect(result).toHaveProperty('txid')
625
+ expect(result).not.toHaveProperty('tx')
626
+ expect(result).not.toHaveProperty('noSendChange')
627
+ expect(result).not.toHaveProperty('sendWithResults')
628
+ expect(result).not.toHaveProperty('signableTransaction')
629
+ expect(createActionMock).toHaveBeenCalledWith(args, '')
630
+ })
631
+
632
+ it('should create an action and return only txid when returnTXIDOnly is true', async () => {
633
+ // Mock the createAction method
634
+ const createActionMock = jest.fn().mockResolvedValue({
635
+ txid: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806'
636
+ })
637
+ const wallet = createTestWalletWire(
638
+ mockUnsupportedMethods({
639
+ createAction: createActionMock
640
+ })
641
+ )
642
+
643
+ const args = {
644
+ description: 'Test action with returnTXIDOnly',
645
+ options: {
646
+ returnTXIDOnly: true
647
+ }
648
+ }
649
+ const result = await wallet.createAction(args)
650
+ expect(result).toHaveProperty('txid')
651
+ expect(result).not.toHaveProperty('tx')
652
+ expect(result).not.toHaveProperty('noSendChange')
653
+ expect(result).not.toHaveProperty('sendWithResults')
654
+ expect(result).not.toHaveProperty('signableTransaction')
655
+ expect(createActionMock).toHaveBeenCalledWith(args, '')
656
+ })
657
+
658
+ it('should create an action and return a signableTransaction when noSend is true', async () => {
659
+ // Mock the createAction method
660
+ const createActionMock = jest.fn().mockResolvedValue({
661
+ signableTransaction: {
662
+ tx: [0x01],
663
+ reference: Utils.toBase64([0x01])
664
+ }
665
+ })
666
+ const wallet = createTestWalletWire(
667
+ mockUnsupportedMethods({
668
+ createAction: createActionMock
669
+ })
670
+ )
671
+
672
+ const args = {
673
+ description: 'Test action with noSend',
674
+ options: {
675
+ noSend: true
676
+ }
677
+ }
678
+ const result = await wallet.createAction(args)
679
+ expect(result).toHaveProperty('signableTransaction')
680
+ expect(result.signableTransaction).toHaveProperty('tx')
681
+ expect(result.signableTransaction).toHaveProperty('reference')
682
+ expect(result).not.toHaveProperty('txid')
683
+ expect(result).not.toHaveProperty('tx')
684
+ expect(createActionMock).toHaveBeenCalledWith(args, '')
685
+ })
686
+
687
+ it('should create an action with all options set and handle all return values', async () => {
688
+ // Mock the createAction method
689
+ const createActionMock = jest.fn().mockResolvedValue({
690
+ txid: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806',
691
+ tx: [1, 2, 3, 4],
692
+ noSendChange: [
693
+ 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0'
694
+ ],
695
+ sendWithResults: [
696
+ {
697
+ txid: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806',
698
+ status: 'sending'
699
+ }
700
+ ],
701
+ signableTransaction: {
702
+ tx: [0x01],
703
+ reference: Utils.toBase64([0x01])
704
+ }
705
+ })
706
+ const wallet = createTestWalletWire(
707
+ mockUnsupportedMethods({
708
+ createAction: createActionMock
709
+ })
710
+ )
711
+
712
+ const args = {
713
+ description: 'Test action with all options',
714
+ inputs: [],
715
+ inputBEEF: [1, 2, 3, 4],
716
+ outputs: [{
717
+ lockingScript: '016a',
718
+ satoshis: 1,
719
+ outputDescription: 'This is a test.'
720
+ }],
721
+ lockTime: 0,
722
+ version: 1,
723
+ labels: ['label1', 'label2'],
724
+ options: {
725
+ signAndProcess: false,
726
+ acceptDelayedBroadcast: false,
727
+ trustSelf: 'known' as 'known',
728
+ knownTxids: [
729
+ 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806'
730
+ ],
731
+ returnTXIDOnly: false,
732
+ noSend: true,
733
+ noSendChange: [
734
+ 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0'
735
+ ],
736
+ sendWith: [
737
+ 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806'
738
+ ],
739
+ randomizeOutputs: false
740
+ }
741
+ }
742
+ const result = await wallet.createAction(args)
743
+ expect(result).toHaveProperty('txid')
744
+ expect(result).toHaveProperty('tx')
745
+ expect(result).toHaveProperty('noSendChange')
746
+ expect(result).toHaveProperty('sendWithResults')
747
+ expect(result).toHaveProperty('signableTransaction')
748
+ expect(createActionMock).toHaveBeenCalledWith(args, '')
749
+ })
750
+
751
+ it('should throw an error with invalid inputs', async () => {
752
+ // Mock the createAction method to throw an error
753
+ const createActionMock = jest.fn().mockRejectedValue(new Error('Invalid inputs'))
754
+ const wallet = createTestWalletWire(
755
+ mockUnsupportedMethods({
756
+ createAction: createActionMock
757
+ })
758
+ )
759
+ const args = {
760
+ description: '' // Invalid description (too short)
761
+ }
762
+ await expect(wallet.createAction(args)).rejects.toThrow('Invalid inputs')
763
+ expect(createActionMock).toHaveBeenCalledWith(args, '')
764
+ })
765
+ })
766
+
767
+ describe('signAction', () => {
768
+ it('should sign an action with valid inputs', async () => {
769
+ // Mock the signAction method
770
+ const signActionMock = jest.fn().mockResolvedValue({
771
+ txid: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806',
772
+ tx: [1, 2, 3, 4]
773
+ })
774
+ const wallet = createTestWalletWire(
775
+ mockUnsupportedMethods({
776
+ signAction: signActionMock
777
+ })
778
+ )
779
+
780
+ const spends = {
781
+ 0: {
782
+ unlockingScript: '00' // Sample unlocking script
783
+ }
784
+ }
785
+ const reference = Utils.toBase64([1, 2, 3])
786
+ const args = { spends, reference }
787
+ const result = await wallet.signAction(args)
788
+ expect(result).toHaveProperty('txid')
789
+ expect(result).toHaveProperty('tx')
790
+ expect(result.tx).toBeInstanceOf(Array)
791
+ expect(signActionMock).toHaveBeenCalledWith(args, '')
792
+ })
793
+
794
+ it('should throw an error with invalid inputs', async () => {
795
+ // Mock the signAction method to throw an error
796
+ const signActionMock = jest.fn().mockRejectedValue(new Error('Invalid inputs'))
797
+ const wallet = createTestWalletWire(
798
+ mockUnsupportedMethods({
799
+ signAction: signActionMock
800
+ })
801
+ )
802
+ const spends = {}
803
+ const reference = ''
804
+ const args = { spends, reference }
805
+ await expect(wallet.signAction(args)).rejects.toThrow('Invalid inputs')
806
+ expect(signActionMock).toHaveBeenCalledWith(args, '')
807
+ })
808
+ })
809
+
810
+ describe('abortAction', () => {
811
+ it('should abort an action with valid reference', async () => {
812
+ // Mock the abortAction method
813
+ const abortActionMock = jest.fn().mockResolvedValue({ aborted: true })
814
+ const wallet = createTestWalletWire(
815
+ mockUnsupportedMethods({
816
+ abortAction: abortActionMock
817
+ })
818
+ )
819
+
820
+ const reference = Utils.toBase64([1, 2, 3])
821
+ const args = { reference }
822
+ const result = await wallet.abortAction(args)
823
+ expect(result).toEqual({ aborted: true })
824
+ expect(abortActionMock).toHaveBeenCalledWith(args, '')
825
+ })
826
+
827
+ it('should throw an error with invalid reference', async () => {
828
+ // Mock the abortAction method to throw an error
829
+ const abortActionMock = jest.fn().mockRejectedValue(new Error('Invalid reference'))
830
+ const wallet = createTestWalletWire(
831
+ mockUnsupportedMethods({
832
+ abortAction: abortActionMock
833
+ })
834
+ )
835
+ const reference = ''
836
+ const args = { reference }
837
+ await expect(wallet.abortAction(args)).rejects.toThrow('Invalid reference')
838
+ expect(abortActionMock).toHaveBeenCalledWith(args, '')
839
+ })
840
+ })
841
+
842
+ describe('listActions', () => {
843
+ it('should list actions with valid inputs', async () => {
844
+ // Mock the listActions method
845
+ const listActionsMock = jest.fn().mockResolvedValue({
846
+ totalActions: 1,
847
+ actions: [
848
+ {
849
+ txid: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806',
850
+ satoshis: 1000,
851
+ status: 'completed',
852
+ isOutgoing: true,
853
+ description: 'Test action',
854
+ labels: ['test-label'],
855
+ version: 1,
856
+ lockTime: 0
857
+ }
858
+ ]
859
+ })
860
+ const wallet = createTestWalletWire(
861
+ mockUnsupportedMethods({
862
+ listActions: listActionsMock
863
+ })
864
+ )
865
+
866
+ const args = {
867
+ labels: ['test-label'],
868
+ includeLabels: true,
869
+ limit: 10,
870
+ offset: 0
871
+ }
872
+ const result = await wallet.listActions(args)
873
+ expect(result).toHaveProperty('totalActions')
874
+ expect(result).toHaveProperty('actions')
875
+ expect(Array.isArray(result.actions)).toBe(true)
876
+ expect(listActionsMock).toHaveBeenCalledWith(args, '')
877
+ })
878
+
879
+ it('should list actions with empty labels array', async () => {
880
+ // Mock the listActions method
881
+ const listActionsMock = jest.fn().mockResolvedValue({
882
+ totalActions: 0,
883
+ actions: []
884
+ })
885
+ const wallet = createTestWalletWire(
886
+ mockUnsupportedMethods({
887
+ listActions: listActionsMock
888
+ })
889
+ )
890
+
891
+ const args = {
892
+ labels: [],
893
+ includeLabels: true,
894
+ limit: 10,
895
+ offset: 0
896
+ }
897
+ const result = await wallet.listActions(args)
898
+ expect(result).toHaveProperty('totalActions')
899
+ expect(result.totalActions).toBe(0)
900
+ expect(result.actions).toEqual([])
901
+ expect(listActionsMock).toHaveBeenCalledWith(args, '')
902
+ })
903
+
904
+ it('should throw an error with invalid inputs', async () => {
905
+ // Mock the listActions method to throw an error
906
+ const listActionsMock = jest.fn().mockRejectedValue(new Error('Invalid inputs'))
907
+ const wallet = createTestWalletWire(
908
+ mockUnsupportedMethods({
909
+ listActions: listActionsMock
910
+ })
911
+ )
912
+ const args = {
913
+ labels: []
914
+ }
915
+ await expect(wallet.listActions(args)).rejects.toThrow('Invalid inputs')
916
+ expect(listActionsMock).toHaveBeenCalledWith(args, '')
917
+ })
918
+ })
919
+
920
+ describe('internalizeAction', () => {
921
+ it('should internalize an action with valid inputs', async () => {
922
+ // Mock the internalizeAction method
923
+ const internalizeActionMock = jest.fn().mockResolvedValue({ accepted: true })
924
+ const wallet = createTestWalletWire(
925
+ mockUnsupportedMethods({
926
+ internalizeAction: internalizeActionMock
927
+ })
928
+ )
929
+
930
+ const args = {
931
+ tx: [0x00], // Sample transaction byte array
932
+ outputs: [
933
+ {
934
+ outputIndex: 0,
935
+ protocol: 'wallet payment' as 'wallet payment',
936
+ paymentRemittance: {
937
+ derivationPrefix: Utils.toBase64([1, 2, 3]),
938
+ derivationSuffix: Utils.toBase64([4, 5, 6]),
939
+ senderIdentityKey: '02' + '1'.repeat(64)
940
+ }
941
+ }
942
+ ],
943
+ description: 'Test internalize action',
944
+ labels: ['test-label']
945
+ }
946
+ const result = await wallet.internalizeAction(args)
947
+ expect(result).toEqual({ accepted: true })
948
+ expect(internalizeActionMock).toHaveBeenCalledWith(args, '')
949
+ })
950
+
951
+ it('should throw an error with invalid inputs', async () => {
952
+ // Mock the internalizeAction method to throw an error
953
+ const internalizeActionMock = jest.fn().mockRejectedValue(new Error('Invalid inputs'))
954
+ const wallet = createTestWalletWire(
955
+ mockUnsupportedMethods({
956
+ internalizeAction: internalizeActionMock
957
+ })
958
+ )
959
+ const args = {
960
+ tx: [], // Empty transaction array
961
+ outputs: [],
962
+ description: 'Test internalize action'
963
+ }
964
+ await expect(wallet.internalizeAction(args)).rejects.toThrow('Invalid inputs')
965
+ expect(internalizeActionMock).toHaveBeenCalledWith(args, '')
966
+ })
967
+ it('should internalize an action with "basket insertion" protocol', async () => {
968
+ // Mock the internalizeAction method
969
+ const internalizeActionMock = jest.fn().mockResolvedValue({ accepted: true })
970
+ const wallet = createTestWalletWire(
971
+ mockUnsupportedMethods({
972
+ internalizeAction: internalizeActionMock
973
+ })
974
+ )
975
+
976
+ const args = {
977
+ tx: [0x00], // Sample transaction byte array
978
+ outputs: [
979
+ {
980
+ outputIndex: 0,
981
+ protocol: 'basket insertion' as 'basket insertion',
982
+ insertionRemittance: {
983
+ basket: 'test-basket',
984
+ customInstructions: 'Test instructions',
985
+ tags: ['test-tag1', 'test-tag2']
986
+ }
987
+ }
988
+ ],
989
+ description: 'Test internalize action with basket insertion',
990
+ labels: ['test-label']
991
+ }
992
+ const result = await wallet.internalizeAction(args)
993
+ expect(result).toEqual({ accepted: true })
994
+ expect(internalizeActionMock).toHaveBeenCalledWith(args, '')
995
+ })
996
+ })
997
+
998
+ describe('listOutputs', () => {
999
+ it('should list outputs with valid inputs', async () => {
1000
+ // Mock the listOutputs method
1001
+ const listOutputsMock = jest.fn().mockResolvedValue({
1002
+ totalOutputs: 1,
1003
+ outputs: [
1004
+ {
1005
+ outpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1006
+ satoshis: 1000,
1007
+ lockingScript: '00',
1008
+ spendable: true,
1009
+ customInstructions: 'Test instructions',
1010
+ tags: ['test-tag'],
1011
+ labels: ['test-label']
1012
+ }
1013
+ ]
1014
+ })
1015
+ const wallet = createTestWalletWire(
1016
+ mockUnsupportedMethods({
1017
+ listOutputs: listOutputsMock
1018
+ })
1019
+ )
1020
+
1021
+ const args = {
1022
+ basket: 'test-basket',
1023
+ includeLabels: true,
1024
+ limit: 10,
1025
+ offset: 0
1026
+ }
1027
+ const result = await wallet.listOutputs(args)
1028
+ expect(result).toHaveProperty('totalOutputs')
1029
+ expect(result).toHaveProperty('outputs')
1030
+ expect(Array.isArray(result.outputs)).toBe(true)
1031
+ expect(listOutputsMock).toHaveBeenCalledWith(args, '')
1032
+ })
1033
+
1034
+ it('should throw an error with invalid inputs', async () => {
1035
+ // Mock the listOutputs method to throw an error
1036
+ const listOutputsMock = jest.fn().mockRejectedValue(new Error('Invalid inputs'))
1037
+ const wallet = createTestWalletWire(
1038
+ mockUnsupportedMethods({
1039
+ listOutputs: listOutputsMock
1040
+ })
1041
+ )
1042
+ const args = {
1043
+ basket: ''
1044
+ }
1045
+ await expect(wallet.listOutputs(args)).rejects.toThrow('Invalid inputs')
1046
+ expect(listOutputsMock).toHaveBeenCalledWith(args, '')
1047
+ })
1048
+ it('should list outputs without specifying optional parameters', async () => {
1049
+ // Mock the listOutputs method
1050
+ const listOutputsMock = jest.fn().mockResolvedValue({
1051
+ totalOutputs: 1,
1052
+ BEEF: [1, 2, 3, 4],
1053
+ outputs: [
1054
+ {
1055
+ outpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1056
+ satoshis: 1000,
1057
+ spendable: true
1058
+ }
1059
+ ]
1060
+ })
1061
+ const wallet = createTestWalletWire(
1062
+ mockUnsupportedMethods({
1063
+ listOutputs: listOutputsMock
1064
+ })
1065
+ )
1066
+
1067
+ const args = {
1068
+ basket: 'test-basket'
1069
+ // Optional parameters are not specified
1070
+ }
1071
+ const result = await wallet.listOutputs(args)
1072
+ expect(result).toHaveProperty('totalOutputs')
1073
+ expect(result).toHaveProperty('outputs')
1074
+ expect(result).toHaveProperty('BEEF')
1075
+ expect(result.outputs[0]).toHaveProperty('outpoint')
1076
+ expect(result.outputs[0]).toHaveProperty('satoshis')
1077
+ expect(result.outputs[0]).toHaveProperty('spendable')
1078
+ expect(result.outputs[0]).not.toHaveProperty('lockingScript')
1079
+ expect(result.outputs[0]).not.toHaveProperty('customInstructions')
1080
+ expect(result.outputs[0]).not.toHaveProperty('tags')
1081
+ expect(result.outputs[0]).not.toHaveProperty('labels')
1082
+ expect(listOutputsMock).toHaveBeenCalledWith(args, '')
1083
+ })
1084
+ })
1085
+
1086
+ describe('getPublicKey', () => {
1087
+ it('should get the identity public key', async () => {
1088
+ const wallet = createTestWalletWire(new ProtoWallet(PrivateKey.fromRandom()))
1089
+ const result = await wallet.getPublicKey({ identityKey: true })
1090
+ expect(result).toHaveProperty('publicKey')
1091
+ expect(typeof result.publicKey).toBe('string')
1092
+ expect(result.publicKey.length).toBe(66) // Compressed public key hex length
1093
+ })
1094
+
1095
+ it('should get a derived public key with valid inputs', async () => {
1096
+ const userKey = PrivateKey.fromRandom()
1097
+ const counterpartyKey = PrivateKey.fromRandom()
1098
+ const wallet = createTestWalletWire(new ProtoWallet(userKey))
1099
+ const args = {
1100
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1101
+ keyID: 'test-key-id',
1102
+ counterparty: counterpartyKey.toPublicKey().toString()
1103
+ }
1104
+ const result = await wallet.getPublicKey(args)
1105
+ expect(result).toHaveProperty('publicKey')
1106
+ expect(typeof result.publicKey).toBe('string')
1107
+ expect(result.publicKey.length).toBe(66)
1108
+ })
1109
+
1110
+ it('should get the public key with counterparty "anyone"', async () => {
1111
+ const wallet = createTestWalletWire(new ProtoWallet(PrivateKey.fromRandom()))
1112
+ const args = {
1113
+ protocolID: [1, 'testprotocol'] as [0 | 1 | 2, string],
1114
+ keyID: 'testkeyid',
1115
+ counterparty: 'anyone' as 'anyone'
1116
+ }
1117
+ const result = await wallet.getPublicKey(args)
1118
+ expect(result).toHaveProperty('publicKey')
1119
+ expect(typeof result.publicKey).toBe('string')
1120
+ expect(result.publicKey.length).toBe(66) // Compressed public key hex length
1121
+ })
1122
+
1123
+ it('should get the public key with missing optional parameters', async () => {
1124
+ const wallet = createTestWalletWire(new ProtoWallet(PrivateKey.fromRandom()))
1125
+ const args = {
1126
+ protocolID: [0, 'minimalprotocol'] as [0 | 1 | 2, string],
1127
+ keyID: 'minimalkeyid'
1128
+ // Missing counterparty, should default to 'self' or 'anyone' based on context
1129
+ }
1130
+ const result = await wallet.getPublicKey(args)
1131
+ expect(result).toHaveProperty('publicKey')
1132
+ expect(typeof result.publicKey).toBe('string')
1133
+ expect(result.publicKey.length).toBe(66)
1134
+ })
1135
+
1136
+ it('should throw an error for privileged operation without permission', async () => {
1137
+ const wallet = createTestWalletWire(new ProtoWallet(PrivateKey.fromRandom()))
1138
+ const args = {
1139
+ identityKey: true as true,
1140
+ privileged: true,
1141
+ privilegedReason: 'Test privileged operation'
1142
+ }
1143
+ await expect(wallet.getPublicKey(args)).rejects.toThrow()
1144
+ })
1145
+ })
1146
+
1147
+ describe('encrypt and decrypt', () => {
1148
+ it('should encrypt and decrypt data correctly', async () => {
1149
+ const userKey = PrivateKey.fromRandom()
1150
+ const counterpartyKey = PrivateKey.fromRandom()
1151
+ const userWallet = createTestWalletWire(new ProtoWallet(userKey))
1152
+ const counterpartyWallet = createTestWalletWire(new ProtoWallet(counterpartyKey))
1153
+
1154
+ const plaintext = sampleData
1155
+ const encryptArgs = {
1156
+ plaintext,
1157
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1158
+ keyID: 'test-key-id',
1159
+ counterparty: counterpartyKey.toPublicKey().toString()
1160
+ }
1161
+ const encryptResult = await userWallet.encrypt(encryptArgs)
1162
+ expect(encryptResult).toHaveProperty('ciphertext')
1163
+ expect(encryptResult.ciphertext).not.toEqual(plaintext)
1164
+
1165
+ const decryptArgs = {
1166
+ ciphertext: encryptResult.ciphertext,
1167
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1168
+ keyID: 'test-key-id',
1169
+ counterparty: userKey.toPublicKey().toString()
1170
+ }
1171
+ const decryptResult = await counterpartyWallet.decrypt(decryptArgs)
1172
+ expect(decryptResult).toHaveProperty('plaintext')
1173
+ expect(decryptResult.plaintext).toEqual(plaintext)
1174
+ })
1175
+
1176
+ it('should throw an error for invalid decryption inputs', async () => {
1177
+ const userKey = PrivateKey.fromRandom()
1178
+ const counterpartyKey = PrivateKey.fromRandom()
1179
+ const counterpartyWallet = createTestWalletWire(new ProtoWallet(counterpartyKey))
1180
+
1181
+ const decryptArgs = {
1182
+ ciphertext: [0x00],
1183
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1184
+ keyID: 'test-key-id',
1185
+ counterparty: userKey.toPublicKey().toString()
1186
+ }
1187
+ await expect(counterpartyWallet.decrypt(decryptArgs)).rejects.toThrow()
1188
+ })
1189
+ })
1190
+
1191
+ describe('createHmac and verifyHmac', () => {
1192
+ it('should create and verify HMAC correctly', async () => {
1193
+ const userKey = PrivateKey.fromRandom()
1194
+ const counterpartyKey = PrivateKey.fromRandom()
1195
+ const userWallet = createTestWalletWire(new ProtoWallet(userKey))
1196
+ const counterpartyWallet = createTestWalletWire(new ProtoWallet(counterpartyKey))
1197
+
1198
+ const data = sampleData
1199
+ const createHmacArgs = {
1200
+ data,
1201
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1202
+ keyID: 'test-key-id',
1203
+ counterparty: counterpartyKey.toPublicKey().toString()
1204
+ }
1205
+ const createHmacResult = await userWallet.createHmac(createHmacArgs)
1206
+ expect(createHmacResult).toHaveProperty('hmac')
1207
+ expect(createHmacResult.hmac.length).toBe(32)
1208
+
1209
+ const verifyHmacArgs = {
1210
+ data,
1211
+ hmac: createHmacResult.hmac,
1212
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1213
+ keyID: 'test-key-id',
1214
+ counterparty: userKey.toPublicKey().toString()
1215
+ }
1216
+ const verifyHmacResult = await counterpartyWallet.verifyHmac(verifyHmacArgs)
1217
+ expect(verifyHmacResult).toEqual({ valid: true })
1218
+ })
1219
+
1220
+ it('should throw an error for invalid HMAC verification', async () => {
1221
+ const userKey = PrivateKey.fromRandom()
1222
+ const counterpartyWallet = createTestWalletWire(new ProtoWallet(PrivateKey.fromRandom()))
1223
+
1224
+ const verifyHmacArgs = {
1225
+ data: sampleData,
1226
+ hmac: [0x00],
1227
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1228
+ keyID: 'test-key-id',
1229
+ counterparty: userKey.toPublicKey().toString()
1230
+ }
1231
+ await expect(counterpartyWallet.verifyHmac(verifyHmacArgs)).rejects.toThrow()
1232
+ })
1233
+ })
1234
+
1235
+ describe('createSignature and verifySignature', () => {
1236
+ it('should create and verify signature correctly', async () => {
1237
+ const userKey = PrivateKey.fromRandom()
1238
+ const counterpartyKey = PrivateKey.fromRandom()
1239
+ const userWallet = createTestWalletWire(new ProtoWallet(userKey))
1240
+ const counterpartyWallet = createTestWalletWire(new ProtoWallet(counterpartyKey))
1241
+
1242
+ const data = sampleData
1243
+ const createSignatureArgs = {
1244
+ data,
1245
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1246
+ keyID: 'test-key-id',
1247
+ counterparty: counterpartyKey.toPublicKey().toString()
1248
+ }
1249
+ const createSignatureResult = await userWallet.createSignature(createSignatureArgs)
1250
+ expect(createSignatureResult).toHaveProperty('signature')
1251
+ expect(createSignatureResult.signature.length).toBeGreaterThan(0)
1252
+
1253
+ const verifySignatureArgs = {
1254
+ data,
1255
+ signature: createSignatureResult.signature,
1256
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1257
+ keyID: 'test-key-id',
1258
+ counterparty: userKey.toPublicKey().toString()
1259
+ }
1260
+ const verifySignatureResult = await counterpartyWallet.verifySignature(verifySignatureArgs)
1261
+ expect(verifySignatureResult).toEqual({ valid: true })
1262
+ })
1263
+
1264
+ it('should throw an error for invalid signature verification', async () => {
1265
+ const userKey = PrivateKey.fromRandom()
1266
+ const counterpartyWallet = createTestWalletWire(new ProtoWallet(PrivateKey.fromRandom()))
1267
+
1268
+ const verifySignatureArgs = {
1269
+ data: sampleData,
1270
+ signature: [0x00],
1271
+ protocolID: [2, 'tests'] as [0 | 1 | 2, string],
1272
+ keyID: 'test-key-id',
1273
+ counterparty: userKey.toPublicKey().toString()
1274
+ }
1275
+ await expect(counterpartyWallet.verifySignature(verifySignatureArgs)).rejects.toThrow()
1276
+ })
1277
+ })
1278
+
1279
+ describe('revealCounterpartyKeyLinkage', () => {
1280
+ it('should reveal counterparty key linkage correctly', async () => {
1281
+ const proverKey = PrivateKey.fromRandom()
1282
+ const counterpartyKey = PrivateKey.fromRandom()
1283
+ const verifierKey = PrivateKey.fromRandom()
1284
+
1285
+ const proverWallet = createTestWalletWire(new ProtoWallet(proverKey))
1286
+ const verifierWallet = createTestWalletWire(new ProtoWallet(verifierKey))
1287
+
1288
+ const args = {
1289
+ counterparty: counterpartyKey.toPublicKey().toString(),
1290
+ verifier: verifierKey.toPublicKey().toString()
1291
+ }
1292
+
1293
+ const revelation = await proverWallet.revealCounterpartyKeyLinkage(args)
1294
+ expect(revelation.encryptedLinkageProof).toBeDefined()
1295
+
1296
+ const decryptArgs = {
1297
+ ciphertext: revelation.encryptedLinkage,
1298
+ protocolID: [2, 'counterparty linkage revelation'] as [0 | 1 | 2, string],
1299
+ keyID: revelation.revelationTime,
1300
+ counterparty: proverKey.toPublicKey().toString()
1301
+ }
1302
+ const decryptedResult = await verifierWallet.decrypt(decryptArgs)
1303
+
1304
+ const expectedLinkage = proverKey.deriveSharedSecret(counterpartyKey.toPublicKey()).encode(true)
1305
+ expect(decryptedResult.plaintext).toEqual(expectedLinkage)
1306
+ })
1307
+
1308
+ it('should throw an error when trying to reveal linkage without privilege', async () => {
1309
+ const proverKey = PrivateKey.fromRandom()
1310
+ const counterpartyKey = PrivateKey.fromRandom()
1311
+ const verifierKey = PrivateKey.fromRandom()
1312
+
1313
+ const proverWallet = createTestWalletWire(new ProtoWallet(proverKey))
1314
+
1315
+ const args = {
1316
+ counterparty: counterpartyKey.toPublicKey().toString(),
1317
+ verifier: verifierKey.toPublicKey().toString(),
1318
+ privileged: true
1319
+ }
1320
+
1321
+ await expect(proverWallet.revealCounterpartyKeyLinkage(args)).rejects.toThrow()
1322
+ })
1323
+ })
1324
+
1325
+ describe('getVersion and getNetwork', () => {
1326
+ it('should return the correct version', async () => {
1327
+ const wallet = createTestWalletWire(new ProtoWallet('anyone'))
1328
+ const result = await wallet.getVersion({})
1329
+ expect(result).toHaveProperty('version')
1330
+ expect(typeof result.version).toBe('string')
1331
+ })
1332
+
1333
+ it('should return the correct network', async () => {
1334
+ const wallet = createTestWalletWire(new ProtoWallet('anyone'))
1335
+ const result = await wallet.getNetwork({})
1336
+ expect(result).toHaveProperty('network')
1337
+ expect(['mainnet', 'testnet']).toContain(result.network)
1338
+ })
1339
+ })
1340
+
1341
+ describe('isAuthenticated and waitForAuthentication', () => {
1342
+ it('should return authentication status', async () => {
1343
+ const wallet = createTestWalletWire(new ProtoWallet('anyone'))
1344
+ const result = await wallet.isAuthenticated({})
1345
+ expect(result).toHaveProperty('authenticated')
1346
+ expect(typeof result.authenticated).toBe('boolean')
1347
+ })
1348
+
1349
+ it('should wait for authentication', async () => {
1350
+ const wallet = createTestWalletWire(new ProtoWallet('anyone'))
1351
+ const result = await wallet.waitForAuthentication({})
1352
+ expect(result).toEqual({ authenticated: true })
1353
+ })
1354
+ })
1355
+ describe('acquireCertificate', () => {
1356
+ it('should acquire a certificate with valid inputs', async () => {
1357
+ // Mock the acquireCertificate method
1358
+ const acquireCertificateMock = jest.fn().mockResolvedValue({
1359
+ type: Utils.toBase64(new Array(32).fill(1)),
1360
+ subject: '02' + 'a'.repeat(64),
1361
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1362
+ certifier: '02' + 'b'.repeat(64),
1363
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1364
+ signature: '00',
1365
+ fields: {
1366
+ field1: 'value1',
1367
+ field2: 'value2'
1368
+ }
1369
+ })
1370
+ const wallet = createTestWalletWire(
1371
+ mockUnsupportedMethods({
1372
+ acquireCertificate: acquireCertificateMock
1373
+ })
1374
+ )
1375
+
1376
+ const args = {
1377
+ type: Utils.toBase64(new Array(32).fill(1)),
1378
+ certifier: '02' + 'b'.repeat(64),
1379
+ acquisitionProtocol: 'direct' as 'direct',
1380
+ fields: {
1381
+ field1: 'value1',
1382
+ field2: 'value2'
1383
+ },
1384
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1385
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1386
+ signature: '00',
1387
+ keyringRevealer: 'certifier',
1388
+ keyringForSubject: {}
1389
+ }
1390
+ const result = await wallet.acquireCertificate(args)
1391
+ expect(result).toHaveProperty('type')
1392
+ expect(result).toHaveProperty('subject')
1393
+ expect(result).toHaveProperty('serialNumber')
1394
+ expect(result).toHaveProperty('certifier')
1395
+ expect(result).toHaveProperty('revocationOutpoint')
1396
+ expect(result).toHaveProperty('signature')
1397
+ expect(result).toHaveProperty('fields')
1398
+ expect(acquireCertificateMock).toHaveBeenCalledWith(args, '')
1399
+ })
1400
+ it('should acquire a certificate using acquisitionProtocol "direct" with keyringRevealer as "certifier"', async () => {
1401
+ // Mock the acquireCertificate method
1402
+ const acquireCertificateMock = jest.fn().mockResolvedValue({
1403
+ type: Utils.toBase64(new Array(32).fill(1)),
1404
+ subject: '02' + 'a'.repeat(64),
1405
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1406
+ certifier: '02' + 'b'.repeat(64),
1407
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1408
+ signature: '00',
1409
+ fields: {
1410
+ field1: 'value1',
1411
+ field2: 'value2'
1412
+ }
1413
+ })
1414
+ const wallet = createTestWalletWire(
1415
+ mockUnsupportedMethods({
1416
+ acquireCertificate: acquireCertificateMock
1417
+ })
1418
+ )
1419
+
1420
+ const args = {
1421
+ type: Utils.toBase64(new Array(32).fill(1)),
1422
+ certifier: '02' + 'b'.repeat(64),
1423
+ acquisitionProtocol: 'direct' as 'direct',
1424
+ fields: {
1425
+ field1: 'value1',
1426
+ field2: 'value2'
1427
+ },
1428
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1429
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1430
+ signature: '00',
1431
+ keyringRevealer: 'certifier' as 'certifier',
1432
+ keyringForSubject: {
1433
+ field1: Utils.toBase64([0x01, 0x02, 0x03]),
1434
+ field2: Utils.toBase64([0x04, 0x05, 0x06])
1435
+ }
1436
+ }
1437
+ const result = await wallet.acquireCertificate(args)
1438
+ expect(result).toHaveProperty('type')
1439
+ expect(result).toHaveProperty('subject')
1440
+ expect(result).toHaveProperty('serialNumber')
1441
+ expect(result).toHaveProperty('certifier')
1442
+ expect(result).toHaveProperty('revocationOutpoint')
1443
+ expect(result).toHaveProperty('signature')
1444
+ expect(result).toHaveProperty('fields')
1445
+ expect(acquireCertificateMock).toHaveBeenCalledWith(args, '')
1446
+ })
1447
+
1448
+ it('should acquire a certificate using acquisitionProtocol "direct" with keyringRevealer as PubKeyHex', async () => {
1449
+ // Mock the acquireCertificate method
1450
+ const acquireCertificateMock = jest.fn().mockResolvedValue({
1451
+ type: Utils.toBase64(new Array(32).fill(1)),
1452
+ subject: '02' + 'a'.repeat(64),
1453
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1454
+ certifier: '02' + 'b'.repeat(64),
1455
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1456
+ signature: '00',
1457
+ fields: {
1458
+ field1: 'value1',
1459
+ field2: 'value2'
1460
+ }
1461
+ })
1462
+ const keyringRevealerPubKey = '02' + 'c'.repeat(64)
1463
+ const wallet = createTestWalletWire(
1464
+ mockUnsupportedMethods({
1465
+ acquireCertificate: acquireCertificateMock
1466
+ })
1467
+ )
1468
+
1469
+ const args = {
1470
+ type: Utils.toBase64(new Array(32).fill(1)),
1471
+ certifier: '02' + 'b'.repeat(64),
1472
+ acquisitionProtocol: 'direct' as 'direct',
1473
+ fields: {
1474
+ field1: 'value1',
1475
+ field2: 'value2'
1476
+ },
1477
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1478
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1479
+ signature: '00',
1480
+ keyringRevealer: keyringRevealerPubKey,
1481
+ keyringForSubject: {
1482
+ field1: Utils.toBase64([0x01, 0x02, 0x03]),
1483
+ field2: Utils.toBase64([0x04, 0x05, 0x06])
1484
+ }
1485
+ }
1486
+ const result = await wallet.acquireCertificate(args)
1487
+ expect(result).toHaveProperty('type')
1488
+ expect(result).toHaveProperty('subject')
1489
+ expect(result).toHaveProperty('serialNumber')
1490
+ expect(result).toHaveProperty('certifier')
1491
+ expect(result).toHaveProperty('revocationOutpoint')
1492
+ expect(result).toHaveProperty('signature')
1493
+ expect(result).toHaveProperty('fields')
1494
+ expect(acquireCertificateMock).toHaveBeenCalledWith(args, '')
1495
+ })
1496
+
1497
+ it('should acquire a certificate using acquisitionProtocol "issuance"', async () => {
1498
+ // Mock the acquireCertificate method
1499
+ const acquireCertificateMock = jest.fn().mockResolvedValue({
1500
+ type: Utils.toBase64(new Array(32).fill(1)),
1501
+ subject: '02' + 'd'.repeat(64),
1502
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1503
+ certifier: '02' + 'b'.repeat(64),
1504
+ revocationOutpoint: 'cafebabedeadbeefcafebabedeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.1',
1505
+ signature: '01',
1506
+ fields: {
1507
+ field3: 'value3',
1508
+ field4: 'value4'
1509
+ }
1510
+ })
1511
+ const wallet = createTestWalletWire(
1512
+ mockUnsupportedMethods({
1513
+ acquireCertificate: acquireCertificateMock
1514
+ })
1515
+ )
1516
+
1517
+ const args = {
1518
+ type: Utils.toBase64(new Array(32).fill(1)),
1519
+ certifier: '02' + 'b'.repeat(64),
1520
+ acquisitionProtocol: 'issuance' as 'issuance',
1521
+ fields: {
1522
+ field3: 'value3',
1523
+ field4: 'value4'
1524
+ },
1525
+ certifierUrl: 'https://certifier.example.com/api/issue'
1526
+ }
1527
+ const result = await wallet.acquireCertificate(args)
1528
+ expect(result).toHaveProperty('type')
1529
+ expect(result).toHaveProperty('subject')
1530
+ expect(result).toHaveProperty('serialNumber')
1531
+ expect(result).toHaveProperty('certifier')
1532
+ expect(result).toHaveProperty('revocationOutpoint')
1533
+ expect(result).toHaveProperty('signature')
1534
+ expect(result).toHaveProperty('fields')
1535
+ expect(acquireCertificateMock).toHaveBeenCalledWith(args, '')
1536
+ })
1537
+
1538
+ it('should handle optional keyringForSubject being empty in "direct" protocol', async () => {
1539
+ // Mock the acquireCertificate method
1540
+ const acquireCertificateMock = jest.fn().mockResolvedValue({
1541
+ type: Utils.toBase64(new Array(32).fill(1)),
1542
+ subject: '02' + 'e'.repeat(64),
1543
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1544
+ certifier: '02' + 'b'.repeat(64),
1545
+ revocationOutpoint: 'beadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbead.2',
1546
+ signature: '02',
1547
+ fields: {
1548
+ field5: 'value5'
1549
+ }
1550
+ })
1551
+ const wallet = createTestWalletWire(
1552
+ mockUnsupportedMethods({
1553
+ acquireCertificate: acquireCertificateMock
1554
+ })
1555
+ )
1556
+
1557
+ const args = {
1558
+ type: Utils.toBase64(new Array(32).fill(1)),
1559
+ certifier: '02' + 'b'.repeat(64),
1560
+ acquisitionProtocol: 'direct' as 'direct',
1561
+ fields: {
1562
+ field5: 'value5'
1563
+ },
1564
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1565
+ revocationOutpoint: 'beadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbead.2',
1566
+ signature: '02',
1567
+ keyringRevealer: 'certifier' as 'certifier',
1568
+ keyringForSubject: {} // Empty keyring
1569
+ }
1570
+ const result = await wallet.acquireCertificate(args)
1571
+ expect(result).toHaveProperty('type')
1572
+ expect(result).toHaveProperty('subject')
1573
+ expect(result).toHaveProperty('serialNumber')
1574
+ expect(result).toHaveProperty('certifier')
1575
+ expect(result).toHaveProperty('revocationOutpoint')
1576
+ expect(result).toHaveProperty('signature')
1577
+ expect(result).toHaveProperty('fields')
1578
+ expect(result.fields).toEqual({ field5: 'value5' })
1579
+ expect(acquireCertificateMock).toHaveBeenCalledWith(args, '')
1580
+ })
1581
+ })
1582
+
1583
+ describe('listCertificates', () => {
1584
+ it('should list certificates with valid inputs', async () => {
1585
+ // Mock the listCertificates method
1586
+ const listCertificatesMock = jest.fn().mockResolvedValue({
1587
+ totalCertificates: 1,
1588
+ certificates: [
1589
+ {
1590
+ type: Utils.toBase64(new Array(32).fill(1)),
1591
+ subject: '02' + 'a'.repeat(64),
1592
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1593
+ certifier: '02' + 'b'.repeat(64),
1594
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1595
+ signature: '00',
1596
+ fields: {
1597
+ field1: 'value1',
1598
+ field2: 'value2'
1599
+ }
1600
+ }
1601
+ ]
1602
+ })
1603
+ const wallet = createTestWalletWire(
1604
+ mockUnsupportedMethods({
1605
+ listCertificates: listCertificatesMock
1606
+ })
1607
+ )
1608
+
1609
+ const args = {
1610
+ certifiers: ['02' + 'b'.repeat(64)],
1611
+ types: [Utils.toBase64(new Array(32).fill(1))],
1612
+ limit: 10,
1613
+ offset: 0
1614
+ }
1615
+ const result = await wallet.listCertificates(args)
1616
+ expect(result).toHaveProperty('totalCertificates')
1617
+ expect(result).toHaveProperty('certificates')
1618
+ expect(Array.isArray(result.certificates)).toBe(true)
1619
+ expect(listCertificatesMock).toHaveBeenCalledWith(args, '')
1620
+ })
1621
+ it('should list certificates with multiple fields in each certificate', async () => {
1622
+ // Mock the listCertificates method
1623
+ const listCertificatesMock = jest.fn().mockResolvedValue({
1624
+ totalCertificates: 2,
1625
+ certificates: [
1626
+ {
1627
+ type: Utils.toBase64(new Array(32).fill(1)),
1628
+ subject: '02' + 'a'.repeat(64),
1629
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1630
+ certifier: '02' + 'b'.repeat(64),
1631
+ revocationOutpoint: 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.0',
1632
+ signature: '00',
1633
+ fields: {
1634
+ field1: 'value1',
1635
+ field2: 'value2'
1636
+ }
1637
+ },
1638
+ {
1639
+ type: Utils.toBase64(new Array(32).fill(1)),
1640
+ subject: '02' + 'c'.repeat(64),
1641
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1642
+ certifier: '02' + 'b'.repeat(64),
1643
+ revocationOutpoint: 'cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe.1',
1644
+ signature: '01',
1645
+ fields: {
1646
+ field3: 'value3',
1647
+ field4: 'value4',
1648
+ field5: 'value5'
1649
+ }
1650
+ }
1651
+ ]
1652
+ })
1653
+ const wallet = createTestWalletWire(
1654
+ mockUnsupportedMethods({
1655
+ listCertificates: listCertificatesMock
1656
+ })
1657
+ )
1658
+
1659
+ const args = {
1660
+ certifiers: ['02' + 'b'.repeat(64)],
1661
+ types: [
1662
+ Utils.toBase64(new Array(32).fill(1)),
1663
+ Utils.toBase64(new Array(32).fill(2))
1664
+ ],
1665
+ limit: 10,
1666
+ offset: 0
1667
+ }
1668
+ const result = await wallet.listCertificates(args)
1669
+ expect(result).toHaveProperty('totalCertificates', 2)
1670
+ expect(result.certificates.length).toBe(2)
1671
+ expect(result.certificates[0].fields).toEqual({ field1: 'value1', field2: 'value2' })
1672
+ expect(result.certificates[1].fields).toEqual({
1673
+ field3: 'value3',
1674
+ field4: 'value4',
1675
+ field5: 'value5'
1676
+ })
1677
+ expect(listCertificatesMock).toHaveBeenCalledWith(args, '')
1678
+ })
1679
+
1680
+ it('should list certificates when privileged is true', async () => {
1681
+ // Mock the listCertificates method
1682
+ const listCertificatesMock = jest.fn().mockResolvedValue({
1683
+ totalCertificates: 1,
1684
+ certificates: [
1685
+ {
1686
+ type: Utils.toBase64(new Array(32).fill(1)),
1687
+ subject: '02' + 'd'.repeat(64),
1688
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1689
+ certifier: '02' + 'e'.repeat(64),
1690
+ revocationOutpoint: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe.2',
1691
+ signature: '03',
1692
+ fields: {
1693
+ field6: 'value6'
1694
+ }
1695
+ }
1696
+ ]
1697
+ })
1698
+ const wallet = createTestWalletWire(
1699
+ mockUnsupportedMethods({
1700
+ listCertificates: listCertificatesMock
1701
+ })
1702
+ )
1703
+
1704
+ const args = {
1705
+ certifiers: ['02' + 'e'.repeat(64)],
1706
+ types: [Utils.toBase64(new Array(32).fill(1))],
1707
+ limit: 10,
1708
+ offset: 0,
1709
+ privileged: true,
1710
+ privilegedReason: 'Testing privileged access'
1711
+ }
1712
+ const result = await wallet.listCertificates(args)
1713
+ expect(result).toHaveProperty('totalCertificates', 1)
1714
+ expect(result.certificates[0].fields).toEqual({ field6: 'value6' })
1715
+ expect(listCertificatesMock).toHaveBeenCalledWith(args, '')
1716
+ })
1717
+ })
1718
+
1719
+ describe('proveCertificate', () => {
1720
+ it('should prove a certificate with valid inputs', async () => {
1721
+ // Mock the proveCertificate method
1722
+ const proveCertificateMock = jest.fn().mockResolvedValue({
1723
+ keyringForVerifier: {
1724
+ field1: Utils.toBase64([0x01, 0x02, 0x03]),
1725
+ field2: Utils.toBase64([0x04, 0x05, 0x06])
1726
+ }
1727
+ })
1728
+ const wallet = createTestWalletWire(
1729
+ mockUnsupportedMethods({
1730
+ proveCertificate: proveCertificateMock
1731
+ })
1732
+ )
1733
+
1734
+ const args = {
1735
+ certificate: {
1736
+ type: Utils.toBase64(new Array(32).fill(1)),
1737
+ subject: '02' + 'a'.repeat(64),
1738
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1739
+ certifier: '02' + 'b'.repeat(64),
1740
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1741
+ signature: '00',
1742
+ fields: {
1743
+ field1: 'value1',
1744
+ field2: 'value2'
1745
+ }
1746
+ },
1747
+ fieldsToReveal: ['field1'],
1748
+ verifier: '02' + 'c'.repeat(64)
1749
+ }
1750
+ const result = await wallet.proveCertificate(args)
1751
+ expect(result).toHaveProperty('keyringForVerifier')
1752
+ expect(proveCertificateMock).toHaveBeenCalledWith(args, '')
1753
+ })
1754
+ it('should prove a certificate revealing multiple fields', async () => {
1755
+ // Mock the proveCertificate method
1756
+ const proveCertificateMock = jest.fn().mockResolvedValue({
1757
+ keyringForVerifier: {
1758
+ field1: Utils.toBase64([0x01, 0x02, 0x03]),
1759
+ field2: Utils.toBase64([0x04, 0x05, 0x06])
1760
+ }
1761
+ })
1762
+ const wallet = createTestWalletWire(
1763
+ mockUnsupportedMethods({
1764
+ proveCertificate: proveCertificateMock
1765
+ })
1766
+ )
1767
+
1768
+ const args = {
1769
+ certificate: {
1770
+ type: Utils.toBase64(new Array(32).fill(1)),
1771
+ subject: '02' + 'a'.repeat(64),
1772
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1773
+ certifier: '02' + 'b'.repeat(64),
1774
+ revocationOutpoint: 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.0',
1775
+ signature: '00',
1776
+ fields: {
1777
+ field1: 'value1',
1778
+ field2: 'value2',
1779
+ field3: 'value3'
1780
+ }
1781
+ },
1782
+ fieldsToReveal: ['field1', 'field2'],
1783
+ verifier: '02' + 'f'.repeat(64)
1784
+ }
1785
+ const result = await wallet.proveCertificate(args)
1786
+ expect(result).toHaveProperty('keyringForVerifier')
1787
+ expect(Object.keys(result.keyringForVerifier)).toEqual(['field1', 'field2'])
1788
+ expect(proveCertificateMock).toHaveBeenCalledWith(args, '')
1789
+ })
1790
+
1791
+ it('should handle empty fieldsToReveal array (no fields revealed)', async () => {
1792
+ // Mock the proveCertificate method
1793
+ const proveCertificateMock = jest.fn().mockResolvedValue({
1794
+ keyringForVerifier: {}
1795
+ })
1796
+ const wallet = createTestWalletWire(
1797
+ mockUnsupportedMethods({
1798
+ proveCertificate: proveCertificateMock
1799
+ })
1800
+ )
1801
+
1802
+ const args = {
1803
+ certificate: {
1804
+ type: Utils.toBase64(new Array(32).fill(1)),
1805
+ subject: '02' + 'a'.repeat(64),
1806
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1807
+ certifier: '02' + 'b'.repeat(64),
1808
+ revocationOutpoint: 'cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe.1',
1809
+ signature: '01',
1810
+ fields: {
1811
+ field4: 'value4',
1812
+ field5: 'value5'
1813
+ }
1814
+ },
1815
+ fieldsToReveal: [],
1816
+ verifier: '02' + 'f'.repeat(64)
1817
+ }
1818
+ const result = await wallet.proveCertificate(args)
1819
+ expect(result).toHaveProperty('keyringForVerifier')
1820
+ expect(Object.keys(result.keyringForVerifier).length).toBe(0)
1821
+ expect(proveCertificateMock).toHaveBeenCalledWith(args, '')
1822
+ })
1823
+ })
1824
+
1825
+ describe('relinquishCertificate', () => {
1826
+ it('should relinquish a certificate with valid inputs', async () => {
1827
+ // Mock the relinquishCertificate method
1828
+ const relinquishCertificateMock = jest.fn().mockResolvedValue({ relinquished: true })
1829
+ const wallet = createTestWalletWire(
1830
+ mockUnsupportedMethods({
1831
+ relinquishCertificate: relinquishCertificateMock
1832
+ })
1833
+ )
1834
+
1835
+ const args = {
1836
+ type: Utils.toBase64(new Array(32).fill(1)),
1837
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1838
+ certifier: '02' + 'b'.repeat(64)
1839
+ }
1840
+ const result = await wallet.relinquishCertificate(args)
1841
+ expect(result).toEqual({ relinquished: true })
1842
+ expect(relinquishCertificateMock).toHaveBeenCalledWith(args, '')
1843
+ })
1844
+ })
1845
+
1846
+ describe('getHeight and getHeaderForHeight', () => {
1847
+ it('should get the current blockchain height', async () => {
1848
+ // Mock the getHeight method
1849
+ const getHeightMock = jest.fn().mockResolvedValue({ height: 680000 })
1850
+ const wallet = createTestWalletWire(
1851
+ mockUnsupportedMethods({
1852
+ getHeight: getHeightMock
1853
+ })
1854
+ )
1855
+
1856
+ const result = await wallet.getHeight({})
1857
+ expect(result).toHaveProperty('height')
1858
+ expect(typeof result.height).toBe('number')
1859
+ expect(result.height).toBeGreaterThan(0)
1860
+ expect(getHeightMock).toHaveBeenCalledWith({}, '')
1861
+ })
1862
+
1863
+ it('should throw an error when getHeight fails', async () => {
1864
+ // Mock the getHeight method to throw an error
1865
+ const getHeightMock = jest.fn().mockRejectedValue(new Error('Failed to get height'))
1866
+ const wallet = createTestWalletWire(
1867
+ mockUnsupportedMethods({
1868
+ getHeight: getHeightMock
1869
+ })
1870
+ )
1871
+ await expect(wallet.getHeight({})).rejects.toThrow('Failed to get height')
1872
+ expect(getHeightMock).toHaveBeenCalledWith({}, '')
1873
+ })
1874
+
1875
+ it('should get the header for a given height', async () => {
1876
+ // Mock the getHeaderForHeight method
1877
+ const getHeaderForHeightMock = jest.fn().mockResolvedValue({
1878
+ header: '00' + 'ff'.repeat(79)
1879
+ })
1880
+ const wallet = createTestWalletWire(
1881
+ mockUnsupportedMethods({
1882
+ getHeaderForHeight: getHeaderForHeightMock
1883
+ })
1884
+ )
1885
+
1886
+ const args = { height: 680000 }
1887
+ const result = await wallet.getHeaderForHeight(args)
1888
+ expect(result).toHaveProperty('header')
1889
+ expect(typeof result.header).toBe('string')
1890
+ expect(result.header.length).toBe(80 * 2) // 80 bytes in hex
1891
+ expect(getHeaderForHeightMock).toHaveBeenCalledWith(args, '')
1892
+ })
1893
+
1894
+ it('should throw an error when getHeaderForHeight fails', async () => {
1895
+ // Mock the getHeaderForHeight method to throw an error
1896
+ const getHeaderForHeightMock = jest.fn().mockRejectedValue(new Error('Failed to get header'))
1897
+ const wallet = createTestWalletWire(
1898
+ mockUnsupportedMethods({
1899
+ getHeaderForHeight: getHeaderForHeightMock
1900
+ })
1901
+ )
1902
+ const args = { height: -1 } // Invalid height
1903
+ await expect(wallet.getHeaderForHeight(args)).rejects.toThrow('Failed to get header')
1904
+ expect(getHeaderForHeightMock).toHaveBeenCalledWith(args, '')
1905
+ })
1906
+ })
1907
+
1908
+ describe('discoverByIdentityKey', () => {
1909
+ it('should discover certificates by identity key with valid inputs', async () => {
1910
+ // Mock the discoverByIdentityKey method
1911
+ const discoverByIdentityKeyMock = jest.fn().mockResolvedValue({
1912
+ totalCertificates: 1,
1913
+ certificates: [
1914
+ {
1915
+ type: Utils.toBase64(new Array(32).fill(1)),
1916
+ subject: '02' + 'a'.repeat(64),
1917
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1918
+ certifier: '02' + 'b'.repeat(64),
1919
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
1920
+ signature: '00',
1921
+ fields: {},
1922
+ certifierInfo: {
1923
+ name: 'Test Certifier',
1924
+ iconUrl: 'https://example.com/icon.png',
1925
+ description: 'Test description',
1926
+ trust: 5
1927
+ },
1928
+ publiclyRevealedKeyring: {},
1929
+ decryptedFields: {}
1930
+ }
1931
+ ]
1932
+ })
1933
+ const wallet = createTestWalletWire(
1934
+ mockUnsupportedMethods({
1935
+ discoverByIdentityKey: discoverByIdentityKeyMock
1936
+ })
1937
+ )
1938
+
1939
+ const args = {
1940
+ identityKey: '02' + 'a'.repeat(64),
1941
+ limit: 10,
1942
+ offset: 0
1943
+ }
1944
+ const result = await wallet.discoverByIdentityKey(args)
1945
+ expect(result).toHaveProperty('totalCertificates')
1946
+ expect(result).toHaveProperty('certificates')
1947
+ expect(Array.isArray(result.certificates)).toBe(true)
1948
+ expect(discoverByIdentityKeyMock).toHaveBeenCalledWith(args, '')
1949
+ })
1950
+ it('should discover certificates with empty decryptedFields and publiclyRevealedKeyring', async () => {
1951
+ // Mock the discoverByIdentityKey method
1952
+ const discoverByIdentityKeyMock = jest.fn().mockResolvedValue({
1953
+ totalCertificates: 1,
1954
+ certificates: [
1955
+ {
1956
+ type: Utils.toBase64(new Array(32).fill(1)),
1957
+ subject: '02' + 'a'.repeat(64),
1958
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
1959
+ certifier: '02' + 'b'.repeat(64),
1960
+ revocationOutpoint: 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.0',
1961
+ signature: '00',
1962
+ fields: {},
1963
+ certifierInfo: {
1964
+ name: 'Test Certifier',
1965
+ iconUrl: 'https://example.com/icon.png',
1966
+ description: 'Test description',
1967
+ trust: 5
1968
+ },
1969
+ publiclyRevealedKeyring: {},
1970
+ decryptedFields: {}
1971
+ }
1972
+ ]
1973
+ })
1974
+ const wallet = createTestWalletWire(
1975
+ mockUnsupportedMethods({
1976
+ discoverByIdentityKey: discoverByIdentityKeyMock
1977
+ })
1978
+ )
1979
+
1980
+ const args = {
1981
+ identityKey: '02' + 'a'.repeat(64),
1982
+ limit: 10,
1983
+ offset: 0
1984
+ }
1985
+ const result = await wallet.discoverByIdentityKey(args)
1986
+ expect(result).toHaveProperty('totalCertificates')
1987
+ expect(result.certificates.length).toBe(1)
1988
+ expect(result.certificates[0].publiclyRevealedKeyring).toEqual({})
1989
+ expect(result.certificates[0].decryptedFields).toEqual({})
1990
+ expect(discoverByIdentityKeyMock).toHaveBeenCalledWith(args, '')
1991
+ })
1992
+
1993
+ it('should discover multiple certificates with varying fields', async () => {
1994
+ // Mock the discoverByIdentityKey method
1995
+ const discoverByIdentityKeyMock = jest.fn().mockResolvedValue({
1996
+ totalCertificates: 2,
1997
+ certificates: [
1998
+ {
1999
+ type: Utils.toBase64(new Array(32).fill(1)),
2000
+ subject: '02' + 'a'.repeat(64),
2001
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
2002
+ certifier: '02' + 'b'.repeat(64),
2003
+ revocationOutpoint: 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.0',
2004
+ signature: '00',
2005
+ fields: {},
2006
+ certifierInfo: {
2007
+ name: 'Certifier One',
2008
+ iconUrl: 'https://example.com/icon1.png',
2009
+ description: 'First certifier',
2010
+ trust: 5
2011
+ },
2012
+ publiclyRevealedKeyring: {
2013
+ field1: Utils.toBase64([0x01])
2014
+ },
2015
+ decryptedFields: {
2016
+ fieldA: 'decryptedValueA'
2017
+ }
2018
+ },
2019
+ {
2020
+ type: Utils.toBase64(new Array(32).fill(1)),
2021
+ subject: '02' + 'a'.repeat(64),
2022
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
2023
+ certifier: '02' + 'c'.repeat(64),
2024
+ revocationOutpoint: 'cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe.1',
2025
+ signature: '01',
2026
+ fields: {},
2027
+ certifierInfo: {
2028
+ name: 'Certifier Two',
2029
+ iconUrl: 'https://example.com/icon2.png',
2030
+ description: 'Second certifier',
2031
+ trust: 7
2032
+ },
2033
+ publiclyRevealedKeyring: {},
2034
+ decryptedFields: {
2035
+ fieldB: 'decryptedValueB',
2036
+ fieldC: 'decryptedValueC'
2037
+ }
2038
+ }
2039
+ ]
2040
+ })
2041
+ const wallet = createTestWalletWire(
2042
+ mockUnsupportedMethods({
2043
+ discoverByIdentityKey: discoverByIdentityKeyMock
2044
+ })
2045
+ )
2046
+
2047
+ const args = {
2048
+ identityKey: '02' + 'a'.repeat(64),
2049
+ limit: 10,
2050
+ offset: 0
2051
+ }
2052
+ const result = await wallet.discoverByIdentityKey(args)
2053
+ expect(result).toHaveProperty('totalCertificates', 2)
2054
+ expect(result.certificates.length).toBe(2)
2055
+ expect(result.certificates[0].certifierInfo.name).toBe('Certifier One')
2056
+ expect(result.certificates[1].certifierInfo.name).toBe('Certifier Two')
2057
+ expect(discoverByIdentityKeyMock).toHaveBeenCalledWith(args, '')
2058
+ })
2059
+ })
2060
+
2061
+ describe('discoverByAttributes', () => {
2062
+ it('should discover certificates by attributes with valid inputs', async () => {
2063
+ // Mock the discoverByAttributes method
2064
+ const discoverByAttributesMock = jest.fn().mockResolvedValue({
2065
+ totalCertificates: 1,
2066
+ certificates: [
2067
+ {
2068
+ type: Utils.toBase64(new Array(32).fill(1)),
2069
+ subject: '02' + 'a'.repeat(64),
2070
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
2071
+ certifier: '02' + 'b'.repeat(64),
2072
+ revocationOutpoint: 'deadbeef20248806deadbeef20248806deadbeef20248806deadbeef20248806.0',
2073
+ signature: '00',
2074
+ fields: {},
2075
+ certifierInfo: {
2076
+ name: 'Test Certifier',
2077
+ iconUrl: 'https://example.com/icon.png',
2078
+ description: 'Test description',
2079
+ trust: 5
2080
+ },
2081
+ publiclyRevealedKeyring: {},
2082
+ decryptedFields: {}
2083
+ }
2084
+ ]
2085
+ })
2086
+ const wallet = createTestWalletWire(
2087
+ mockUnsupportedMethods({
2088
+ discoverByAttributes: discoverByAttributesMock
2089
+ })
2090
+ )
2091
+
2092
+ const args = {
2093
+ attributes: {
2094
+ field1: 'value1'
2095
+ },
2096
+ limit: 10,
2097
+ offset: 0
2098
+ }
2099
+ const result = await wallet.discoverByAttributes(args)
2100
+ expect(result).toHaveProperty('totalCertificates')
2101
+ expect(result).toHaveProperty('certificates')
2102
+ expect(Array.isArray(result.certificates)).toBe(true)
2103
+ expect(discoverByAttributesMock).toHaveBeenCalledWith(args, '')
2104
+ })
2105
+
2106
+ it('should throw an error with invalid inputs', async () => {
2107
+ // Mock the discoverByAttributes method to throw an error
2108
+ const discoverByAttributesMock = jest.fn().mockRejectedValue(new Error('Invalid inputs'))
2109
+ const wallet = createTestWalletWire(
2110
+ mockUnsupportedMethods({
2111
+ discoverByAttributes: discoverByAttributesMock
2112
+ })
2113
+ )
2114
+ const args = {
2115
+ attributes: {}
2116
+ }
2117
+ await expect(wallet.discoverByAttributes(args)).rejects.toThrow('Invalid inputs')
2118
+ expect(discoverByAttributesMock).toHaveBeenCalledWith(args, '')
2119
+ })
2120
+ it('should discover certificates matching provided attributes', async () => {
2121
+ // Mock the discoverByAttributes method
2122
+ const discoverByAttributesMock = jest.fn().mockResolvedValue({
2123
+ totalCertificates: 1,
2124
+ certificates: [
2125
+ {
2126
+ type: Utils.toBase64(new Array(32).fill(1)),
2127
+ subject: '02' + 'd'.repeat(64),
2128
+ serialNumber: Utils.toBase64(new Array(32).fill(2)),
2129
+ certifier: '02' + 'e'.repeat(64),
2130
+ revocationOutpoint: 'beadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbeadbead.2',
2131
+ signature: '02',
2132
+ fields: {},
2133
+ certifierInfo: {
2134
+ name: 'Certifier Three',
2135
+ iconUrl: 'https://example.com/icon3.png',
2136
+ description: 'Third certifier',
2137
+ trust: 8
2138
+ },
2139
+ publiclyRevealedKeyring: {
2140
+ fieldX: Utils.toBase64([0x0A, 0x0B])
2141
+ },
2142
+ decryptedFields: {
2143
+ fieldY: 'decryptedValueY'
2144
+ }
2145
+ }
2146
+ ]
2147
+ })
2148
+ const wallet = createTestWalletWire(
2149
+ mockUnsupportedMethods({
2150
+ discoverByAttributes: discoverByAttributesMock
2151
+ })
2152
+ )
2153
+
2154
+ const args = {
2155
+ attributes: {
2156
+ fieldY: 'decryptedValueY'
2157
+ },
2158
+ limit: 5,
2159
+ offset: 0
2160
+ }
2161
+ const result = await wallet.discoverByAttributes(args)
2162
+ expect(result).toHaveProperty('totalCertificates', 1)
2163
+ expect(result.certificates.length).toBe(1)
2164
+ expect(result.certificates[0].certifierInfo.name).toBe('Certifier Three')
2165
+ expect(result.certificates[0].decryptedFields.fieldY).toBe('decryptedValueY')
2166
+ expect(discoverByAttributesMock).toHaveBeenCalledWith(args, '')
2167
+ })
2168
+
2169
+ it('should return empty certificates array when no matches found', async () => {
2170
+ // Mock the discoverByAttributes method
2171
+ const discoverByAttributesMock = jest.fn().mockResolvedValue({
2172
+ totalCertificates: 0,
2173
+ certificates: []
2174
+ })
2175
+ const wallet = createTestWalletWire(
2176
+ mockUnsupportedMethods({
2177
+ discoverByAttributes: discoverByAttributesMock
2178
+ })
2179
+ )
2180
+
2181
+ const args = {
2182
+ attributes: {
2183
+ nonExistentField: 'noValue'
2184
+ },
2185
+ limit: 5,
2186
+ offset: 0
2187
+ }
2188
+ const result = await wallet.discoverByAttributes(args)
2189
+ expect(result).toHaveProperty('totalCertificates', 0)
2190
+ expect(result.certificates.length).toBe(0)
2191
+ expect(discoverByAttributesMock).toHaveBeenCalledWith(args, '')
2192
+ })
2193
+ })
2194
+ })