@did-btcr2/method 0.13.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 (310) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +7 -0
  3. package/dist/browser.js +2364 -0
  4. package/dist/browser.js.map +7 -0
  5. package/dist/browser.mjs +2364 -0
  6. package/dist/browser.mjs.map +7 -0
  7. package/dist/cjs/bitcoin/constants.js +20 -0
  8. package/dist/cjs/bitcoin/constants.js.map +1 -0
  9. package/dist/cjs/bitcoin/errors.js +11 -0
  10. package/dist/cjs/bitcoin/errors.js.map +1 -0
  11. package/dist/cjs/bitcoin/index.js +95 -0
  12. package/dist/cjs/bitcoin/index.js.map +1 -0
  13. package/dist/cjs/bitcoin/interface.js +2 -0
  14. package/dist/cjs/bitcoin/interface.js.map +1 -0
  15. package/dist/cjs/bitcoin/network.js +17 -0
  16. package/dist/cjs/bitcoin/network.js.map +1 -0
  17. package/dist/cjs/bitcoin/rest-client.js +289 -0
  18. package/dist/cjs/bitcoin/rest-client.js.map +1 -0
  19. package/dist/cjs/bitcoin/rpc-client.js +722 -0
  20. package/dist/cjs/bitcoin/rpc-client.js.map +1 -0
  21. package/dist/cjs/bitcoin/taproot.js +219 -0
  22. package/dist/cjs/bitcoin/taproot.js.map +1 -0
  23. package/dist/cjs/btcr2/beacon/aggregation/coordinator.js +120 -0
  24. package/dist/cjs/btcr2/beacon/aggregation/coordinator.js.map +1 -0
  25. package/dist/cjs/btcr2/beacon/aggregation/messages/advert.js +24 -0
  26. package/dist/cjs/btcr2/beacon/aggregation/messages/advert.js.map +1 -0
  27. package/dist/cjs/btcr2/beacon/aggregation/messages/base.js +37 -0
  28. package/dist/cjs/btcr2/beacon/aggregation/messages/base.js.map +1 -0
  29. package/dist/cjs/btcr2/beacon/aggregation/messages/cohort-set.js +25 -0
  30. package/dist/cjs/btcr2/beacon/aggregation/messages/cohort-set.js.map +1 -0
  31. package/dist/cjs/btcr2/beacon/aggregation/messages/keygen.js +8 -0
  32. package/dist/cjs/btcr2/beacon/aggregation/messages/keygen.js.map +1 -0
  33. package/dist/cjs/btcr2/beacon/aggregation/messages/opt-in.js +23 -0
  34. package/dist/cjs/btcr2/beacon/aggregation/messages/opt-in.js.map +1 -0
  35. package/dist/cjs/btcr2/beacon/aggregation/messages/sign.js +7 -0
  36. package/dist/cjs/btcr2/beacon/aggregation/messages/sign.js.map +1 -0
  37. package/dist/cjs/btcr2/beacon/aggregation/models/cohort/index.js +92 -0
  38. package/dist/cjs/btcr2/beacon/aggregation/models/cohort/index.js.map +1 -0
  39. package/dist/cjs/btcr2/beacon/aggregation/models/cohort/status.js +8 -0
  40. package/dist/cjs/btcr2/beacon/aggregation/models/cohort/status.js.map +1 -0
  41. package/dist/cjs/btcr2/beacon/aggregation/participant.js +2 -0
  42. package/dist/cjs/btcr2/beacon/aggregation/participant.js.map +1 -0
  43. package/dist/cjs/btcr2/beacon/aggregation/protocol/nostr.js +57 -0
  44. package/dist/cjs/btcr2/beacon/aggregation/protocol/nostr.js.map +1 -0
  45. package/dist/cjs/btcr2/beacon/aggregation/protocol/service.js +2 -0
  46. package/dist/cjs/btcr2/beacon/aggregation/protocol/service.js.map +1 -0
  47. package/dist/cjs/btcr2/beacon/cid-aggregate.js +116 -0
  48. package/dist/cjs/btcr2/beacon/cid-aggregate.js.map +1 -0
  49. package/dist/cjs/btcr2/beacon/factory.js +30 -0
  50. package/dist/cjs/btcr2/beacon/factory.js.map +1 -0
  51. package/dist/cjs/btcr2/beacon/singleton.js +220 -0
  52. package/dist/cjs/btcr2/beacon/singleton.js.map +1 -0
  53. package/dist/cjs/btcr2/beacon/smt-aggregate.js +126 -0
  54. package/dist/cjs/btcr2/beacon/smt-aggregate.js.map +1 -0
  55. package/dist/cjs/btcr2/crud/create.js +102 -0
  56. package/dist/cjs/btcr2/crud/create.js.map +1 -0
  57. package/dist/cjs/btcr2/crud/deactivate.js +14 -0
  58. package/dist/cjs/btcr2/crud/deactivate.js.map +1 -0
  59. package/dist/cjs/btcr2/crud/read.js +686 -0
  60. package/dist/cjs/btcr2/crud/read.js.map +1 -0
  61. package/dist/cjs/btcr2/crud/update.js +195 -0
  62. package/dist/cjs/btcr2/crud/update.js.map +1 -0
  63. package/dist/cjs/btcr2/key-manager/index.js +290 -0
  64. package/dist/cjs/btcr2/key-manager/index.js.map +1 -0
  65. package/dist/cjs/btcr2/key-manager/interface.js +2 -0
  66. package/dist/cjs/btcr2/key-manager/interface.js.map +1 -0
  67. package/dist/cjs/did-btcr2.js +222 -0
  68. package/dist/cjs/did-btcr2.js.map +1 -0
  69. package/dist/cjs/index.js +27 -0
  70. package/dist/cjs/index.js.map +1 -0
  71. package/dist/cjs/interfaces/beacon.js +41 -0
  72. package/dist/cjs/interfaces/beacon.js.map +1 -0
  73. package/dist/cjs/interfaces/crud.js +2 -0
  74. package/dist/cjs/interfaces/crud.js.map +1 -0
  75. package/dist/cjs/interfaces/ibeacon.js +2 -0
  76. package/dist/cjs/interfaces/ibeacon.js.map +1 -0
  77. package/dist/cjs/package.json +1 -0
  78. package/dist/cjs/types/bitcoin.js +62 -0
  79. package/dist/cjs/types/bitcoin.js.map +1 -0
  80. package/dist/cjs/types/crud.js +2 -0
  81. package/dist/cjs/types/crud.js.map +1 -0
  82. package/dist/cjs/utils/appendix.js +221 -0
  83. package/dist/cjs/utils/appendix.js.map +1 -0
  84. package/dist/cjs/utils/beacons.js +206 -0
  85. package/dist/cjs/utils/beacons.js.map +1 -0
  86. package/dist/cjs/utils/did-document-builder.js +61 -0
  87. package/dist/cjs/utils/did-document-builder.js.map +1 -0
  88. package/dist/cjs/utils/did-document.js +380 -0
  89. package/dist/cjs/utils/did-document.js.map +1 -0
  90. package/dist/cjs/utils/general.js +195 -0
  91. package/dist/cjs/utils/general.js.map +1 -0
  92. package/dist/cjs/utils/identifier.js +238 -0
  93. package/dist/cjs/utils/identifier.js.map +1 -0
  94. package/dist/esm/bitcoin/constants.js +20 -0
  95. package/dist/esm/bitcoin/constants.js.map +1 -0
  96. package/dist/esm/bitcoin/errors.js +11 -0
  97. package/dist/esm/bitcoin/errors.js.map +1 -0
  98. package/dist/esm/bitcoin/index.js +95 -0
  99. package/dist/esm/bitcoin/index.js.map +1 -0
  100. package/dist/esm/bitcoin/interface.js +2 -0
  101. package/dist/esm/bitcoin/interface.js.map +1 -0
  102. package/dist/esm/bitcoin/network.js +17 -0
  103. package/dist/esm/bitcoin/network.js.map +1 -0
  104. package/dist/esm/bitcoin/rest-client.js +289 -0
  105. package/dist/esm/bitcoin/rest-client.js.map +1 -0
  106. package/dist/esm/bitcoin/rpc-client.js +722 -0
  107. package/dist/esm/bitcoin/rpc-client.js.map +1 -0
  108. package/dist/esm/bitcoin/taproot.js +219 -0
  109. package/dist/esm/bitcoin/taproot.js.map +1 -0
  110. package/dist/esm/btcr2/beacon/aggregation/coordinator.js +120 -0
  111. package/dist/esm/btcr2/beacon/aggregation/coordinator.js.map +1 -0
  112. package/dist/esm/btcr2/beacon/aggregation/messages/advert.js +24 -0
  113. package/dist/esm/btcr2/beacon/aggregation/messages/advert.js.map +1 -0
  114. package/dist/esm/btcr2/beacon/aggregation/messages/base.js +37 -0
  115. package/dist/esm/btcr2/beacon/aggregation/messages/base.js.map +1 -0
  116. package/dist/esm/btcr2/beacon/aggregation/messages/cohort-set.js +25 -0
  117. package/dist/esm/btcr2/beacon/aggregation/messages/cohort-set.js.map +1 -0
  118. package/dist/esm/btcr2/beacon/aggregation/messages/keygen.js +8 -0
  119. package/dist/esm/btcr2/beacon/aggregation/messages/keygen.js.map +1 -0
  120. package/dist/esm/btcr2/beacon/aggregation/messages/opt-in.js +23 -0
  121. package/dist/esm/btcr2/beacon/aggregation/messages/opt-in.js.map +1 -0
  122. package/dist/esm/btcr2/beacon/aggregation/messages/sign.js +7 -0
  123. package/dist/esm/btcr2/beacon/aggregation/messages/sign.js.map +1 -0
  124. package/dist/esm/btcr2/beacon/aggregation/models/cohort/index.js +92 -0
  125. package/dist/esm/btcr2/beacon/aggregation/models/cohort/index.js.map +1 -0
  126. package/dist/esm/btcr2/beacon/aggregation/models/cohort/status.js +8 -0
  127. package/dist/esm/btcr2/beacon/aggregation/models/cohort/status.js.map +1 -0
  128. package/dist/esm/btcr2/beacon/aggregation/participant.js +2 -0
  129. package/dist/esm/btcr2/beacon/aggregation/participant.js.map +1 -0
  130. package/dist/esm/btcr2/beacon/aggregation/protocol/nostr.js +57 -0
  131. package/dist/esm/btcr2/beacon/aggregation/protocol/nostr.js.map +1 -0
  132. package/dist/esm/btcr2/beacon/aggregation/protocol/service.js +2 -0
  133. package/dist/esm/btcr2/beacon/aggregation/protocol/service.js.map +1 -0
  134. package/dist/esm/btcr2/beacon/cid-aggregate.js +116 -0
  135. package/dist/esm/btcr2/beacon/cid-aggregate.js.map +1 -0
  136. package/dist/esm/btcr2/beacon/factory.js +30 -0
  137. package/dist/esm/btcr2/beacon/factory.js.map +1 -0
  138. package/dist/esm/btcr2/beacon/singleton.js +220 -0
  139. package/dist/esm/btcr2/beacon/singleton.js.map +1 -0
  140. package/dist/esm/btcr2/beacon/smt-aggregate.js +126 -0
  141. package/dist/esm/btcr2/beacon/smt-aggregate.js.map +1 -0
  142. package/dist/esm/btcr2/crud/create.js +102 -0
  143. package/dist/esm/btcr2/crud/create.js.map +1 -0
  144. package/dist/esm/btcr2/crud/deactivate.js +14 -0
  145. package/dist/esm/btcr2/crud/deactivate.js.map +1 -0
  146. package/dist/esm/btcr2/crud/read.js +686 -0
  147. package/dist/esm/btcr2/crud/read.js.map +1 -0
  148. package/dist/esm/btcr2/crud/update.js +195 -0
  149. package/dist/esm/btcr2/crud/update.js.map +1 -0
  150. package/dist/esm/btcr2/key-manager/index.js +290 -0
  151. package/dist/esm/btcr2/key-manager/index.js.map +1 -0
  152. package/dist/esm/btcr2/key-manager/interface.js +2 -0
  153. package/dist/esm/btcr2/key-manager/interface.js.map +1 -0
  154. package/dist/esm/did-btcr2.js +222 -0
  155. package/dist/esm/did-btcr2.js.map +1 -0
  156. package/dist/esm/index.js +27 -0
  157. package/dist/esm/index.js.map +1 -0
  158. package/dist/esm/interfaces/beacon.js +41 -0
  159. package/dist/esm/interfaces/beacon.js.map +1 -0
  160. package/dist/esm/interfaces/crud.js +2 -0
  161. package/dist/esm/interfaces/crud.js.map +1 -0
  162. package/dist/esm/interfaces/ibeacon.js +2 -0
  163. package/dist/esm/interfaces/ibeacon.js.map +1 -0
  164. package/dist/esm/types/bitcoin.js +62 -0
  165. package/dist/esm/types/bitcoin.js.map +1 -0
  166. package/dist/esm/types/crud.js +2 -0
  167. package/dist/esm/types/crud.js.map +1 -0
  168. package/dist/esm/utils/appendix.js +221 -0
  169. package/dist/esm/utils/appendix.js.map +1 -0
  170. package/dist/esm/utils/beacons.js +206 -0
  171. package/dist/esm/utils/beacons.js.map +1 -0
  172. package/dist/esm/utils/did-document-builder.js +61 -0
  173. package/dist/esm/utils/did-document-builder.js.map +1 -0
  174. package/dist/esm/utils/did-document.js +380 -0
  175. package/dist/esm/utils/did-document.js.map +1 -0
  176. package/dist/esm/utils/general.js +195 -0
  177. package/dist/esm/utils/general.js.map +1 -0
  178. package/dist/esm/utils/identifier.js +238 -0
  179. package/dist/esm/utils/identifier.js.map +1 -0
  180. package/dist/types/bitcoin/constants.d.ts +19 -0
  181. package/dist/types/bitcoin/constants.d.ts.map +1 -0
  182. package/dist/types/bitcoin/errors.d.ts +5 -0
  183. package/dist/types/bitcoin/errors.d.ts.map +1 -0
  184. package/dist/types/bitcoin/index.d.ts +75 -0
  185. package/dist/types/bitcoin/index.d.ts.map +1 -0
  186. package/dist/types/bitcoin/interface.d.ts +86 -0
  187. package/dist/types/bitcoin/interface.d.ts.map +1 -0
  188. package/dist/types/bitcoin/network.d.ts +2 -0
  189. package/dist/types/bitcoin/network.d.ts.map +1 -0
  190. package/dist/types/bitcoin/rest-client.d.ts +268 -0
  191. package/dist/types/bitcoin/rest-client.d.ts.map +1 -0
  192. package/dist/types/bitcoin/rpc-client.d.ts +506 -0
  193. package/dist/types/bitcoin/rpc-client.d.ts.map +1 -0
  194. package/dist/types/bitcoin/taproot.d.ts +34 -0
  195. package/dist/types/bitcoin/taproot.d.ts.map +1 -0
  196. package/dist/types/btcr2/beacon/aggregation/coordinator.d.ts +74 -0
  197. package/dist/types/btcr2/beacon/aggregation/coordinator.d.ts.map +1 -0
  198. package/dist/types/btcr2/beacon/aggregation/messages/advert.d.ts +22 -0
  199. package/dist/types/btcr2/beacon/aggregation/messages/advert.d.ts.map +1 -0
  200. package/dist/types/btcr2/beacon/aggregation/messages/base.d.ts +36 -0
  201. package/dist/types/btcr2/beacon/aggregation/messages/base.d.ts.map +1 -0
  202. package/dist/types/btcr2/beacon/aggregation/messages/cohort-set.d.ts +23 -0
  203. package/dist/types/btcr2/beacon/aggregation/messages/cohort-set.d.ts.map +1 -0
  204. package/dist/types/btcr2/beacon/aggregation/messages/keygen.d.ts +6 -0
  205. package/dist/types/btcr2/beacon/aggregation/messages/keygen.d.ts.map +1 -0
  206. package/dist/types/btcr2/beacon/aggregation/messages/opt-in.d.ts +22 -0
  207. package/dist/types/btcr2/beacon/aggregation/messages/opt-in.d.ts.map +1 -0
  208. package/dist/types/btcr2/beacon/aggregation/messages/sign.d.ts +5 -0
  209. package/dist/types/btcr2/beacon/aggregation/messages/sign.d.ts.map +1 -0
  210. package/dist/types/btcr2/beacon/aggregation/models/cohort/index.d.ts +77 -0
  211. package/dist/types/btcr2/beacon/aggregation/models/cohort/index.d.ts.map +1 -0
  212. package/dist/types/btcr2/beacon/aggregation/models/cohort/status.d.ts +7 -0
  213. package/dist/types/btcr2/beacon/aggregation/models/cohort/status.d.ts.map +1 -0
  214. package/dist/types/btcr2/beacon/aggregation/participant.d.ts +1 -0
  215. package/dist/types/btcr2/beacon/aggregation/participant.d.ts.map +1 -0
  216. package/dist/types/btcr2/beacon/aggregation/protocol/nostr.d.ts +36 -0
  217. package/dist/types/btcr2/beacon/aggregation/protocol/nostr.d.ts.map +1 -0
  218. package/dist/types/btcr2/beacon/aggregation/protocol/service.d.ts +6 -0
  219. package/dist/types/btcr2/beacon/aggregation/protocol/service.d.ts.map +1 -0
  220. package/dist/types/btcr2/beacon/cid-aggregate.d.ts +103 -0
  221. package/dist/types/btcr2/beacon/cid-aggregate.d.ts.map +1 -0
  222. package/dist/types/btcr2/beacon/factory.d.ts +17 -0
  223. package/dist/types/btcr2/beacon/factory.d.ts.map +1 -0
  224. package/dist/types/btcr2/beacon/singleton.d.ts +93 -0
  225. package/dist/types/btcr2/beacon/singleton.d.ts.map +1 -0
  226. package/dist/types/btcr2/beacon/smt-aggregate.d.ts +112 -0
  227. package/dist/types/btcr2/beacon/smt-aggregate.d.ts.map +1 -0
  228. package/dist/types/btcr2/crud/create.d.ts +92 -0
  229. package/dist/types/btcr2/crud/create.d.ts.map +1 -0
  230. package/dist/types/btcr2/crud/deactivate.d.ts +13 -0
  231. package/dist/types/btcr2/crud/deactivate.d.ts.map +1 -0
  232. package/dist/types/btcr2/crud/read.d.ts +341 -0
  233. package/dist/types/btcr2/crud/read.d.ts.map +1 -0
  234. package/dist/types/btcr2/crud/update.d.ts +83 -0
  235. package/dist/types/btcr2/crud/update.d.ts.map +1 -0
  236. package/dist/types/btcr2/key-manager/index.d.ts +145 -0
  237. package/dist/types/btcr2/key-manager/index.d.ts.map +1 -0
  238. package/dist/types/btcr2/key-manager/interface.d.ts +113 -0
  239. package/dist/types/btcr2/key-manager/interface.d.ts.map +1 -0
  240. package/dist/types/did-btcr2.d.ts +117 -0
  241. package/dist/types/did-btcr2.d.ts.map +1 -0
  242. package/dist/types/index.d.ts +26 -0
  243. package/dist/types/index.d.ts.map +1 -0
  244. package/dist/types/interfaces/beacon.d.ts +57 -0
  245. package/dist/types/interfaces/beacon.d.ts.map +1 -0
  246. package/dist/types/interfaces/crud.d.ts +35 -0
  247. package/dist/types/interfaces/crud.d.ts.map +1 -0
  248. package/dist/types/interfaces/ibeacon.d.ts +66 -0
  249. package/dist/types/interfaces/ibeacon.d.ts.map +1 -0
  250. package/dist/types/types/bitcoin.d.ts +827 -0
  251. package/dist/types/types/bitcoin.d.ts.map +1 -0
  252. package/dist/types/types/crud.d.ts +38 -0
  253. package/dist/types/types/crud.d.ts.map +1 -0
  254. package/dist/types/utils/appendix.d.ts +118 -0
  255. package/dist/types/utils/appendix.d.ts.map +1 -0
  256. package/dist/types/utils/beacons.d.ts +156 -0
  257. package/dist/types/utils/beacons.d.ts.map +1 -0
  258. package/dist/types/utils/did-document-builder.d.ts +13 -0
  259. package/dist/types/utils/did-document-builder.d.ts.map +1 -0
  260. package/dist/types/utils/did-document.d.ts +211 -0
  261. package/dist/types/utils/did-document.d.ts.map +1 -0
  262. package/dist/types/utils/general.d.ts +85 -0
  263. package/dist/types/utils/general.d.ts.map +1 -0
  264. package/dist/types/utils/identifier.d.ts +59 -0
  265. package/dist/types/utils/identifier.d.ts.map +1 -0
  266. package/package.json +137 -0
  267. package/src/bitcoin/constants.ts +19 -0
  268. package/src/bitcoin/errors.ts +10 -0
  269. package/src/bitcoin/index.ts +154 -0
  270. package/src/bitcoin/interface.ts +160 -0
  271. package/src/bitcoin/network.ts +17 -0
  272. package/src/bitcoin/rest-client.ts +415 -0
  273. package/src/bitcoin/rpc-client.ts +888 -0
  274. package/src/bitcoin/taproot.ts +237 -0
  275. package/src/btcr2/beacon/aggregation/coordinator.ts +135 -0
  276. package/src/btcr2/beacon/aggregation/messages/advert.ts +36 -0
  277. package/src/btcr2/beacon/aggregation/messages/base.ts +59 -0
  278. package/src/btcr2/beacon/aggregation/messages/cohort-set.ts +37 -0
  279. package/src/btcr2/beacon/aggregation/messages/keygen.ts +8 -0
  280. package/src/btcr2/beacon/aggregation/messages/opt-in.ts +35 -0
  281. package/src/btcr2/beacon/aggregation/messages/sign.ts +7 -0
  282. package/src/btcr2/beacon/aggregation/models/cohort/index.ts +112 -0
  283. package/src/btcr2/beacon/aggregation/models/cohort/status.ts +7 -0
  284. package/src/btcr2/beacon/aggregation/participant.ts +0 -0
  285. package/src/btcr2/beacon/aggregation/protocol/nostr.ts +81 -0
  286. package/src/btcr2/beacon/aggregation/protocol/service.ts +6 -0
  287. package/src/btcr2/beacon/cid-aggregate.ts +154 -0
  288. package/src/btcr2/beacon/factory.ts +36 -0
  289. package/src/btcr2/beacon/singleton.ts +257 -0
  290. package/src/btcr2/beacon/smt-aggregate.ts +136 -0
  291. package/src/btcr2/crud/create.ts +160 -0
  292. package/src/btcr2/crud/deactivate.ts +13 -0
  293. package/src/btcr2/crud/read.ts +946 -0
  294. package/src/btcr2/crud/update.ts +277 -0
  295. package/src/btcr2/key-manager/index.ts +364 -0
  296. package/src/btcr2/key-manager/interface.ts +129 -0
  297. package/src/canonicalize.d.ts +6 -0
  298. package/src/did-btcr2.ts +288 -0
  299. package/src/index.ts +34 -0
  300. package/src/interfaces/beacon.ts +68 -0
  301. package/src/interfaces/crud.ts +36 -0
  302. package/src/interfaces/ibeacon.ts +76 -0
  303. package/src/types/bitcoin.ts +1028 -0
  304. package/src/types/crud.ts +41 -0
  305. package/src/utils/appendix.ts +257 -0
  306. package/src/utils/beacons.ts +276 -0
  307. package/src/utils/did-document-builder.ts +73 -0
  308. package/src/utils/did-document.ts +474 -0
  309. package/src/utils/general.ts +204 -0
  310. package/src/utils/identifier.ts +276 -0
@@ -0,0 +1,946 @@
1
+ import {
2
+ BitcoinNetworkNames,
3
+ Btcr2Error,
4
+ Btcr2IdentifierHrp,
5
+ Btcr2ReadError,
6
+ DidUpdatePayload,
7
+ ID_PLACEHOLDER_VALUE,
8
+ INVALID_DID,
9
+ INVALID_DID_DOCUMENT,
10
+ INVALID_DID_UPDATE,
11
+ LATE_PUBLISHING_ERROR,
12
+ Logger,
13
+ UnixTimestamp
14
+ } from '@did-btcr2/common';
15
+ import { Cryptosuite, DataIntegrityProof, SchnorrMultikey } from '@did-btcr2/cryptosuite';
16
+ import { PublicKey } from '@did-btcr2/keypair';
17
+ import { bytesToHex } from '@noble/hashes/utils';
18
+ import { GENESIS_TX_ID, TXIN_WITNESS_COINBASE } from '../../bitcoin/constants.js';
19
+ import bitcoinNetwork, { Bitcoin } from '../../bitcoin/index.js';
20
+ import { getNetwork } from '../../bitcoin/network.js';
21
+ import BitcoinRest, { RawTransactionRest } from '../../bitcoin/rest-client.js';
22
+ import BitcoinRpc from '../../bitcoin/rpc-client.js';
23
+ import { DidBtc1 } from '../../did-btcr2.js';
24
+ import { DidResolutionOptions } from '../../interfaces/crud.js';
25
+ import { BeaconService, BeaconServiceAddress, BeaconSignal } from '../../interfaces/ibeacon.js';
26
+ import { BlockV3, RawTransactionV2 } from '../../types/bitcoin.js';
27
+ import {
28
+ CIDAggregateSidecar,
29
+ SidecarData,
30
+ SignalsMetadata
31
+ } from '../../types/crud.js';
32
+ import { Btc1Appendix, DidComponents } from '../../utils/appendix.js';
33
+ import { BeaconUtils } from '../../utils/beacons.js';
34
+ import { Btc1DidDocument } from '../../utils/did-document.js';
35
+ import { BeaconFactory } from '../beacon/factory.js';
36
+
37
+ export type FindNextSignalsRestParams = {
38
+ connection: BitcoinRest;
39
+ beaconSignals: Array<BeaconSignal>;
40
+ block: BlockV3;
41
+ beacons: Array<BeaconServiceAddress>;
42
+ }
43
+ export type BeaconSignals = Array<BeaconSignal>;
44
+ export type BitcoinClient = BitcoinRpc | BitcoinRest;
45
+
46
+ export type NetworkVersion = {
47
+ version?: string;
48
+ network?: string;
49
+ };
50
+ export type ResolveInitialDocument = {
51
+ identifier: string;
52
+ components: DidComponents;
53
+ resolutionsOptions: DidResolutionOptions;
54
+ };
55
+
56
+ // Deterministic
57
+ export interface Btc1ReadDeterministic {
58
+ components: DidComponents;
59
+ identifier: string;
60
+ };
61
+
62
+ // External
63
+ export interface Btc1ReadExternal {
64
+ components: DidComponents;
65
+ identifier: string;
66
+ resolutionsOptions: DidResolutionOptions;
67
+ }
68
+ export interface Btc1ReadSidecar {
69
+ identifierComponents: DidComponents;
70
+ initialDocument: Btc1DidDocument;
71
+ };
72
+ export interface DidReadCas {
73
+ identifier: string;
74
+ identifierComponents: DidComponents;
75
+ }
76
+
77
+ // Methods
78
+ export interface ApplyDidUpdateParams {
79
+ contemporaryDidDocument: Btc1DidDocument;
80
+ update: DidUpdatePayload;
81
+ }
82
+
83
+ export interface TargetDocumentParams {
84
+ initialDocument: Btc1DidDocument;
85
+ resolutionsOptions: DidResolutionOptions;
86
+ };
87
+
88
+ export interface TargetBlockheightParams {
89
+ network: BitcoinNetworkNames;
90
+ targetTime?: UnixTimestamp;
91
+ }
92
+
93
+ /**
94
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#read | 4.2 Read}.
95
+ * The read operation is executed by a resolver after a resolution request identifying a specific did:btcr2 identifier is
96
+ * received from a client at Resolution Time. The request MAY contain a resolutionOptions object containing additional
97
+ * information to be used in resolution. The resolver then attempts to resolve the DID document of the identifier at a
98
+ * specific Target Time. The Target Time is either provided in resolutionOptions or is set to the Resolution Time of the
99
+ * request.
100
+ * To do so it executes the following algorithm:
101
+ * 1. Let identifierComponents be the result of running the algorithm
102
+ * in Parse did:btcr2 identifier, passing in the identifier.
103
+ * 2. Set initialDocument to the result of running Resolve Initial Document
104
+ * passing identifier, identifierComponents and resolutionOptions.
105
+ * 3. Set targetDocument to the result of running the algorithm in Resolve
106
+ * Target Document passing in initialDocument and resolutionOptions.
107
+ * 4. Return targetDocument.
108
+ *
109
+ * @class Btc1Read
110
+ * @type {Btc1Read}
111
+ */
112
+ export class Btc1Read {
113
+ /**
114
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#deterministically-generate-initial-did-document | 4.2.2.1 Deterministically Generate Initial DID Document}.
115
+ *
116
+ * The Deterministically Generate Initial DID Document algorithm deterministically generates an initial DID
117
+ * Document from a secp256k1 public key. It takes in a did:btcr2 identifier and a identifierComponents object and
118
+ * returns an initialDocument.
119
+ *
120
+ * @param {Btc1ReadDeterministic} params See {@link Btc1ReadDeterministic} for details.
121
+ * @param {string} params.identifier The did-btcr2 version.
122
+ * @param {DidComponents} params.identifierComponents The decoded components of the identifier.
123
+ * @returns {Btc1DidDocument} The resolved DID Document object.
124
+ */
125
+ public static deterministic({ identifier, identifierComponents }: {
126
+ identifier: string;
127
+ identifierComponents: DidComponents;
128
+ }): Btc1DidDocument {
129
+ // Deconstruct the components
130
+ const { network, genesisBytes } = identifierComponents;
131
+
132
+ // Construct a new PublicKey and deconstruct the publicKey and publicKeyMultibase
133
+ const { compressed: publicKey, multibase: publicKeyMultibase } = new PublicKey(genesisBytes);
134
+
135
+ // Generate the service field for the DID Document
136
+ const service = BeaconUtils.generateBeaconServices({
137
+ identifier,
138
+ publicKey,
139
+ network : getNetwork(network),
140
+ type : 'SingletonBeacon',
141
+ });
142
+
143
+ return new Btc1DidDocument({
144
+ id : identifier,
145
+ controller : [identifier],
146
+ verificationMethod : [{
147
+ id : `${identifier}#initialKey`,
148
+ type : 'Multikey',
149
+ controller : identifier,
150
+ publicKeyMultibase : publicKeyMultibase.address
151
+ }],
152
+ service
153
+ });
154
+ }
155
+
156
+ /**
157
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#external-resolution | 4.2.2.2 External Resolution}.
158
+ *
159
+ * The External Resolution algorithm externally retrieves an intermediateDocumentRepresentation, either by retrieving
160
+ * it from {@link https://dcdpr.github.io/did-btcr2/#def-content-addressable-storage | Content Addressable Storage (CAS)}
161
+ * or from the {@link https://dcdpr.github.io/did-btcr2/#def-sidecar-data | Sidecar Data} provided as part of the
162
+ * resolution request. It takes in a did:btcr2 identifier, a identifierComponents object and a resolutionOptions object.
163
+ * It returns an initialDocument, which is a conformant DID document validated against the identifier.
164
+ *
165
+ * @param {Btc1ReadExternal} params Required params for calling the external method.
166
+ * @param {string} params.identifier The DID to be resolved.
167
+ * @param {DidComponents} params.identifierComponents The decoded components of the identifier.
168
+ * @param {DidResolutionOptions} params.resolutionsOptions The options for resolving the DID Document.
169
+ * @param {Btc1DidDocument} params.resolutionsOptions.sidecarData The sidecar data for resolving the DID Document.
170
+ * @param {Btc1DidDocument} params.resolutionsOptions.sidecarData.initialDocument The offline user-provided DID Document
171
+ * @returns {Btc1DidDocument} The resolved DID Document object
172
+ */
173
+ public static async external({ identifier, identifierComponents, resolutionsOptions }: {
174
+ identifier: string;
175
+ identifierComponents: DidComponents;
176
+ resolutionsOptions: DidResolutionOptions;
177
+ }): Promise<Btc1DidDocument> {
178
+ // Deconstruct the options
179
+ const { initialDocument: document } = resolutionsOptions.sidecarData as CIDAggregateSidecar;
180
+
181
+ // 1. If resolutionOptions.sidecarData.initialDocument is not null, set initialDocument to the result of passing
182
+ // identifier, identifierComponents and resolutionOptions.sidecarData.initialDocument into algorithm Sidecar
183
+ // Initial Document Validation.
184
+ // 2. Else set initialDocument to the result of passing identifier and identifierComponents to the CAS Retrieval algorithm.
185
+ const initialDocument = document
186
+ ? await this.sidecar({ identifierComponents, initialDocument: document })
187
+ : await this.cas({ identifier, identifierComponents });
188
+
189
+ // 3. Validate initialDocument is a conformant DID document according to the DID Core 1.1 specification. Else MUST
190
+ // raise invalidDidDocument error.
191
+ Btc1DidDocument.validate(initialDocument);
192
+
193
+ // 4. Return initialDocument.
194
+ return initialDocument;
195
+ }
196
+
197
+ /**
198
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#sidecar-initial-document-validation | 4.2.2.2.1 Sidecar Initial Document Validation}.
199
+ *
200
+ * The Sidecar Initial Document Validation algorithm validates an initialDocument against its identifier, by first
201
+ * constructing the intermediateDocumentRepresentation and verifying the hash of this document matches the bytes
202
+ * encoded within the identifier. It takes in a did:btcr2 identifier, identifierComponents and a
203
+ * initialDocument. It returns the initialDocument if validated, otherwise it throws an error.
204
+ *
205
+ * @param {Btc1ReadSidecar} params Required params for calling the sidecar method
206
+ * @param {string} params.identifier The DID to be resolved
207
+ * @param {DidComponents} params.identifierComponents The components of the DID identifier
208
+ * @param {Btc1DidDocument} params.initialDocument The initial DID Document provided by the user
209
+ * @returns {Btc1DidDocument} The resolved DID Document object
210
+ * @throws {DidError} InvalidDidDocument if genesisBytes !== initialDocument hashBytes
211
+ */
212
+ public static async sidecar({ identifierComponents, initialDocument }: Btc1ReadSidecar): Promise<Btc1DidDocument> {
213
+ // Replace the placeholder did with the identifier throughout the initialDocument.
214
+ const intermediateDocument = JSON.parse(
215
+ JSON.stringify(initialDocument).replaceAll(initialDocument.id, ID_PLACEHOLDER_VALUE)
216
+ );
217
+
218
+ // Canonicalize and sha256 hash the intermediateDocument
219
+ const hashBytes = await JSON.canonicalization.process(intermediateDocument, 'hex');
220
+
221
+ // Compare the genesisBytes to the hashBytes
222
+ const genesisBytes = bytesToHex(identifierComponents.genesisBytes);
223
+
224
+ // If the genesisBytes do not match the hashBytes, throw an error
225
+ if (genesisBytes !== hashBytes) {
226
+ throw new Btcr2Error(
227
+ `Initial document mismatch: genesisBytes ${genesisBytes} !== hashBytes ${hashBytes}`,
228
+ INVALID_DID_DOCUMENT, { genesisBytes, hashBytes }
229
+ );
230
+ }
231
+
232
+ // Return a W3C conformant DID Document
233
+ return new Btc1DidDocument(initialDocument);
234
+ }
235
+
236
+ /**
237
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#cas-retrieval | 4.2.2.2.2 CAS Retrieval}.
238
+ *
239
+ * The CAS Retrieval algorithm attempts to retrieve an initialDocument from a Content Addressable Storage (CAS) system
240
+ * by converting the bytes in the identifier into a Content Identifier (CID). It takes in an identifier and
241
+ * an identifierComponents object. It returns an initialDocument.
242
+ *
243
+ * @param {DidReadCas} params Required params for calling the cas method
244
+ * @param {string} params.identifier BTCR2 DID used to resolve the DID Document
245
+ * @param {DidComponents} params.identifierComponents BTCR2 DID components used to resolve the DID Document
246
+ * @returns {Btc1DidDocument} The resolved DID Document object
247
+ * @throws {Btcr2Error} if the DID Document content is invalid
248
+ */
249
+ public static async cas({ identifier, identifierComponents }: DidReadCas): Promise<Btc1DidDocument> {
250
+ // 1. Set hashBytes to identifierComponents.genesisBytes.
251
+ const hashBytes = identifierComponents.genesisBytes;
252
+
253
+ // 3. Set intermediateDocumentRepresentation to the result of fetching the cid against a Content Addressable Storage
254
+ // (CAS) system such as IPFS.
255
+ const intermediateDocument = await Btc1Appendix.fetchFromCas(hashBytes);
256
+
257
+ // Validate the intermediateDocument is not null and is parsable JSON
258
+ if (!intermediateDocument || !JSON.parsable(intermediateDocument)) {
259
+ throw new Btcr2Error(INVALID_DID_DOCUMENT, 'Invalid DID Document content', { intermediateDocument });
260
+ }
261
+ // 5. Replace the placeholder did with the identifier throughout the initialDocument.
262
+ const initialDocument = JSON.parse(
263
+ intermediateDocument.replaceAll(ID_PLACEHOLDER_VALUE, identifier)
264
+ );
265
+
266
+ // 6. Return initialDocument.
267
+ return new Btc1DidDocument(initialDocument);
268
+ }
269
+
270
+ /**
271
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#resolve-initial-document | 4.2.2 Resolve Initial Document}.
272
+ *
273
+ * This algorithm resolves an initial DID document and validates it against the identifier for a specific did:btcr2.
274
+ * The algorithm takes in a did:btcr2 identifier, identifier components object, resolutionsOptions object and returns
275
+ * a valid initialDocument for that identifier.
276
+ *
277
+ * @public
278
+ * @param {ResolveInitialDocument} params See {@link ResolveInitialDocument} for parameter details.
279
+ * @param {string} params.identifier The DID to be resolved.
280
+ * @param {DidComponents} params.identifierComponents The decoded components of the identifier.
281
+ * @param {DidResolutionOptions} params.resolutionsOptions Options for resolving the DID Document. See {@link DidResolutionOptions}.
282
+ * @returns {Promise<Btc1DidDocument>} The resolved DID Document object.
283
+ * @throws {DidError} if the DID hrp is invalid, no sidecarData passed and hrp = "x".
284
+ */
285
+ public static async initialDocument({ identifier, identifierComponents, resolutionsOptions }: {
286
+ identifier: string;
287
+ identifierComponents: DidComponents;
288
+ resolutionsOptions: DidResolutionOptions
289
+ }): Promise<Btc1DidDocument> {
290
+ // Deconstruct the hrp from the components
291
+ const hrp = identifierComponents.hrp;
292
+
293
+ // Validate the hrp is either 'k' or 'x'
294
+ if (!(hrp in Btcr2IdentifierHrp)) {
295
+ throw new Btcr2Error(`Invalid DID hrp ${hrp}`, INVALID_DID, { hrp });
296
+ }
297
+
298
+ // Make sure options.sidecarData is not null if hrp === x
299
+ if (hrp === Btcr2IdentifierHrp.x && !resolutionsOptions.sidecarData) {
300
+ throw new Btcr2Error('External resolution requires sidecar data', INVALID_DID, resolutionsOptions);
301
+ }
302
+
303
+ return hrp === Btcr2IdentifierHrp.k
304
+ ? this.deterministic({ identifier, identifierComponents })
305
+ : await this.external({ identifier, identifierComponents, resolutionsOptions });
306
+
307
+ }
308
+
309
+ /**
310
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#resolve-target-document | 4.2.3 Resolve Target Document}.
311
+ *
312
+ * The Resolve Target Document algorithm resolves a DID document from an initial document by walking the Bitcoin
313
+ * blockchain to identify Beacon Signals that announce DID Update Payloads applicable to the did:btcr2 identifier being
314
+ * resolved. It takes as inputs initialDocument, resolutionOptions and network. It returns a valid DID document.
315
+ *
316
+ * @public
317
+ * @param {TargetDocumentParams} params See {@link TargetDocumentParams} for details.
318
+ * @param {Btc1DidDocument} params.initialDocument The initial DID Document to resolve
319
+ * @param {ResolutionOptions} params.options See {@link DidResolutionOptions} for details.
320
+ * @returns {Btc1DidDocument} The resolved DID Document object with a validated single, canonical history
321
+ */
322
+ public static async targetDocument({ initialDocument, resolutionsOptions }: {
323
+ initialDocument: Btc1DidDocument;
324
+ resolutionsOptions: DidResolutionOptions;
325
+ }): Promise<Btc1DidDocument> {
326
+ // Set the network from the options or default to mainnet
327
+ const network = resolutionsOptions.network ?? BitcoinNetworkNames.bitcoin;
328
+
329
+ // 1. If resolutionOptions.versionId is not null, set targetVersionId to resolutionOptions.versionId.
330
+ const targetVersionId = resolutionsOptions.versionId;
331
+
332
+ // 2. Else if resolutionOptions.versionTime is not null, set targetTime to resolutionOptions.versionTime.
333
+ // 3. Else set targetTime to the UNIX timestamp for now at the moment of execution.
334
+ const targetTime = resolutionsOptions.versionTime ?? new Date().toUnix();
335
+
336
+ // 4. Set signalsMetadata to resolutionOptions.sidecarData.signalsMetadata.
337
+ const signalsMetadata = (resolutionsOptions.sidecarData as SidecarData).signalsMetadata;
338
+
339
+ // 5. Set currentVersionId to 1
340
+ const currentVersionId = 1;
341
+
342
+ // 6. If currentVersionId equals targetVersionId return initialDocument.
343
+ if (currentVersionId === targetVersionId) {
344
+ return new Btc1DidDocument(initialDocument);
345
+ }
346
+
347
+ // 10. Set targetDocument to the result of calling the Traverse Bitcoin Blockchain History algorithm
348
+ // passing in contemporaryDIDDocument, contemporaryBlockheight, currentVersionId, targetVersionId,
349
+ // targetTime, didDocumentHistory, btc1UpdateHashHistory, signalsMetadata, and network.
350
+ const targetDocument = this.traverseBlockchainHistory({
351
+ contemporaryDidDocument : initialDocument,
352
+ contemporaryBlockHeight : 0,
353
+ currentVersionId,
354
+ targetVersionId,
355
+ targetTime,
356
+ didDocumentHistory : new Array(),
357
+ btc1UpdateHashHistory : new Array(),
358
+ signalsMetadata,
359
+ network
360
+ });
361
+
362
+ // 11. Return targetDocument.
363
+ return targetDocument;
364
+ }
365
+
366
+ /**
367
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#traverse-blockchain-history | 4.2.3.2 Traverse Blockchain History}.
368
+ *
369
+ * The Traverse Blockchain History algorithm traverses Bitcoin blocks, starting from the block with the
370
+ * contemporaryBlockheight, to find beaconSignals emitted by Beacons within the contemporaryDidDocument. Each
371
+ * beaconSignal is processed to retrieve a didUpdatePayload to the DID document. Each update is applied to the
372
+ * document and duplicates are ignored. If the algorithm reaches the block with the blockheight specified by a
373
+ * targetBlockheight, the contemporaryDidDocument at that blockheight is returned assuming a single canonical history
374
+ * of the DID document has been constructed up to that point. It takes in contemporaryDidDocument,
375
+ * contemporaryBlockHeight, currentVersionId, targetVersionId, targetBlockheight, updateHashHistory, signalsMetadata
376
+ * and network. It returns the contemporaryDidDocument once either the targetBlockheight or targetVersionId have been
377
+ * reached.
378
+ *
379
+ * @protected
380
+ * @param {ReadBlockchainParams} params The parameters for the traverseBlockchainHistory operation.
381
+ * @param {Btc1DidDocument} params.contemporaryDidDocument The DID document for the did:btcr2 identifier being resolved.
382
+ * It should be "current" (contemporary) at the blockheight of the contemporaryBlockheight.
383
+ * It should be a DID Core conformant DID document.
384
+ * @param {number} params.contemporaryBlockHeight The Bitcoin blockheight signaling the "contemporary time" of the
385
+ * contemporary DID Document that is being resolved and updated using the Traverse Blockchain History algorithm.
386
+ * @param {number} params.currentVersionId The version of the contemporary DID document starting from 1 and
387
+ * incrementing by 1 with each BTCR2 Update applied to the DID document.
388
+ * @param {number} params.targetVersionId The version of the DID document where resolution will complete.
389
+ * @param {UnixTimestamp} params.targetTime The timestamp used to target specific historical states of a DID document.
390
+ * Only Beacon Signals included in the Bitcoin blockchain before the targetTime are processed.
391
+ * @param {boolean} params.didDocumentHistory An array of DID documents ordered ascensing by version (1...N).
392
+ * @param {boolean} params.btc1UpdateHashHistory An array of SHA256 hashes of BTCR2 Updates ordered by version that are
393
+ * applied to the DID document in order to construct the contemporaryDIDDocument.
394
+ * @param {SignalsMetadata} params.signalsMetadata See {@link SignalsMetadata} for details.
395
+ * @param {BitcoinNetworkNames} params.network The bitcoin network to connect to (mainnet, signet, testnet, regtest).
396
+ * @returns {Promise<Btc1DidDocument>} The resolved DID Document object with a validated single, canonical history.
397
+ */
398
+ protected static async traverseBlockchainHistory({
399
+ contemporaryDidDocument,
400
+ contemporaryBlockHeight,
401
+ currentVersionId,
402
+ targetVersionId,
403
+ targetTime,
404
+ didDocumentHistory,
405
+ btc1UpdateHashHistory,
406
+ signalsMetadata,
407
+ network
408
+ }: {
409
+ contemporaryDidDocument: Btc1DidDocument;
410
+ contemporaryBlockHeight: number;
411
+ currentVersionId: number;
412
+ targetVersionId?: number;
413
+ targetTime: number;
414
+ didDocumentHistory: Btc1DidDocument[];
415
+ btc1UpdateHashHistory: string[];
416
+ signalsMetadata: SignalsMetadata;
417
+ network: BitcoinNetworkNames;
418
+ }): Promise<Btc1DidDocument> {
419
+ // 1. Set contemporaryHash to the SHA256 hash of the contemporaryDidDocument
420
+ let contemporaryHash = await JSON.canonicalization.process(contemporaryDidDocument, 'base58');
421
+
422
+ // 2. Find all BTCR2 Beacons in contemporaryDIDDocument.service where service.type equals one of
423
+ // SingletonBeacon, CIDAggregateBeacon and SMTAggregateBeacon.
424
+ // 3. For each beacon in beacons convert the beacon.serviceEndpoint to a Bitcoin address
425
+ // following BIP21. Set beacon.address to the Bitcoin address.
426
+ const beacons = BeaconUtils.toBeaconServiceAddress(
427
+ BeaconUtils.getBeaconServices(contemporaryDidDocument)
428
+ );
429
+
430
+ // 4. Set nextSignals to the result of calling algorithm Find Next Signals passing in contemporaryBlockheight,
431
+ // beacons and network.
432
+ const nextSignals = await this.findNextSignals({ contemporaryBlockHeight, beacons, network, targetTime });
433
+ if (!nextSignals || nextSignals.length === 0) {
434
+ // 5. If nextSignals is null or empty, return contemporaryDidDocument.
435
+ return new Btc1DidDocument(contemporaryDidDocument);
436
+ }
437
+
438
+ // 6. If nextSignals[0].blocktime is greater than targetTime, return contemporaryDIDDocument.
439
+ if (nextSignals[0].blocktime > targetTime) {
440
+ return new Btc1DidDocument(contemporaryDidDocument);
441
+ }
442
+
443
+ // 8. Set updates to the result of calling algorithm Process Beacon Signals passing in signals and sidecarData.
444
+ // 9. Set orderedUpdates to the list of updates ordered by the targetVersionId property.
445
+ const orderedUpdates = (
446
+ await Promise.all(
447
+ nextSignals.map(
448
+ async signal => await this.processBeaconSignal(signal, signalsMetadata)
449
+ )
450
+ )
451
+ ).sort((a, b) => a.targetVersionId - b.targetVersionId);
452
+
453
+ // 10. For update in orderedUpdates:
454
+ for (let update of orderedUpdates) {
455
+ const updateTargetVersionId = update.targetVersionId;
456
+ // 10.1. If update.targetVersionId is less than or equal to currentVersionId, run Algorithm Confirm Duplicate
457
+ // Update passing in update, documentHistory, and contemporaryHash.
458
+ if (updateTargetVersionId <= currentVersionId) {
459
+ btc1UpdateHashHistory.push(contemporaryHash);
460
+ await this.confirmDuplicateUpdate({ update, updateHashHistory: btc1UpdateHashHistory });
461
+
462
+ // 10.2. If update.targetVersionId equals currentVersionId + 1:
463
+ } else if (updateTargetVersionId === currentVersionId + 1) {
464
+ // Prepend `z` to the sourceHash if it does not start with it
465
+ const sourceHash = update.sourceHash.startsWith('z') ? update.sourceHash : `z${update.sourceHash}`;
466
+
467
+ // 10.2.1. Check that update.sourceHash equals contemporaryHash, else MUST raise latePublishing error.
468
+ if (sourceHash !== contemporaryHash) {
469
+ throw new Btcr2ReadError(
470
+ `Hash mismatch: sourceHash ${sourceHash} !== contemporaryHash ${contemporaryHash}`,
471
+ LATE_PUBLISHING_ERROR, { sourceHash: sourceHash, contemporaryHash }
472
+ );
473
+ }
474
+
475
+ // 10.2.2. Set contemporaryDidDocument to the result of calling Apply DID Update algorithm passing in
476
+ // contemporaryDidDocument, update.
477
+ contemporaryDidDocument = await this.applyDidUpdate({ contemporaryDidDocument, update });
478
+
479
+ // 10.2.4 Push contemporaryDIDDocument onto didDocumentHistory.
480
+ didDocumentHistory.push(contemporaryDidDocument);
481
+
482
+ // 10.2.4. Increment currentVersionId.
483
+ currentVersionId++;
484
+
485
+ // 10.2.5. Set unsecuredUpdate to a copy of the update object.
486
+ const unsecuredUpdate = update;
487
+
488
+ // 10.2.6 Remove the proof property from the unsecuredUpdate object.
489
+ delete unsecuredUpdate.proof;
490
+
491
+ // 10.2.7 Set updateHash to the result of passing unsecuredUpdate into the JSON Canonicalization and Hash algorithm.
492
+ const updateHash = await JSON.canonicalization.process(update, 'base58');
493
+
494
+ // 10.2.8. Push updateHash onto btc1UpdateHashHistory.
495
+ btc1UpdateHashHistory.push(updateHash as string);
496
+
497
+ // 10.2.9. Set contemporaryHash to result of passing contemporaryDIDDocument into the JSON Canonicalization and Hash algorithm.
498
+ contemporaryHash = await JSON.canonicalization.process(contemporaryDidDocument, 'base58');
499
+
500
+ // 10.3. If update.targetVersionId is greater than currentVersionId + 1, MUST throw a LatePublishing error.
501
+ } else if (update.targetVersionId > currentVersionId + 1) {
502
+ throw new Btcr2ReadError(
503
+ `Version Id Mismatch: target ${update.targetVersionId} cannot be > current+1 ${currentVersionId + 1}`,
504
+ 'LATE_PUBLISHING_ERROR'
505
+ );
506
+ }
507
+ }
508
+
509
+ // 13. If targetVersionId in not null, set targetDocument to the index at the targetVersionId of the didDocumentHistory array.
510
+ if(targetVersionId) {
511
+ return new Btc1DidDocument(didDocumentHistory[targetVersionId]);
512
+ }
513
+
514
+ // 14. Return contemporaryDidDocument.
515
+ return new Btc1DidDocument(contemporaryDidDocument);
516
+ }
517
+
518
+
519
+ /**
520
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#find-next-signals | 4.2.3.3 Find Next Signals}.
521
+ *
522
+ * The Find Next Signals algorithm finds the next Bitcoin block containing Beacon Signals from one or more of the
523
+ * beacons and retuns all Beacon Signals within that block.
524
+ *
525
+ * It takes the following inputs:
526
+ * - `contemporaryBlockhieght`: The height of the block this function is looking for Beacon Signals in.
527
+ * An integer greater or equal to 0.
528
+ * - `targetBlockheight`: The height of the Bitcoin block that the resolution algorithm searches for Beacon Signals
529
+ * up to. An integer greater or equal to 0.
530
+ * - `beacons`: An array of Beacon services in the contemporary DID document. Each Beacon contains properties:
531
+ * - `id`: The id of the Beacon service in the DID document. A string.
532
+ * - `type`: The type of the Beacon service in the DID document. A string whose values MUST be
533
+ * either SingletonBeacon, CIDAggregateBeacon or SMTAggregateBeacon.
534
+ * - `serviceEndpoint`: A BIP21 URI representing a Bitcoin address.
535
+ * - `address`: The Bitcoin address decoded from the `serviceEndpoint value.
536
+ * - `network`: A string identifying the Bitcoin network of the did:btcr2 identifier. This algorithm MUST query the
537
+ * Bitcoin blockchain identified by the network.
538
+ *
539
+ * It returns a nextSignals struct, containing the following properties:
540
+ * - blockheight: The Bitcoin blockheight for the block containing the Beacon Signals.
541
+ * - signals: An array of signals. Each signal is a struct containing the following:
542
+ * - beaconId: The id for the Beacon that the signal was announced by.
543
+ * - beaconType: The type of the Beacon that announced the signal.
544
+ * - tx: The Bitcoin transaction that is the Beacon Signal.
545
+ *
546
+ * @public
547
+ * @param {FindNextSignals} params The parameters for the findNextSignals operation.
548
+ * @param {number} params.blockheight The blockheight to start looking for beacon signals.
549
+ * @param {Array<BeaconService>} params.target The target blockheight at which to stop finding signals.
550
+ * @param {Array<BeaconService>} params.beacons The beacons to look for in the block.
551
+ * @returns {Promise<Array<BeaconSignal>>} An array of BeaconSignal objects with blockHeight and signals.
552
+ */
553
+ public static async findNextSignals({ contemporaryBlockHeight, targetTime, beacons }: {
554
+ contemporaryBlockHeight: number;
555
+ beacons: Array<BeaconServiceAddress>;
556
+ network: BitcoinNetworkNames;
557
+ targetTime: UnixTimestamp;
558
+ }): Promise<Array<BeaconSignal>> {
559
+ let height = contemporaryBlockHeight;
560
+
561
+ // Toggle RPC or REST connection based on the connection type
562
+ const bitcoin = bitcoinNetwork ?? new Bitcoin();
563
+
564
+ // Create an default beaconSignal and beaconSignals array
565
+ let beaconSignals: BeaconSignals = [];
566
+
567
+ if (bitcoin.network.rest) {
568
+ return await this.findSignalsRest({ beacons });
569
+ }
570
+
571
+ // Use connection to get the block data at the blockhash
572
+ let block = await bitcoin.network.rpc.getBlock({ height }) as BlockV3;
573
+
574
+ Logger.info(`Searching for signals, please wait ...`);
575
+ while (block.time <= targetTime) {
576
+ // Iterate over each transaction in the block
577
+ for (const tx of block.tx) {
578
+ // If the txid is a coinbase, continue ...
579
+ if (tx.txid === GENESIS_TX_ID) {
580
+ continue;
581
+ }
582
+
583
+ // Iterate over each input in the transaction
584
+ for (const vin of tx.vin) {
585
+
586
+ // If the vin is a coinbase transaction, continue ...
587
+ if (vin.coinbase) {
588
+ continue;
589
+ }
590
+
591
+ // If the vin txinwitness contains a coinbase identifier, continue ...
592
+ if (vin.txinwitness && vin.txinwitness.length === 1 && vin.txinwitness[0] === TXIN_WITNESS_COINBASE) {
593
+ continue;
594
+ }
595
+
596
+ // If the txid from the vin is undefined, continue ...
597
+ if (!vin.txid) {
598
+ continue;
599
+ }
600
+
601
+ // If the vout from the vin is undefined, continue ...
602
+ if (vin.vout === undefined) {
603
+ continue;
604
+ }
605
+
606
+ // Get the previous output transaction data
607
+ const prevout = await bitcoin.network.rpc.getRawTransaction(vin.txid, 2) as RawTransactionV2;
608
+
609
+ // If the previous output vout at the vin.vout index is undefined, continue ...
610
+ if (!prevout.vout[vin.vout]) {
611
+ continue;
612
+ }
613
+
614
+ // Get the address from the scriptPubKey from the prevvout (previous output's input at the vout index)
615
+ const scriptPubKey = prevout.vout[vin.vout].scriptPubKey;
616
+
617
+ // If the scriptPubKey.address is undefined, continue ...
618
+ if (!scriptPubKey.address) {
619
+ continue;
620
+ }
621
+
622
+ // If the beaconAddress from prevvout scriptPubKey is not a beacon service endpoint address, continue ...
623
+ const beaconAddresses = BeaconUtils.getBeaconServiceAddressMap(beacons);
624
+ const beacon = (beaconAddresses.get(scriptPubKey.address) ?? {}) as BeaconServiceAddress;
625
+ if (!beacon || !(beacon.id && beacon.type)) {
626
+ continue;
627
+ }
628
+
629
+ // If the prevout.vout[vin.vout].scriptPubKey.asm does not include 'OP_RETURN', continue ...
630
+ if(!prevout.vout[vin.vout].scriptPubKey.asm.includes('OP_RETURN')) {
631
+ continue;
632
+ }
633
+
634
+ // Log the found txid and beacon
635
+ Logger.info(`Tx ${tx.txid} contains beacon address ${scriptPubKey.address} and OP_RETURN!`, tx);
636
+
637
+ // Push the signal object to to signals array
638
+ beaconSignals.push({
639
+ beaconId : beacon.id,
640
+ beaconType : beacon.type,
641
+ beaconAddress : beacon.address,
642
+ tx,
643
+ blockheight : block.height,
644
+ blocktime : block.time
645
+ });
646
+ };
647
+ }
648
+
649
+ height += 1;
650
+ const tip = await bitcoin.network.rpc.getBlockCount();
651
+ if(height > tip) {
652
+ Logger.info(`Chain tip reached ${height}, breaking ...`);
653
+ break;
654
+ }
655
+
656
+ // Reset the block to the next block
657
+ block = await bitcoin.network.rpc.getBlock({ height }) as BlockV3;
658
+ }
659
+
660
+ return beaconSignals;
661
+ }
662
+
663
+ /**
664
+ * Helper method for the {@link findNextSignals | Find Next Signals} algorithm.
665
+ *
666
+ * @param params See {@link FindNextSignalsRestParams} for details.
667
+ * @param {BitcoinClient} params.connection The bitcoin connection to use.
668
+ * @param {Array<BeaconSignal>} params.beaconSignals The beacon signals to process.
669
+ * @param {BlockV3} params.block The block to process.
670
+ * @param {Array<BeaconService>} params.beacons The beacons to process.
671
+ * @returns {Promise<Array<BeaconSignal>>} The beacon signals found in the block.
672
+ */
673
+ public static async findSignalsRest({ beacons }: { beacons: Array<BeaconService>; }): Promise<Array<BeaconSignal>> {
674
+ const bitcoin = bitcoinNetwork ?? new Bitcoin();
675
+
676
+ // Empty array of beaconSignals
677
+ const beaconSignals = new Array<BeaconSignal>();
678
+
679
+ // Iterate over each beacon
680
+ for (const beacon of BeaconUtils.toBeaconServiceAddress(beacons)) {
681
+ // Get the transactions for the beacon address via REST
682
+ const transactions = await bitcoin.rest.address.getTxs(beacon.address);
683
+
684
+ // If no transactions are found, continue
685
+ if (!transactions || transactions.length === 0) {
686
+ continue;
687
+ }
688
+
689
+ // Iterate over each transaction and push a beaconSignal
690
+ for (const tx of transactions) {
691
+ for(const vout of tx.vout) {
692
+ if(vout.scriptpubkey_asm.includes('OP_RETURN')) {
693
+ beaconSignals.push({
694
+ beaconId : beacon.id,
695
+ beaconType : beacon.type,
696
+ beaconAddress : beacon.address,
697
+ tx,
698
+ blockheight : tx.status.block_height,
699
+ blocktime : tx.status.block_time,
700
+ });
701
+ }
702
+ }
703
+ }
704
+ }
705
+
706
+ // Return the beaconSignals
707
+ return beaconSignals;
708
+ }
709
+
710
+ /**
711
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#process-beacon-signals | 4.2.3.4 Process Beacon Signals}.
712
+ *
713
+ * The Process Beacon Signals algorithm processes each Beacon Signal by attempting to retrieve and validate an
714
+ * announce DID Update Payload for that signal according to the type of the Beacon.
715
+ *
716
+ * It takes as inputs
717
+ * - `beaconSignals`: An array of Beacon Signals retrieved from the Find Next Signals algorithm. Each signal contains:
718
+ * - `beaconId`: The id for the Beacon that the signal was announced by.
719
+ * - `beaconType`: The type of the Beacon that announced the signal.
720
+ * - `tx`: The Bitcoin transaction that is the Beacon Signal.
721
+ * - `signalsMetadata`: Maps Beacon Signal Bitcoin transaction ids to a SignalMetadata object containing:
722
+ * - `updatePayload`: A DID Update Payload which should match the update announced by the Beacon Signal.
723
+ * In the case of a SMT proof of non-inclusion, no DID Update Payload may be provided.
724
+ * - `proofs`: Sparse Merkle Tree proof used to verify that the `updatePayload` exists as the leaf indexed by the
725
+ * did:btcr2 identifier being resolved.
726
+ *
727
+ * It returns an array of {@link https://dcdpr.github.io/did-btcr2/#def-did-update-payload | DID Update Payloads}.
728
+ *
729
+ * @public
730
+ * @param {BeaconSignal} signal The beacon signals to process.
731
+ * @param {SignalsMetadata} signalsMetadata The sidecar data for the DID Document.
732
+ * @returns {DidUpdatePayload[]} The updated DID Document object.
733
+ */
734
+ public static async processBeaconSignal(signal: BeaconSignal, signalsMetadata: SignalsMetadata): Promise<DidUpdatePayload> {
735
+ // 1. Set updates to an empty array.
736
+ const updates = new Array<DidUpdatePayload>();
737
+
738
+ // 2. For beaconSignal in beaconSignals:
739
+ // 2.1 Set type to beaconSignal.beaconType.
740
+ // 2.2 Set signalTx to beaconSignal.tx.
741
+ // 2.3 Set signalId to signalTx.id.
742
+ const {
743
+ beaconId: id,
744
+ beaconType: type,
745
+ beaconAddress: address,
746
+ tx
747
+ } = signal;
748
+ const signalTx = tx as RawTransactionRest | RawTransactionV2;
749
+
750
+ // 2.4 Set signalSidecarData to signalsMetadata[signalId]. TODO: formalize structure of sidecarData
751
+ // const signalSidecarData = new Map(Object.entries(signalsMetadata)).get(id)!;
752
+ // TODO: processBeaconSignal - formalize structure of sidecarData, signalSidecarData
753
+
754
+ // 2.6 If type == SingletonBeacon:
755
+ // 2.6.1 Set didUpdatePayload to the result of passing signalTx and signalSidecarData to Process Singleton Beacon Signal algorithm.
756
+ // 2.7 If type == CIDAggregateBeacon:
757
+ // 2.7.1 Set didUpdatePayload to the result of passing signalTx and signalSidecarData to the Process CIDAggregate Beacon Signal algorithm.
758
+ // 2.8 If type == SMTAggregateBeacon:
759
+ // 2.8.1 Set didUpdatePayload to the result of passing signalTx and signalSidecarData to the Process SMTAggregate Beacon Signal algorithm.
760
+
761
+ // TODO: processBeaconSignal - where/how to convert signalsMetadata to diff sidecars
762
+ const sidecar = { signalsMetadata } as SidecarData;
763
+ // switch (type) {
764
+ // case 'SingletonBeacon': {
765
+ // sidecar = { signalsMetadata } as SingletonSidecar;
766
+ // break;
767
+ // }
768
+ // case 'CIDAggregateBeacon': {
769
+ // sidecar = {} as CIDAggregateSidecar;
770
+ // break;
771
+ // }
772
+ // case 'SMTAggregateBeacon': {
773
+ // sidecar = {} as SMTAggregateSidecar;
774
+ // break;
775
+ // }
776
+ // default: {
777
+ // throw new Btcr2Error('Invalid beacon type', 'INVALID_BEACON_TYPE', { type });
778
+ // }
779
+ // }
780
+
781
+ // Construct a service object from the beaconId and type
782
+ // and set the serviceEndpoint to the BIP21 URI for the Bitcoin address.
783
+ const service = { id, type, serviceEndpoint: `bitcoin:${address}` };
784
+
785
+ // Establish a Beacon instance using the service and sidecar
786
+ const beacon = BeaconFactory.establish(service, sidecar);
787
+
788
+ // 2.5 Set btc1Update to null.
789
+ const btc1Update = await beacon.processSignal(signalTx, signalsMetadata) ?? null;
790
+
791
+ // If the updates is null, throw an error
792
+ if (!btc1Update) {
793
+ throw new Btcr2Error('No btc1Update for beacon', 'PROCESS_BEACON_SIGNALS_ERROR', { tx, signalsMetadata });
794
+ }
795
+
796
+ // 2.9 If btc1Update is not null, push btc1Update to updates.
797
+ updates.push(btc1Update);
798
+
799
+ // 3. Return updates.
800
+ return btc1Update;
801
+ }
802
+
803
+ /**
804
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#confirm-duplicate-update | 7.2.2.4 Confirm Duplicate Update}.
805
+ *
806
+ * The Confirm Duplicate Update algorithm takes in a {@link DidUpdatePayload | DID Update Payload} and verifies that
807
+ * the update is a duplicate against the hash history of previously applied updates. The algorithm takes in an update
808
+ * and an array of hashes, updateHashHistory. It throws an error if the update is not a duplicate, otherwise it
809
+ * returns.
810
+ *
811
+ * @public
812
+ * @param {{ update: DidUpdatePayload; updateHashHistory: string[]; }} params Parameters for confirmDuplicateUpdate.
813
+ * @param {DidUpdatePayload} params.update The DID Update Payload to confirm.
814
+ * @param {Array<string>} params.updateHashHistory The history of hashes for previously applied updates.
815
+ * @returns {Promise<void>} A promise that resolves if the update is a duplicate, otherwise throws an error.
816
+ * @throws {DidBtc1Error} if the update hash does not match the historical hash.
817
+ */
818
+ public static async confirmDuplicateUpdate({ update, updateHashHistory }: {
819
+ update: DidUpdatePayload;
820
+ updateHashHistory: string[];
821
+ }): Promise<void> {
822
+ // 1. Let unsecuredUpdate be a copy of the update object.
823
+ const unsecuredUpdate = update;
824
+
825
+ // 2. Remove the proof property from the unsecuredUpdate object.
826
+ delete unsecuredUpdate.proof;
827
+
828
+ // 3. Let updateHash equal the result of passing unsecuredUpdate into the JSON Canonicalization and Hash algorithm.
829
+ const updateHash = await JSON.canonicalization.process(unsecuredUpdate);
830
+
831
+ // 4. Let updateHashIndex equal update.targetVersionId - 2.
832
+ // const updateHashIndex = update.targetVersionId - 2;
833
+
834
+ // 5. Let historicalUpdateHash equal updateHashHistory[updateHashIndex].
835
+ const historicalUpdateHash = updateHashHistory[update.targetVersionId - 2];
836
+
837
+ // Check if the updateHash matches the historical hash
838
+ if (historicalUpdateHash !== updateHash) {
839
+ throw new Btcr2ReadError(
840
+ `Invalid duplicate: ${updateHash} does not match ${historicalUpdateHash}`,
841
+ 'LATE_PUBLISHING_ERROR', { updateHash, updateHashHistory }
842
+ );
843
+ }
844
+ }
845
+
846
+ /**
847
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#apply-did-update | 4.2.3.6 Apply DID Update}.
848
+ *
849
+ * This algorithm attempts to apply a DID Update to a DID document, it first verifies the proof on the update is a
850
+ * valid capabilityInvocation of the root authority over the DID being resolved. Then it applies the JSON patch
851
+ * transformation to the DID document, checks the transformed DID document matches the targetHash specified by the
852
+ * update and validates it is a conformant DID document before returning it. This algorithm takes inputs
853
+ * contemporaryDidDocument and an update.
854
+ *
855
+ * @public
856
+ * @param {ApplyDidUpdateParams} params Parameters for applyDidUpdate.
857
+ * @param {Btc1DidDocument} params.contemporaryDidDocument The current DID Document to update.
858
+ * @param {DidUpdatePayload} params.update The DID Update Payload to apply.
859
+ * @param {Bytes} params.genesisBytes The genesis bytes for the DID Document.
860
+ * @returns {Promise<Btc1DidDocument>}
861
+ */
862
+ public static async applyDidUpdate({ contemporaryDidDocument, update }: {
863
+ contemporaryDidDocument: Btc1DidDocument;
864
+ update: DidUpdatePayload;
865
+ }): Promise<Btc1DidDocument> {
866
+ // 1. Set capabilityId to update.proof.capability.
867
+ const capabilityId = update.proof?.capability;
868
+ if (!capabilityId) {
869
+ throw new Btcr2ReadError('No capabilityId found in update', INVALID_DID_UPDATE);
870
+ }
871
+
872
+ // 2. Set rootCapability to the result of passing capabilityId to the Dereference Root Capability Identifier algorithm.
873
+ const rootCapability = Btc1Appendix.derefernceRootCapabilityIdentifier(capabilityId);
874
+
875
+ // 3. If rootCapability.invocationTarget does not equal contemporaryDidDocument.id
876
+ // and rootCapability.controller does not equal contemporaryDidDocument.id, MUST throw an invalidDidUpdate error.
877
+ const { invocationTarget, controller: rootController } = rootCapability;
878
+ if (![invocationTarget, rootController].every((id) => id === contemporaryDidDocument.id)) {
879
+ throw new Btcr2ReadError(`Invalid root capability: ${rootCapability}`, INVALID_DID_UPDATE);
880
+ }
881
+
882
+ // 4. Instantiate a bip340-jcs-2025 cryptosuite instance using the key referenced by the verificationMethod field in the update.
883
+ // Get the verificationMethod field from the update.
884
+ const methodId = update.proof?.verificationMethod;
885
+ if(!methodId) {
886
+ throw new Btcr2ReadError('No verificationMethod found in update', INVALID_DID_UPDATE, update);
887
+ }
888
+
889
+ // Get the verificationMethod from the DID Document using the methodId.
890
+ const { id: vmId, publicKeyMultibase } = DidBtc1.getSigningMethod({ didDocument: contemporaryDidDocument, methodId });
891
+
892
+ // Split the vmId by the `#` to get the id and controller.
893
+ const [controller, id] = vmId.split('#');
894
+
895
+ // Construct a new Multikey.
896
+ const multikey = SchnorrMultikey.fromPublicKeyMultibase({ id: `#${id}`, controller, publicKeyMultibase });
897
+ // Logger.warn('// TODO: applyDidUpdate - Refactor Multikey to accept pub/priv bytes => Pub/PrivKey => KeyPair.');
898
+
899
+ const cryptosuite = new Cryptosuite({ cryptosuite: 'bip340-jcs-2025', multikey });
900
+ // Logger.warn('// TODO: applyDidUpdate - Refactor Cryptosuite to default to RDFC.');
901
+
902
+ // 5. Set expectedProofPurpose to capabilityInvocation.
903
+ const expectedPurpose = 'capabilityInvocation';
904
+
905
+ // 6. Set mediaType to ????
906
+ // const mediaType = 'application/json';
907
+ // Logger.warn('// TODO: applyDidUpdate - is this just application/json?');
908
+
909
+ // 7. Set documentBytes to the bytes representation of update.
910
+ const documentBytes = await JSON.canonicalization.canonicalize(update);
911
+
912
+ // 8. Set verificationResult to the result of passing mediaType, documentBytes, cryptosuite, and
913
+ // expectedProofPurpose into the Verify Proof algorithm defined in the VC Data Integrity specification.
914
+ const diProof = new DataIntegrityProof(cryptosuite);
915
+ const verificationResult = await diProof.verifyProof({ document: documentBytes, expectedPurpose });
916
+
917
+ // 9. If verificationResult.verified equals False, MUST raise a invalidUpdateProof exception.
918
+ if (!verificationResult.verified) {
919
+ throw new Btcr2Error('Invalid update: proof not verified', INVALID_DID_UPDATE, verificationResult);
920
+ }
921
+
922
+ // 10. Set targetDIDDocument to a copy of contemporaryDidDocument.
923
+ let targetDIDDocument = contemporaryDidDocument;
924
+
925
+ // 11. Use JSON Patch to apply the update.patch to the targetDIDDOcument.
926
+ targetDIDDocument = JSON.patch.apply(targetDIDDocument, update.patch) as Btc1DidDocument;
927
+
928
+ // 12. Verify that targetDIDDocument is conformant with the data model specified by the DID Core specification.
929
+ Btc1DidDocument.validate(targetDIDDocument);
930
+
931
+ // 13. Set targetHash to the SHA256 hash of targetDIDDocument.
932
+ const targetHash = await JSON.canonicalization.process(targetDIDDocument, 'base58');
933
+
934
+ // Prepend the sourceHash if it does not start with `z`
935
+ const updateTargetHash = update.targetHash.startsWith('z')
936
+ ? update.targetHash
937
+ : `z${update.targetHash}`;
938
+ // 14. Check that targetHash equals update.targetHash, else raise InvalidDIDUpdate error.
939
+ if (updateTargetHash !== targetHash) {
940
+ throw new Btcr2Error(`Invalid update: updateTargetHash ${updateTargetHash} does not match targetHash ${targetHash}`, INVALID_DID_UPDATE);
941
+ }
942
+
943
+ // 15. Return targetDIDDocument.
944
+ return targetDIDDocument;
945
+ }
946
+ }