@bsv/sdk 1.3.36 → 1.4.1

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 (198) hide show
  1. package/dist/cjs/mod.js +3 -0
  2. package/dist/cjs/mod.js.map +1 -1
  3. package/dist/cjs/package.json +1 -1
  4. package/dist/cjs/src/auth/Peer.js +42 -14
  5. package/dist/cjs/src/auth/Peer.js.map +1 -1
  6. package/dist/cjs/src/auth/certificates/Certificate.js +50 -22
  7. package/dist/cjs/src/auth/certificates/Certificate.js.map +1 -1
  8. package/dist/cjs/src/auth/certificates/MasterCertificate.js +35 -10
  9. package/dist/cjs/src/auth/certificates/MasterCertificate.js.map +1 -1
  10. package/dist/cjs/src/auth/certificates/VerifiableCertificate.js +28 -4
  11. package/dist/cjs/src/auth/certificates/VerifiableCertificate.js.map +1 -1
  12. package/dist/cjs/src/auth/certificates/__tests/CompletedProtoWallet.js +5 -2
  13. package/dist/cjs/src/auth/certificates/__tests/CompletedProtoWallet.js.map +1 -1
  14. package/dist/cjs/src/auth/clients/AuthFetch.js +50 -20
  15. package/dist/cjs/src/auth/clients/AuthFetch.js.map +1 -1
  16. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js +40 -17
  17. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  18. package/dist/cjs/src/auth/utils/createNonce.js +31 -4
  19. package/dist/cjs/src/auth/utils/createNonce.js.map +1 -1
  20. package/dist/cjs/src/auth/utils/verifyNonce.js +26 -3
  21. package/dist/cjs/src/auth/utils/verifyNonce.js.map +1 -1
  22. package/dist/cjs/src/identity/IdentityClient.js +258 -0
  23. package/dist/cjs/src/identity/IdentityClient.js.map +1 -0
  24. package/dist/cjs/src/identity/index.js +19 -0
  25. package/dist/cjs/src/identity/index.js.map +1 -0
  26. package/dist/cjs/src/identity/types/index.js +30 -0
  27. package/dist/cjs/src/identity/types/index.js.map +1 -0
  28. package/dist/cjs/src/overlay-tools/LookupResolver.js +2 -2
  29. package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
  30. package/dist/cjs/src/primitives/utils.js.map +1 -1
  31. package/dist/cjs/src/registry/RegistryClient.js +392 -0
  32. package/dist/cjs/src/registry/RegistryClient.js.map +1 -0
  33. package/dist/cjs/src/registry/index.js +19 -0
  34. package/dist/cjs/src/registry/index.js.map +1 -0
  35. package/dist/cjs/src/registry/types/index.js +3 -0
  36. package/dist/cjs/src/registry/types/index.js.map +1 -0
  37. package/dist/cjs/src/storage/StorageUploader.js +93 -0
  38. package/dist/cjs/src/storage/StorageUploader.js.map +1 -0
  39. package/dist/cjs/src/storage/StorageUtils.js +73 -0
  40. package/dist/cjs/src/storage/StorageUtils.js.map +1 -0
  41. package/dist/cjs/src/storage/__test/StorageUploader.test.js +92 -0
  42. package/dist/cjs/src/storage/__test/StorageUploader.test.js.map +1 -0
  43. package/dist/cjs/src/storage/__test/StorageUtils.test.js +97 -0
  44. package/dist/cjs/src/storage/__test/StorageUtils.test.js.map +1 -0
  45. package/dist/cjs/src/storage/index.js +30 -0
  46. package/dist/cjs/src/storage/index.js.map +1 -0
  47. package/dist/cjs/src/wallet/WalletClient.js +4 -4
  48. package/dist/cjs/src/wallet/WalletClient.js.map +1 -1
  49. package/dist/cjs/src/wallet/substrates/HTTPWalletWire.js +26 -3
  50. package/dist/cjs/src/wallet/substrates/HTTPWalletWire.js.map +1 -1
  51. package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js +178 -155
  52. package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
  53. package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js +171 -148
  54. package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js.map +1 -1
  55. package/dist/cjs/src/wallet/substrates/XDM.js +29 -2
  56. package/dist/cjs/src/wallet/substrates/XDM.js.map +1 -1
  57. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  58. package/dist/esm/mod.js +3 -0
  59. package/dist/esm/mod.js.map +1 -1
  60. package/dist/esm/src/auth/Peer.js +7 -5
  61. package/dist/esm/src/auth/Peer.js.map +1 -1
  62. package/dist/esm/src/auth/certificates/Certificate.js +3 -1
  63. package/dist/esm/src/auth/certificates/Certificate.js.map +1 -1
  64. package/dist/esm/src/auth/certificates/MasterCertificate.js +3 -1
  65. package/dist/esm/src/auth/certificates/MasterCertificate.js.map +1 -1
  66. package/dist/esm/src/auth/certificates/VerifiableCertificate.js +2 -1
  67. package/dist/esm/src/auth/certificates/VerifiableCertificate.js.map +1 -1
  68. package/dist/esm/src/auth/certificates/__tests/CompletedProtoWallet.js +1 -1
  69. package/dist/esm/src/auth/certificates/__tests/CompletedProtoWallet.js.map +1 -1
  70. package/dist/esm/src/auth/clients/AuthFetch.js +5 -1
  71. package/dist/esm/src/auth/clients/AuthFetch.js.map +1 -1
  72. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js +1 -1
  73. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  74. package/dist/esm/src/auth/utils/createNonce.js +2 -1
  75. package/dist/esm/src/auth/utils/createNonce.js.map +1 -1
  76. package/dist/esm/src/auth/utils/verifyNonce.js +1 -1
  77. package/dist/esm/src/auth/utils/verifyNonce.js.map +1 -1
  78. package/dist/esm/src/identity/IdentityClient.js +255 -0
  79. package/dist/esm/src/identity/IdentityClient.js.map +1 -0
  80. package/dist/esm/src/identity/index.js +3 -0
  81. package/dist/esm/src/identity/index.js.map +1 -0
  82. package/dist/esm/src/identity/types/index.js +27 -0
  83. package/dist/esm/src/identity/types/index.js.map +1 -0
  84. package/dist/esm/src/overlay-tools/LookupResolver.js +2 -2
  85. package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
  86. package/dist/esm/src/primitives/utils.js.map +1 -1
  87. package/dist/esm/src/registry/RegistryClient.js +388 -0
  88. package/dist/esm/src/registry/RegistryClient.js.map +1 -0
  89. package/dist/esm/src/registry/index.js +3 -0
  90. package/dist/esm/src/registry/index.js.map +1 -0
  91. package/dist/esm/src/registry/types/index.js +2 -0
  92. package/dist/esm/src/registry/types/index.js.map +1 -0
  93. package/dist/esm/src/storage/StorageUploader.js +68 -0
  94. package/dist/esm/src/storage/StorageUploader.js.map +1 -0
  95. package/dist/esm/src/storage/StorageUtils.js +65 -0
  96. package/dist/esm/src/storage/StorageUtils.js.map +1 -0
  97. package/dist/esm/src/storage/__test/StorageUploader.test.js +64 -0
  98. package/dist/esm/src/storage/__test/StorageUploader.test.js.map +1 -0
  99. package/dist/esm/src/storage/__test/StorageUtils.test.js +72 -0
  100. package/dist/esm/src/storage/__test/StorageUtils.test.js.map +1 -0
  101. package/dist/esm/src/storage/index.js +3 -0
  102. package/dist/esm/src/storage/index.js.map +1 -0
  103. package/dist/esm/src/wallet/WalletClient.js +4 -4
  104. package/dist/esm/src/wallet/WalletClient.js.map +1 -1
  105. package/dist/esm/src/wallet/substrates/HTTPWalletWire.js +1 -1
  106. package/dist/esm/src/wallet/substrates/HTTPWalletWire.js.map +1 -1
  107. package/dist/esm/src/wallet/substrates/WalletWireProcessor.js +1 -1
  108. package/dist/esm/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
  109. package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js +2 -2
  110. package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js.map +1 -1
  111. package/dist/esm/src/wallet/substrates/XDM.js +2 -1
  112. package/dist/esm/src/wallet/substrates/XDM.js.map +1 -1
  113. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  114. package/dist/types/mod.d.ts +3 -0
  115. package/dist/types/mod.d.ts.map +1 -1
  116. package/dist/types/src/auth/Peer.d.ts +1 -1
  117. package/dist/types/src/auth/Peer.d.ts.map +1 -1
  118. package/dist/types/src/auth/certificates/Certificate.d.ts +2 -1
  119. package/dist/types/src/auth/certificates/Certificate.d.ts.map +1 -1
  120. package/dist/types/src/auth/certificates/MasterCertificate.d.ts +2 -1
  121. package/dist/types/src/auth/certificates/MasterCertificate.d.ts.map +1 -1
  122. package/dist/types/src/auth/certificates/VerifiableCertificate.d.ts +2 -1
  123. package/dist/types/src/auth/certificates/VerifiableCertificate.d.ts.map +1 -1
  124. package/dist/types/src/auth/certificates/__tests/CompletedProtoWallet.d.ts +1 -1
  125. package/dist/types/src/auth/certificates/__tests/CompletedProtoWallet.d.ts.map +1 -1
  126. package/dist/types/src/auth/clients/AuthFetch.d.ts +1 -1
  127. package/dist/types/src/auth/clients/AuthFetch.d.ts.map +1 -1
  128. package/dist/types/src/auth/utils/createNonce.d.ts +1 -1
  129. package/dist/types/src/auth/utils/createNonce.d.ts.map +1 -1
  130. package/dist/types/src/auth/utils/getVerifiableCertificates.d.ts +1 -1
  131. package/dist/types/src/auth/utils/getVerifiableCertificates.d.ts.map +1 -1
  132. package/dist/types/src/auth/utils/verifyNonce.d.ts +1 -1
  133. package/dist/types/src/auth/utils/verifyNonce.d.ts.map +1 -1
  134. package/dist/types/src/identity/IdentityClient.d.ts +50 -0
  135. package/dist/types/src/identity/IdentityClient.d.ts.map +1 -0
  136. package/dist/types/src/identity/index.d.ts +3 -0
  137. package/dist/types/src/identity/index.d.ts.map +1 -0
  138. package/dist/types/src/identity/types/index.d.ts +30 -0
  139. package/dist/types/src/identity/types/index.d.ts.map +1 -0
  140. package/dist/types/src/primitives/utils.d.ts +4 -1
  141. package/dist/types/src/primitives/utils.d.ts.map +1 -1
  142. package/dist/types/src/registry/RegistryClient.d.ts +94 -0
  143. package/dist/types/src/registry/RegistryClient.d.ts.map +1 -0
  144. package/dist/types/src/registry/index.d.ts +3 -0
  145. package/dist/types/src/registry/index.d.ts.map +1 -0
  146. package/dist/types/src/registry/types/index.d.ts +86 -0
  147. package/dist/types/src/registry/types/index.d.ts.map +1 -0
  148. package/dist/types/src/storage/StorageUploader.d.ts +40 -0
  149. package/dist/types/src/storage/StorageUploader.d.ts.map +1 -0
  150. package/dist/types/src/storage/StorageUtils.d.ts +31 -0
  151. package/dist/types/src/storage/StorageUtils.d.ts.map +1 -0
  152. package/dist/types/src/storage/__test/StorageUploader.test.d.ts +2 -0
  153. package/dist/types/src/storage/__test/StorageUploader.test.d.ts.map +1 -0
  154. package/dist/types/src/storage/__test/StorageUtils.test.d.ts +2 -0
  155. package/dist/types/src/storage/__test/StorageUtils.test.d.ts.map +1 -0
  156. package/dist/types/src/storage/index.d.ts +3 -0
  157. package/dist/types/src/storage/index.d.ts.map +1 -0
  158. package/dist/types/src/wallet/substrates/XDM.d.ts +1 -1
  159. package/dist/types/src/wallet/substrates/XDM.d.ts.map +1 -1
  160. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  161. package/dist/umd/bundle.js +1 -1
  162. package/docs/primitives.md +4 -1
  163. package/docs/storage.md +210 -0
  164. package/docs/wallet-substrates.md +0 -225
  165. package/mod.ts +3 -0
  166. package/package.json +11 -1
  167. package/src/auth/Peer.ts +8 -5
  168. package/src/auth/__tests/Peer.test.ts +31 -31
  169. package/src/auth/certificates/Certificate.ts +5 -5
  170. package/src/auth/certificates/MasterCertificate.ts +5 -5
  171. package/src/auth/certificates/VerifiableCertificate.ts +6 -6
  172. package/src/auth/certificates/__tests/CompletedProtoWallet.ts +1 -15
  173. package/src/auth/clients/AuthFetch.ts +6 -1
  174. package/src/auth/transports/SimplifiedFetchTransport.ts +1 -1
  175. package/src/auth/utils/createNonce.ts +3 -3
  176. package/src/auth/utils/getVerifiableCertificates.ts +1 -1
  177. package/src/auth/utils/verifyNonce.ts +2 -1
  178. package/src/identity/IdentityClient.ts +305 -0
  179. package/src/identity/README.md +93 -0
  180. package/src/identity/__tests/IdentityClient.test.ts +278 -0
  181. package/src/identity/index.ts +2 -0
  182. package/src/identity/types/index.ts +46 -0
  183. package/src/overlay-tools/LookupResolver.ts +2 -2
  184. package/src/primitives/utils.ts +1 -1
  185. package/src/registry/RegistryClient.ts +493 -0
  186. package/src/registry/__tests/RegistryClient.test.ts +444 -0
  187. package/src/registry/index.ts +2 -0
  188. package/src/registry/types/index.ts +101 -0
  189. package/src/storage/StorageUploader.ts +108 -0
  190. package/src/storage/StorageUtils.ts +66 -0
  191. package/src/storage/__test/StorageUploader.test.ts +80 -0
  192. package/src/storage/__test/StorageUtils.test.ts +86 -0
  193. package/src/storage/index.ts +2 -0
  194. package/src/wallet/WalletClient.ts +4 -4
  195. package/src/wallet/substrates/HTTPWalletWire.ts +1 -1
  196. package/src/wallet/substrates/WalletWireProcessor.ts +1 -1
  197. package/src/wallet/substrates/WalletWireTransceiver.ts +2 -2
  198. package/src/wallet/substrates/XDM.ts +3 -2
@@ -0,0 +1,493 @@
1
+ import {
2
+ WalletInterface,
3
+ WalletProtocol,
4
+ WalletClient
5
+ ,
6
+ PubKeyHex
7
+ } from '../wallet/index.js'
8
+ import {
9
+ Utils
10
+ } from '../primitives/index.js'
11
+ import {
12
+ Transaction,
13
+ BroadcastResponse,
14
+ BroadcastFailure
15
+ } from '../transaction/index.js'
16
+ import {
17
+ LookupResolver,
18
+ TopicBroadcaster
19
+ } from '../overlay-tools/index.js'
20
+ import {
21
+ PushDrop,
22
+ LockingScript
23
+ } from '../script/index.js'
24
+ import {
25
+ CertificateFieldDescriptor,
26
+ DefinitionData,
27
+ DefinitionType,
28
+ RegistryQueryMapping,
29
+ RegistryRecord
30
+ } from './types/index.js'
31
+
32
+ const REGISTRANT_TOKEN_AMOUNT = 1
33
+
34
+ /**
35
+ * RegistryClient manages on-chain registry definitions for three types:
36
+ * - BasketMap (basket-based items)
37
+ * - ProtoMap (protocol-based items)
38
+ * - CertMap (certificate-based items)
39
+ *
40
+ * It provides methods to:
41
+ * - Register new definitions using pushdrop-based UTXOs.
42
+ * - Resolve existing definitions using a overlay lookup service.
43
+ * - List registry entries associated with the operator's wallet.
44
+ * - Revoke an existing registry entry by spending its UTXO.
45
+ *
46
+ * Registry operators use this client to establish and manage
47
+ * canonical references for baskets, protocols, and certificates types.
48
+ */
49
+ export class RegistryClient {
50
+ private network: 'mainnet' | 'testnet'
51
+ constructor (
52
+ private readonly wallet: WalletInterface = new WalletClient()
53
+ ) { }
54
+
55
+ /**
56
+ * Publishes a new on-chain definition for baskets, protocols, or certificates.
57
+ * The definition data is encoded in a pushdrop-based UTXO.
58
+ *
59
+ * Registry operators (i.e., identity key owners) can create these definitions
60
+ * to establish canonical references for basket IDs, protocol specs, or certificate schemas.
61
+ *
62
+ * @param data - The structured information needed to register an item of kind 'basket', 'protocol', or 'certificate'.
63
+ * @returns A promise with the broadcast result or failure.
64
+ */
65
+ async registerDefinition (data: DefinitionData): Promise<BroadcastResponse | BroadcastFailure> {
66
+ const registryOperator = (await this.wallet.getPublicKey({ identityKey: true })).publicKey
67
+ const pushdrop = new PushDrop(this.wallet)
68
+
69
+ // Build the array of fields, depending on the definition type
70
+ const fields = this.buildPushDropFields(data, registryOperator)
71
+ const protocol = this.getWalletProtocol(data.definitionType)
72
+
73
+ // Lock the fields
74
+ const lockingScript = await pushdrop.lock(
75
+ fields,
76
+ protocol,
77
+ '1',
78
+ 'self'
79
+ )
80
+
81
+ // Create a transaction
82
+ const { tx } = await this.wallet.createAction({
83
+ description: `Register a new ${data.definitionType} item`,
84
+ outputs: [
85
+ {
86
+ satoshis: REGISTRANT_TOKEN_AMOUNT,
87
+ lockingScript: lockingScript.toHex(),
88
+ outputDescription: `New ${data.definitionType} registration token`
89
+ }
90
+ ]
91
+ })
92
+
93
+ if (tx === undefined) {
94
+ throw new Error(`Failed to create ${data.definitionType} registration transaction!`)
95
+ }
96
+
97
+ // Broadcast
98
+
99
+ const broadcaster = new TopicBroadcaster(
100
+ [this.getBroadcastTopic(data.definitionType)],
101
+ {
102
+ networkPreset: this.network ??= (await (this.wallet.getNetwork({}))).network
103
+ }
104
+ )
105
+ return await broadcaster.broadcast(Transaction.fromAtomicBEEF(tx))
106
+ }
107
+
108
+ /**
109
+ * Resolves registrant tokens of a particular type using a lookup service.
110
+ *
111
+ * The query object shape depends on the registry type:
112
+ * - For "basket", the query is of type BasketMapQuery:
113
+ * { basketID?: string; name?: string; registryOperators?: string[]; }
114
+ * - For "protocol", the query is of type ProtoMapQuery:
115
+ * { name?: string; registryOperators?: string[]; protocolID?: string; securityLevel?: number; }
116
+ * - For "certificate", the query is of type CertMapQuery:
117
+ * { type?: string; name?: string; registryOperators?: string[]; }
118
+ *
119
+ * @param definitionType - The registry type, which can be 'basket', 'protocol', or 'certificate'.
120
+ * @param query - The query object used to filter registry records, whose shape is determined by the registry type.
121
+ * @returns A promise that resolves to an array of matching registry records.
122
+ */
123
+ async resolve<T extends DefinitionType>(
124
+ definitionType: T,
125
+ query: RegistryQueryMapping[T]
126
+ ): Promise<DefinitionData[]> {
127
+ const resolver = new LookupResolver()
128
+
129
+ // The service name depends on the kind
130
+ const serviceName = this.getServiceName(definitionType)
131
+ const result = await resolver.query({
132
+ service: serviceName,
133
+ query
134
+ })
135
+
136
+ if (result.type !== 'output-list') {
137
+ return []
138
+ }
139
+
140
+ const parsedRegistryRecords: DefinitionData[] = []
141
+ for (const output of result.outputs) {
142
+ try {
143
+ const parsedTx = Transaction.fromAtomicBEEF(output.beef)
144
+ const record = await this.parseLockingScript(definitionType, parsedTx.outputs[output.outputIndex].lockingScript)
145
+ parsedRegistryRecords.push(record)
146
+ } catch {
147
+ // skip
148
+ }
149
+ }
150
+ return parsedRegistryRecords
151
+ }
152
+
153
+ /**
154
+ * Lists the registry operator's published definitions for the given type.
155
+ *
156
+ * Returns parsed registry records including transaction details such as txid, outputIndex, satoshis, and the locking script.
157
+ *
158
+ * @param definitionType - The type of registry definition to list ('basket', 'protocol', or 'certificate').
159
+ * @returns A promise that resolves to an array of RegistryRecord objects.
160
+ */
161
+ async listOwnRegistryEntries (definitionType: DefinitionType): Promise<RegistryRecord[]> {
162
+ const relevantBasketName = this.getBasketName(definitionType)
163
+ const { outputs } = await this.wallet.listOutputs({
164
+ basket: relevantBasketName,
165
+ include: 'locking scripts'
166
+ })
167
+ const results: RegistryRecord[] = []
168
+
169
+ for (const output of outputs) {
170
+ if (output.spendable) {
171
+ continue
172
+ }
173
+ try {
174
+ const record = await this.parseLockingScript(definitionType, LockingScript.fromHex(output.lockingScript as string))
175
+ const [txid, outputIndex] = output.outpoint.split('.')
176
+ results.push({
177
+ ...record,
178
+ txid,
179
+ outputIndex: Number(outputIndex),
180
+ satoshis: output.satoshis,
181
+ lockingScript: output.lockingScript as string
182
+ })
183
+ } catch {
184
+ // ignore parse errors
185
+ }
186
+ }
187
+
188
+ return results
189
+ }
190
+
191
+ /**
192
+ * Revokes a registry record by spending its associated UTXO.
193
+ *
194
+ * This function creates a transaction that spends the UTXO corresponding to the provided registry record,
195
+ * revoking the registry entry. It prepares an unlocker using the appropriate wallet protocol,
196
+ * builds a signable transaction, signs it, and then broadcasts the finalized transaction.
197
+ *
198
+ * @param registryRecord - The registry record to revoke. It must include a valid txid, outputIndex, and lockingScript.
199
+ * @returns A promise that resolves with either a BroadcastResponse upon success or a BroadcastFailure on error.
200
+ * @throws If required fields are missing or if transaction creation/signing fails.
201
+ */
202
+ async revokeOwnRegistryEntry (
203
+ registryRecord: RegistryRecord
204
+ ): Promise<BroadcastResponse | BroadcastFailure> {
205
+ if (registryRecord.txid === undefined || typeof registryRecord.outputIndex === 'undefined' || registryRecord.lockingScript === undefined) {
206
+ throw new Error('Invalid record. Missing txid, outputIndex, or lockingScript.')
207
+ }
208
+
209
+ // Prepare the unlocker
210
+ const pushdrop = new PushDrop(this.wallet)
211
+ const unlocker = await pushdrop.unlock(
212
+ this.getWalletProtocol(registryRecord.definitionType),
213
+ '1',
214
+ 'anyone'
215
+ )
216
+
217
+ const itemIdentifier =
218
+ registryRecord.definitionType === 'basket'
219
+ ? registryRecord.basketID
220
+ : registryRecord.definitionType === 'protocol'
221
+ ? registryRecord.name
222
+ : registryRecord.definitionType === 'certificate'
223
+ ? (registryRecord.name !== undefined ? registryRecord.name : registryRecord.type)
224
+ : 'unknown'
225
+
226
+ const description = `Revoke ${registryRecord.definitionType} item: ${String(itemIdentifier)}`
227
+
228
+ // Create a new transaction that spends the UTXO
229
+ const outpoint = `${registryRecord.txid}.${registryRecord.outputIndex}`
230
+ const { signableTransaction } = await this.wallet.createAction({
231
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
232
+ description,
233
+ inputs: [
234
+ {
235
+ outpoint,
236
+ unlockingScriptLength: 73,
237
+ inputDescription: `Revoking ${registryRecord.definitionType} token`
238
+ }
239
+ ]
240
+ })
241
+
242
+ if (signableTransaction === undefined) {
243
+ throw new Error('Failed to create signable transaction.')
244
+ }
245
+
246
+ // Build a transaction object, sign with the unlock script
247
+ const tx = Transaction.fromBEEF(signableTransaction.tx)
248
+ const finalUnlockScript = await unlocker.sign(tx, registryRecord.outputIndex)
249
+
250
+ // Complete the signing
251
+ const { tx: signedTx } = await this.wallet.signAction({
252
+ reference: signableTransaction.reference,
253
+ spends: {
254
+ [registryRecord.outputIndex]: {
255
+ unlockingScript: finalUnlockScript.toHex()
256
+ }
257
+ }
258
+ })
259
+
260
+ if (signedTx === undefined) {
261
+ throw new Error('Failed to finalize the transaction signature.')
262
+ }
263
+
264
+ // Broadcast
265
+ const broadcaster = new TopicBroadcaster(
266
+ [this.getBroadcastTopic(registryRecord.definitionType)],
267
+ {
268
+ networkPreset: this.network ??= (await (this.wallet.getNetwork({}))).network
269
+ }
270
+ )
271
+ return await broadcaster.broadcast(Transaction.fromAtomicBEEF(signedTx))
272
+ }
273
+
274
+ // --------------------------------------------------------------------------
275
+ // INTERNAL HELPER METHODS
276
+ // --------------------------------------------------------------------------
277
+
278
+ private buildPushDropFields (
279
+ data: DefinitionData,
280
+ registryOperator: string
281
+ ): number[][] {
282
+ let fields: string[]
283
+
284
+ switch (data.definitionType) {
285
+ case 'basket':
286
+ fields = [
287
+ data.basketID,
288
+ data.name,
289
+ data.iconURL,
290
+ data.description,
291
+ data.documentationURL
292
+ ]
293
+ break
294
+ case 'protocol':
295
+ fields = [
296
+ data.securityLevel.toString(),
297
+ data.protocolID,
298
+ data.name,
299
+ data.iconURL,
300
+ data.description,
301
+ data.documentationURL
302
+ ]
303
+ break
304
+ case 'certificate':
305
+ fields = [
306
+ data.type,
307
+ data.name,
308
+ data.iconURL,
309
+ data.description,
310
+ data.documentationURL,
311
+ JSON.stringify(data.fields)
312
+ ]
313
+ break
314
+ default:
315
+ throw new Error('Invalid registry kind specified')
316
+ }
317
+
318
+ // Append the registry operator to all cases.
319
+ fields.push(registryOperator)
320
+
321
+ return fields.map(field => Utils.toArray(field))
322
+ }
323
+
324
+ /**
325
+ * Decodes a pushdrop locking script for a given registry kind,
326
+ * returning a typed record with the appropriate fields.
327
+ */
328
+ private async parseLockingScript (
329
+ definitionType: DefinitionType,
330
+ lockingScript: LockingScript
331
+ ): Promise<DefinitionData> {
332
+ const decoded = PushDrop.decode(lockingScript)
333
+ if (decoded.fields.length === 0) {
334
+ throw new Error('Not a valid registry pushdrop script.')
335
+ }
336
+
337
+ let registryOperator: PubKeyHex
338
+ let data: DefinitionData
339
+ switch (definitionType) {
340
+ case 'basket': {
341
+ if (decoded.fields.length !== 6) {
342
+ throw new Error('Unexpected field count for basket type.')
343
+ }
344
+ const [basketID, name, iconURL, description, docURL, operator] = decoded.fields
345
+ registryOperator = Utils.toUTF8(operator)
346
+ data = {
347
+ definitionType: 'basket',
348
+ basketID: Utils.toUTF8(basketID),
349
+ name: Utils.toUTF8(name),
350
+ iconURL: Utils.toUTF8(iconURL),
351
+ description: Utils.toUTF8(description),
352
+ documentationURL: Utils.toUTF8(docURL)
353
+ }
354
+ break
355
+ }
356
+ case 'protocol': {
357
+ if (decoded.fields.length !== 7) {
358
+ throw new Error('Unexpected field count for proto type.')
359
+ }
360
+ const [
361
+ securityLevel,
362
+ protocolID,
363
+ name,
364
+ iconURL,
365
+ description,
366
+ docURL,
367
+ operator
368
+ ] = decoded.fields
369
+ registryOperator = Utils.toUTF8(operator)
370
+ data = {
371
+ definitionType: 'protocol',
372
+ securityLevel: parseInt(Utils.toUTF8(securityLevel), 10) as 0 | 1 | 2,
373
+ protocolID: Utils.toUTF8(protocolID),
374
+ name: Utils.toUTF8(name),
375
+ iconURL: Utils.toUTF8(iconURL),
376
+ description: Utils.toUTF8(description),
377
+ documentationURL: Utils.toUTF8(docURL)
378
+ }
379
+ break
380
+ }
381
+ case 'certificate': {
382
+ if (decoded.fields.length !== 7) {
383
+ throw new Error('Unexpected field count for certificate type.')
384
+ }
385
+ const [
386
+ certType,
387
+ name,
388
+ iconURL,
389
+ description,
390
+ docURL,
391
+ fieldsJSON,
392
+ operator
393
+ ] = decoded.fields
394
+
395
+ registryOperator = Utils.toUTF8(operator)
396
+
397
+ let parsedFields: Record<string, CertificateFieldDescriptor>
398
+ try {
399
+ parsedFields = JSON.parse(Utils.toUTF8(fieldsJSON))
400
+ } catch {
401
+ parsedFields = {}
402
+ }
403
+
404
+ data = {
405
+ definitionType: 'certificate',
406
+ type: Utils.toUTF8(certType),
407
+ name: Utils.toUTF8(name),
408
+ iconURL: Utils.toUTF8(iconURL),
409
+ description: Utils.toUTF8(description),
410
+ documentationURL: Utils.toUTF8(docURL),
411
+ fields: parsedFields
412
+ }
413
+ break
414
+ }
415
+ default:
416
+ throw new Error('Invalid registry kind for parsing.')
417
+ }
418
+
419
+ const currentIdentityKey = (await this.wallet.getPublicKey({ identityKey: true })).publicKey
420
+ if (registryOperator !== currentIdentityKey) {
421
+ throw new Error('This registry token does not belong to the current wallet.')
422
+ }
423
+
424
+ return {
425
+ ...data,
426
+ registryOperator
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Returns the (protocolID, keyID) used for pushdrop based on the registry kind.
432
+ */
433
+ private getWalletProtocol (definitionType: DefinitionType): WalletProtocol {
434
+ switch (definitionType) {
435
+ case 'basket':
436
+ return [1, 'basketmap']
437
+ case 'protocol':
438
+ return [1, 'protomap']
439
+ case 'certificate':
440
+ return [1, 'certmap']
441
+ default:
442
+ throw new Error(`Unknown registry type: ${definitionType as string}`)
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Returns the name of the basket used by the wallet
448
+ */
449
+ private getBasketName (definitionType: DefinitionType): string {
450
+ switch (definitionType) {
451
+ case 'basket':
452
+ return 'basketmap'
453
+ case 'protocol':
454
+ return 'protomap'
455
+ case 'certificate':
456
+ return 'certmap'
457
+ default:
458
+ throw new Error(`Unknown basket type: ${definitionType as string}`)
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Returns the broadcast topic to be used with SHIPBroadcaster.
464
+ */
465
+ private getBroadcastTopic (definitionType: DefinitionType): string {
466
+ switch (definitionType) {
467
+ case 'basket':
468
+ return 'tm_basketmap'
469
+ case 'protocol':
470
+ return 'tm_protomap'
471
+ case 'certificate':
472
+ return 'tm_certmap'
473
+ default:
474
+ throw new Error(`Unknown topic type: ${definitionType as string}`)
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Returns the lookup service name to use.
480
+ */
481
+ private getServiceName (definitionType: DefinitionType): string {
482
+ switch (definitionType) {
483
+ case 'basket':
484
+ return 'ls_basketmap'
485
+ case 'protocol':
486
+ return 'ls_protomap'
487
+ case 'certificate':
488
+ return 'ls_certmap'
489
+ default:
490
+ throw new Error(`Unknown service type: ${definitionType as string}`)
491
+ }
492
+ }
493
+ }