@bsv/overlay 0.1.0-alpha.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.
- package/LICENSE.txt +28 -0
- package/README.md +220 -0
- package/dist/cjs/mod.js +43 -0
- package/dist/cjs/mod.js.map +1 -0
- package/dist/cjs/package.json +53 -0
- package/dist/cjs/src/AdmittanceInstructions.js +3 -0
- package/dist/cjs/src/AdmittanceInstructions.js.map +1 -0
- package/dist/cjs/src/Engine.js +361 -0
- package/dist/cjs/src/Engine.js.map +1 -0
- package/dist/cjs/src/LookupAnswer.js +3 -0
- package/dist/cjs/src/LookupAnswer.js.map +1 -0
- package/dist/cjs/src/LookupFormula.js +3 -0
- package/dist/cjs/src/LookupFormula.js.map +1 -0
- package/dist/cjs/src/LookupQuestion.js +3 -0
- package/dist/cjs/src/LookupQuestion.js.map +1 -0
- package/dist/cjs/src/LookupService.js +3 -0
- package/dist/cjs/src/LookupService.js.map +1 -0
- package/dist/cjs/src/Output.js +3 -0
- package/dist/cjs/src/Output.js.map +1 -0
- package/dist/cjs/src/STEAK.js +3 -0
- package/dist/cjs/src/STEAK.js.map +1 -0
- package/dist/cjs/src/TaggedBEEF.js +3 -0
- package/dist/cjs/src/TaggedBEEF.js.map +1 -0
- package/dist/cjs/src/TopicManager.js +3 -0
- package/dist/cjs/src/TopicManager.js.map +1 -0
- package/dist/cjs/src/storage/Storage.js +3 -0
- package/dist/cjs/src/storage/Storage.js.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.js +76 -0
- package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -0
- package/dist/cjs/src/storage/knex/all-migrations.js +11 -0
- package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js +33 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/esm/mod.js +17 -0
- package/dist/esm/mod.js.map +1 -0
- package/dist/esm/src/AdmittanceInstructions.js +2 -0
- package/dist/esm/src/AdmittanceInstructions.js.map +1 -0
- package/dist/esm/src/Engine.js +357 -0
- package/dist/esm/src/Engine.js.map +1 -0
- package/dist/esm/src/LookupAnswer.js +2 -0
- package/dist/esm/src/LookupAnswer.js.map +1 -0
- package/dist/esm/src/LookupFormula.js +2 -0
- package/dist/esm/src/LookupFormula.js.map +1 -0
- package/dist/esm/src/LookupQuestion.js +2 -0
- package/dist/esm/src/LookupQuestion.js.map +1 -0
- package/dist/esm/src/LookupService.js +2 -0
- package/dist/esm/src/LookupService.js.map +1 -0
- package/dist/esm/src/Output.js +2 -0
- package/dist/esm/src/Output.js.map +1 -0
- package/dist/esm/src/STEAK.js +2 -0
- package/dist/esm/src/STEAK.js.map +1 -0
- package/dist/esm/src/TaggedBEEF.js +2 -0
- package/dist/esm/src/TaggedBEEF.js.map +1 -0
- package/dist/esm/src/TopicManager.js +2 -0
- package/dist/esm/src/TopicManager.js.map +1 -0
- package/dist/esm/src/storage/Storage.js +2 -0
- package/dist/esm/src/storage/Storage.js.map +1 -0
- package/dist/esm/src/storage/knex/KnexStorage.js +74 -0
- package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -0
- package/dist/esm/src/storage/knex/all-migrations.js +9 -0
- package/dist/esm/src/storage/knex/all-migrations.js.map +1 -0
- package/dist/esm/src/storage/knex/migrations/2024-05-18-001-initial.js +28 -0
- package/dist/esm/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/types/mod.d.ts +14 -0
- package/dist/types/mod.d.ts.map +1 -0
- package/dist/types/src/AdmittanceInstructions.d.ts +14 -0
- package/dist/types/src/AdmittanceInstructions.d.ts.map +1 -0
- package/dist/types/src/Engine.d.ts +92 -0
- package/dist/types/src/Engine.d.ts.map +1 -0
- package/dist/types/src/LookupAnswer.d.ts +15 -0
- package/dist/types/src/LookupAnswer.d.ts.map +1 -0
- package/dist/types/src/LookupFormula.d.ts +25 -0
- package/dist/types/src/LookupFormula.d.ts.map +1 -0
- package/dist/types/src/LookupQuestion.d.ts +15 -0
- package/dist/types/src/LookupQuestion.d.ts.map +1 -0
- package/dist/types/src/LookupService.d.ts +52 -0
- package/dist/types/src/LookupService.d.ts.map +1 -0
- package/dist/types/src/Output.d.ts +30 -0
- package/dist/types/src/Output.d.ts.map +1 -0
- package/dist/types/src/STEAK.d.ts +10 -0
- package/dist/types/src/STEAK.d.ts.map +1 -0
- package/dist/types/src/TaggedBEEF.d.ts +11 -0
- package/dist/types/src/TaggedBEEF.d.ts.map +1 -0
- package/dist/types/src/TopicManager.d.ts +27 -0
- package/dist/types/src/TopicManager.d.ts.map +1 -0
- package/dist/types/src/storage/Storage.d.ts +66 -0
- package/dist/types/src/storage/Storage.d.ts.map +1 -0
- package/dist/types/src/storage/knex/KnexStorage.d.ts +24 -0
- package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -0
- package/dist/types/src/storage/knex/all-migrations.d.ts +10 -0
- package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -0
- package/dist/types/src/storage/knex/migrations/2024-05-18-001-initial.d.ts +4 -0
- package/dist/types/src/storage/knex/migrations/2024-05-18-001-initial.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -0
- package/docs/API.md +621 -0
- package/docs/README.md +8 -0
- package/docs/concepts/README.md +4 -0
- package/docs/examples/README.md +5 -0
- package/docs/examples/gs-wip.md +105 -0
- package/docs/internal/README.md +4 -0
- package/mod.ts +18 -0
- package/package.json +78 -0
- package/src/AdmittanceInstructions.ts +14 -0
- package/src/Engine.ts +419 -0
- package/src/LookupAnswer.ts +14 -0
- package/src/LookupFormula.ts +26 -0
- package/src/LookupQuestion.ts +15 -0
- package/src/LookupService.ts +52 -0
- package/src/Output.ts +29 -0
- package/src/STEAK.ts +10 -0
- package/src/TaggedBEEF.ts +10 -0
- package/src/TopicManager.ts +23 -0
- package/src/__tests/Engine.test.ts +792 -0
- package/src/storage/Storage.ts +72 -0
- package/src/storage/knex/KnexStorage.ts +90 -0
- package/src/storage/knex/all-migrations.ts +14 -0
- package/src/storage/knex/migrations/2024-05-18-001-initial.ts +30 -0
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
import Engine from '../Engine'
|
|
2
|
+
import LookupService from '../LookupService'
|
|
3
|
+
import TopicManager from '../TopicManager'
|
|
4
|
+
import { AdmittanceInstructions } from '../AdmittanceInstructions'
|
|
5
|
+
import Storage from '../storage/Storage'
|
|
6
|
+
import { Transaction } from '@bsv/sdk'
|
|
7
|
+
import { Output } from '../Output'
|
|
8
|
+
|
|
9
|
+
const mockChainTracker = {
|
|
10
|
+
isValidRootForHeight: jest.fn(async () => true)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const BRC62Hex = '0100beef01fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331020100000001cd4e4cac3c7b56920d1e7655e7e260d31f29d9a388d04910f1bbd72304a79029010000006b483045022100e75279a205a547c445719420aa3138bf14743e3f42618e5f86a19bde14bb95f7022064777d34776b05d816daf1699493fcdf2ef5a5ab1ad710d9c97bfb5b8f7cef3641210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013e660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000001000100000001ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e000000006a47304402203a61a2e931612b4bda08d541cfb980885173b8dcf64a3471238ae7abcd368d6402204cbf24f04b9aa2256d8901f0ed97866603d2be8324c2bfb7a37bf8fc90edd5b441210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013c660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000000'
|
|
14
|
+
const exampleTX = Transaction.fromHexBEEF(BRC62Hex)
|
|
15
|
+
|
|
16
|
+
const exampleBeef = exampleTX.toBEEF()
|
|
17
|
+
const exampleTXID = exampleTX.id('hex') as string
|
|
18
|
+
const examplePreviousTXID = '3ecead27a44d013ad1aae40038acbb1883ac9242406808bb4667c15b4f164eac'
|
|
19
|
+
let mockTopicManager: TopicManager, mockLookupService: LookupService, mockStorageEngine: Storage
|
|
20
|
+
const mockOutput: Output = {
|
|
21
|
+
txid: exampleTXID,
|
|
22
|
+
outputIndex: 0,
|
|
23
|
+
outputScript: exampleTX.outputs[0].lockingScript.toBinary(),
|
|
24
|
+
topic: 'hello',
|
|
25
|
+
satoshis: exampleTX.outputs[0].satoshis as number,
|
|
26
|
+
beef: exampleBeef,
|
|
27
|
+
spent: false,
|
|
28
|
+
outputsConsumed: [],
|
|
29
|
+
consumedBy: []
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('BSV Overlay Services Engine', () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
jest.clearAllMocks()
|
|
35
|
+
|
|
36
|
+
mockTopicManager = {
|
|
37
|
+
identifyAdmissibleOutputs: jest.fn(async (): Promise<AdmittanceInstructions> => ({
|
|
38
|
+
outputsToAdmit: [0],
|
|
39
|
+
coinsToRetain: []
|
|
40
|
+
})),
|
|
41
|
+
getDocumentation: async () => 'Topical Documentation',
|
|
42
|
+
getMetaData: async () => ({ name: 'Mock Manager', shortDescription: 'Mock Short Manager Description' })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
mockLookupService = {
|
|
46
|
+
outputAdded: jest.fn(),
|
|
47
|
+
outputSpent: jest.fn(),
|
|
48
|
+
lookup: jest.fn(),
|
|
49
|
+
outputDeleted: jest.fn(),
|
|
50
|
+
getDocumentation: async () => 'Service Documentation',
|
|
51
|
+
getMetaData: async () => ({ name: 'Mock Service', shortDescription: 'Mock Short Service Description' })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
mockStorageEngine = {
|
|
55
|
+
doesAppliedTransactionExist: jest.fn(async () => false),
|
|
56
|
+
insertAppliedTransaction: jest.fn(),
|
|
57
|
+
insertOutput: jest.fn(),
|
|
58
|
+
findOutput: jest.fn(async () => null),
|
|
59
|
+
markUTXOAsSpent: jest.fn(),
|
|
60
|
+
updateConsumedBy: jest.fn(),
|
|
61
|
+
deleteOutput: jest.fn()
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
describe('submit', () => {
|
|
65
|
+
it('Throws an error if the user submits to a topic that is not supported', async () => {
|
|
66
|
+
const engine = new Engine(
|
|
67
|
+
{
|
|
68
|
+
Hello: mockTopicManager
|
|
69
|
+
},
|
|
70
|
+
{},
|
|
71
|
+
mockStorageEngine,
|
|
72
|
+
mockChainTracker
|
|
73
|
+
)
|
|
74
|
+
await expect(engine.submit({
|
|
75
|
+
beef: exampleBeef,
|
|
76
|
+
topics: ['hello']
|
|
77
|
+
})).rejects.toHaveProperty('message', 'This server does not support this topic: hello')
|
|
78
|
+
})
|
|
79
|
+
it('Verifies the BEEF for the provided transaction', async () => {
|
|
80
|
+
const engine = new Engine(
|
|
81
|
+
{
|
|
82
|
+
Hello: mockTopicManager
|
|
83
|
+
},
|
|
84
|
+
{},
|
|
85
|
+
mockStorageEngine,
|
|
86
|
+
mockChainTracker
|
|
87
|
+
)
|
|
88
|
+
await engine.submit({
|
|
89
|
+
beef: exampleBeef,
|
|
90
|
+
topics: ['Hello']
|
|
91
|
+
})
|
|
92
|
+
expect(mockChainTracker.isValidRootForHeight).toHaveBeenCalledWith('bb6f640cc4ee56bf38eb5a1969ac0c16caa2d3d202b22bf3735d10eec0ca6e00', 814435)
|
|
93
|
+
})
|
|
94
|
+
it('Throws an error if an invalid envelope is provided', async () => {
|
|
95
|
+
const engine = new Engine(
|
|
96
|
+
{
|
|
97
|
+
Hello: mockTopicManager
|
|
98
|
+
},
|
|
99
|
+
{},
|
|
100
|
+
mockStorageEngine,
|
|
101
|
+
mockChainTracker
|
|
102
|
+
)
|
|
103
|
+
mockChainTracker.isValidRootForHeight.mockReturnValueOnce(Promise.resolve(false))
|
|
104
|
+
await expect(engine.submit({
|
|
105
|
+
beef: exampleBeef,
|
|
106
|
+
topics: ['Hello']
|
|
107
|
+
})).rejects.toHaveProperty('message', 'Verification failed because the input at index 0 of transaction 3ecead27a44d013ad1aae40038acbb1883ac9242406808bb4667c15b4f164eac is missing an associated source transaction. This source transaction is required for transaction verification because there is no merkle proof for the transaction spending a UTXO it contains.')
|
|
108
|
+
})
|
|
109
|
+
describe('For each topic being processed', () => {
|
|
110
|
+
it('Checks for duplicate transactions', async () => {
|
|
111
|
+
const engine = new Engine(
|
|
112
|
+
{
|
|
113
|
+
Hello: mockTopicManager
|
|
114
|
+
},
|
|
115
|
+
{},
|
|
116
|
+
mockStorageEngine,
|
|
117
|
+
mockChainTracker
|
|
118
|
+
)
|
|
119
|
+
await engine.submit({
|
|
120
|
+
beef: exampleBeef,
|
|
121
|
+
topics: ['Hello']
|
|
122
|
+
})
|
|
123
|
+
expect(mockStorageEngine.doesAppliedTransactionExist).toHaveBeenCalledWith({ txid: exampleTXID, topic: 'Hello' })
|
|
124
|
+
})
|
|
125
|
+
it('Does not process the output if the transaction was already applied to the topic', async () => {
|
|
126
|
+
mockStorageEngine.doesAppliedTransactionExist = jest.fn(async () => true)
|
|
127
|
+
const engine = new Engine(
|
|
128
|
+
{
|
|
129
|
+
Hello: mockTopicManager
|
|
130
|
+
},
|
|
131
|
+
{},
|
|
132
|
+
mockStorageEngine,
|
|
133
|
+
mockChainTracker
|
|
134
|
+
)
|
|
135
|
+
await engine.submit({
|
|
136
|
+
beef: exampleBeef,
|
|
137
|
+
topics: ['Hello']
|
|
138
|
+
})
|
|
139
|
+
expect(mockStorageEngine.doesAppliedTransactionExist).toReturnWith(Promise.resolve(true))
|
|
140
|
+
expect(mockStorageEngine.findOutput).not.toHaveBeenCalled()
|
|
141
|
+
expect(mockStorageEngine.markUTXOAsSpent).not.toHaveBeenCalled()
|
|
142
|
+
expect(mockStorageEngine.insertOutput).not.toHaveBeenCalled()
|
|
143
|
+
})
|
|
144
|
+
describe('For each input of the transaction', () => {
|
|
145
|
+
it('Acquires the appropriate previous topical UTXOs from the storage engine', async () => {
|
|
146
|
+
// Mock findUTXO to return a UTXO
|
|
147
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
148
|
+
const engine = new Engine(
|
|
149
|
+
{
|
|
150
|
+
Hello: mockTopicManager
|
|
151
|
+
},
|
|
152
|
+
{},
|
|
153
|
+
mockStorageEngine,
|
|
154
|
+
mockChainTracker
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
// Submit the utxo
|
|
158
|
+
await engine.submit({
|
|
159
|
+
beef: exampleBeef,
|
|
160
|
+
topics: ['Hello']
|
|
161
|
+
})
|
|
162
|
+
expect(mockStorageEngine.findOutput).toHaveBeenCalled()
|
|
163
|
+
})
|
|
164
|
+
it('Includes the appropriate previous topical UTXOs when they are returned from the storage engine', async () => {
|
|
165
|
+
// Mock findUTXO to return a UTXO
|
|
166
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
167
|
+
const engine = new Engine(
|
|
168
|
+
{
|
|
169
|
+
Hello: mockTopicManager
|
|
170
|
+
},
|
|
171
|
+
{},
|
|
172
|
+
mockStorageEngine,
|
|
173
|
+
mockChainTracker
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
// Submit the utxo
|
|
177
|
+
await engine.submit({
|
|
178
|
+
beef: exampleBeef,
|
|
179
|
+
topics: ['Hello']
|
|
180
|
+
})
|
|
181
|
+
expect(mockStorageEngine.findOutput).toHaveBeenCalled()
|
|
182
|
+
expect(mockStorageEngine.markUTXOAsSpent).toHaveBeenCalledWith(exampleTXID, 0, 'Hello')
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
it('Identifies admissible outputs with the appropriate topic manager', async () => {
|
|
186
|
+
// Mock findUTXO to return a UTXO
|
|
187
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
188
|
+
const engine = new Engine(
|
|
189
|
+
{
|
|
190
|
+
Hello: mockTopicManager
|
|
191
|
+
},
|
|
192
|
+
{},
|
|
193
|
+
mockStorageEngine,
|
|
194
|
+
mockChainTracker
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
// Submit the utxo
|
|
198
|
+
await engine.submit({
|
|
199
|
+
beef: exampleBeef,
|
|
200
|
+
topics: ['Hello']
|
|
201
|
+
})
|
|
202
|
+
expect(engine.managers.Hello.identifyAdmissibleOutputs).toHaveBeenCalledWith(exampleBeef, [0])
|
|
203
|
+
})
|
|
204
|
+
describe('When previous UTXOs were retained by the topic manager', () => {
|
|
205
|
+
it('Notifies all lookup services about the output being spent (not deleted, see the comment about this in deleteUTXODeep)', async () => {
|
|
206
|
+
// Mock findUTXO to return a UTXO
|
|
207
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
208
|
+
const engine = new Engine(
|
|
209
|
+
{
|
|
210
|
+
Hello: mockTopicManager
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
Hello: mockLookupService
|
|
214
|
+
},
|
|
215
|
+
mockStorageEngine,
|
|
216
|
+
mockChainTracker
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
// Submit the utxo
|
|
220
|
+
await engine.submit({
|
|
221
|
+
beef: exampleBeef,
|
|
222
|
+
topics: ['Hello']
|
|
223
|
+
})
|
|
224
|
+
expect(engine.lookupServices.Hello.outputSpent).toHaveBeenCalledWith(exampleTXID, 0, 'Hello')
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
describe('When previous UTXOs were not retained by the topic manager', () => {
|
|
228
|
+
it('Marks the UTXO as stale, deleting all stale UTXOs by calling deleteUTXODeep', async () => {
|
|
229
|
+
// Mock findUTXO to return a UTXO
|
|
230
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
231
|
+
const engine = new Engine(
|
|
232
|
+
{
|
|
233
|
+
Hello: mockTopicManager
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
Hello: mockLookupService
|
|
237
|
+
},
|
|
238
|
+
mockStorageEngine,
|
|
239
|
+
mockChainTracker
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
// Submit the utxo
|
|
243
|
+
await engine.submit({
|
|
244
|
+
beef: exampleBeef,
|
|
245
|
+
topics: ['Hello']
|
|
246
|
+
})
|
|
247
|
+
// Test that previous UTXOs are deleted
|
|
248
|
+
expect(mockStorageEngine.deleteOutput).toHaveBeenCalledWith(exampleTXID, 0, 'hello')
|
|
249
|
+
expect(mockLookupService.outputDeleted).toHaveBeenCalledWith(exampleTXID, 0, 'hello')
|
|
250
|
+
})
|
|
251
|
+
it('Notifies all lookup services about the output being spent (the notification about the actual deletion will come from deleteUTXODeep)', async () => {
|
|
252
|
+
// Mock findUTXO to return a UTXO
|
|
253
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
254
|
+
const engine = new Engine(
|
|
255
|
+
{
|
|
256
|
+
Hello: mockTopicManager
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
Hello: mockLookupService
|
|
260
|
+
},
|
|
261
|
+
mockStorageEngine,
|
|
262
|
+
mockChainTracker
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
// Submit the utxo
|
|
266
|
+
await engine.submit({
|
|
267
|
+
beef: exampleBeef,
|
|
268
|
+
topics: ['Hello']
|
|
269
|
+
})
|
|
270
|
+
// Was the lookup service notified of the output deletion?
|
|
271
|
+
expect(mockLookupService.outputDeleted).toHaveBeenCalledWith(exampleTXID, 0, 'hello')
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
|
+
it('Adds admissible UTXOs to the storage engine', async () => {
|
|
275
|
+
// Mock findUTXO to return a UTXO
|
|
276
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
277
|
+
const engine = new Engine(
|
|
278
|
+
{
|
|
279
|
+
hello: mockTopicManager
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
Hello: mockLookupService
|
|
283
|
+
},
|
|
284
|
+
mockStorageEngine,
|
|
285
|
+
mockChainTracker
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
// Submit the utxo
|
|
289
|
+
await engine.submit({
|
|
290
|
+
beef: exampleBeef,
|
|
291
|
+
topics: ['hello']
|
|
292
|
+
})
|
|
293
|
+
// Test the new UTXO was added
|
|
294
|
+
expect(mockStorageEngine.insertOutput).toHaveBeenCalledWith(mockOutput)
|
|
295
|
+
})
|
|
296
|
+
it('Notifies lookup services about incoming admissible UTXOs', async () => {
|
|
297
|
+
// Mock findUTXO to return a UTXO
|
|
298
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
299
|
+
const engine = new Engine(
|
|
300
|
+
{
|
|
301
|
+
Hello: mockTopicManager
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
Hello: mockLookupService
|
|
305
|
+
},
|
|
306
|
+
mockStorageEngine,
|
|
307
|
+
mockChainTracker
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
// Submit the utxo
|
|
311
|
+
await engine.submit({
|
|
312
|
+
beef: exampleBeef,
|
|
313
|
+
topics: ['Hello']
|
|
314
|
+
})
|
|
315
|
+
// Test the lookup service was notified of the new UTXO
|
|
316
|
+
expect(mockLookupService.outputAdded).toHaveBeenCalledWith(
|
|
317
|
+
exampleTXID,
|
|
318
|
+
0,
|
|
319
|
+
exampleTX.outputs[0].lockingScript,
|
|
320
|
+
'Hello'
|
|
321
|
+
)
|
|
322
|
+
})
|
|
323
|
+
describe('For each consumed UTXO', () => {
|
|
324
|
+
it('Finds the UTXO', async () => {
|
|
325
|
+
// Mock findUTXO to return a UTXO
|
|
326
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
327
|
+
mockTopicManager.identifyAdmissibleOutputs = jest.fn(async () => {
|
|
328
|
+
return {
|
|
329
|
+
outputsToAdmit: [0],
|
|
330
|
+
coinsToRetain: [0]
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
const engine = new Engine(
|
|
334
|
+
{
|
|
335
|
+
Hello: mockTopicManager
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
Hello: mockLookupService
|
|
339
|
+
},
|
|
340
|
+
mockStorageEngine,
|
|
341
|
+
mockChainTracker
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
// Submit the utxo
|
|
345
|
+
await engine.submit({
|
|
346
|
+
beef: exampleBeef,
|
|
347
|
+
topics: ['Hello']
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
// Test a storage engine lookup happens for the utxo consumed
|
|
351
|
+
expect(mockStorageEngine.findOutput).toHaveBeenCalledWith(examplePreviousTXID, 0, 'Hello')
|
|
352
|
+
})
|
|
353
|
+
it('Updates the UTXO to reflect that it is now additionally consumed by the newly-created UTXOs', async () => {
|
|
354
|
+
// Mock findUTXO to return a UTXO
|
|
355
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
356
|
+
mockTopicManager.identifyAdmissibleOutputs = jest.fn(async () => {
|
|
357
|
+
return {
|
|
358
|
+
outputsToAdmit: [0],
|
|
359
|
+
coinsToRetain: [0]
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
const engine = new Engine(
|
|
363
|
+
{
|
|
364
|
+
Hello: mockTopicManager
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
Hello: mockLookupService
|
|
368
|
+
},
|
|
369
|
+
mockStorageEngine,
|
|
370
|
+
mockChainTracker
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
// Submit the utxo
|
|
374
|
+
await engine.submit({
|
|
375
|
+
beef: exampleBeef,
|
|
376
|
+
topics: ['Hello']
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
// Test the consumedBy data is updated
|
|
380
|
+
expect(mockStorageEngine.updateConsumedBy).toHaveBeenCalledWith(examplePreviousTXID, 0, 'Hello', [{
|
|
381
|
+
txid: exampleTXID,
|
|
382
|
+
outputIndex: 0
|
|
383
|
+
}])
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
it('Inserts a new applied transaction to avoid de-duplication', async () => {
|
|
387
|
+
// Mock findUTXO to return a UTXO
|
|
388
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
389
|
+
mockTopicManager.identifyAdmissibleOutputs = jest.fn(async () => {
|
|
390
|
+
return {
|
|
391
|
+
outputsToAdmit: [0],
|
|
392
|
+
coinsToRetain: [0]
|
|
393
|
+
}
|
|
394
|
+
})
|
|
395
|
+
const engine = new Engine(
|
|
396
|
+
{
|
|
397
|
+
Hello: mockTopicManager
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
Hello: mockLookupService
|
|
401
|
+
},
|
|
402
|
+
mockStorageEngine,
|
|
403
|
+
mockChainTracker
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
// Submit the utxo
|
|
407
|
+
await engine.submit({
|
|
408
|
+
beef: exampleBeef,
|
|
409
|
+
topics: ['Hello']
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
// Test the tx is inserted
|
|
413
|
+
expect(mockStorageEngine.insertAppliedTransaction).toHaveBeenCalledWith({
|
|
414
|
+
txid: exampleTXID,
|
|
415
|
+
topic: 'Hello'
|
|
416
|
+
})
|
|
417
|
+
})
|
|
418
|
+
it('Returns a correct set of admitted topics and outputs', async () => {
|
|
419
|
+
// Mock findUTXO to return a UTXO
|
|
420
|
+
mockStorageEngine.insertOutput = jest.fn()
|
|
421
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
422
|
+
mockTopicManager.identifyAdmissibleOutputs = jest.fn(async () => {
|
|
423
|
+
return {
|
|
424
|
+
outputsToAdmit: [0],
|
|
425
|
+
coinsToRetain: [0]
|
|
426
|
+
}
|
|
427
|
+
})
|
|
428
|
+
const engine = new Engine(
|
|
429
|
+
{
|
|
430
|
+
Hello: mockTopicManager
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
Hello: mockLookupService
|
|
434
|
+
},
|
|
435
|
+
mockStorageEngine,
|
|
436
|
+
mockChainTracker
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
// Submit the utxo
|
|
440
|
+
const results = await engine.submit({
|
|
441
|
+
beef: exampleBeef,
|
|
442
|
+
topics: ['Hello']
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
// Test the correct outputs are admitted
|
|
446
|
+
expect(results).toEqual({
|
|
447
|
+
Hello: {
|
|
448
|
+
outputsToAdmit: [0],
|
|
449
|
+
coinsToRetain: [0]
|
|
450
|
+
}
|
|
451
|
+
})
|
|
452
|
+
})
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
describe('lookup', () => {
|
|
456
|
+
it('Throws an error if no lookup service has this provider name', async () => {
|
|
457
|
+
const engine = new Engine(
|
|
458
|
+
{
|
|
459
|
+
Hello: mockTopicManager
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
Hello: mockLookupService
|
|
463
|
+
},
|
|
464
|
+
mockStorageEngine,
|
|
465
|
+
mockChainTracker
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
// Perform a lookup request
|
|
469
|
+
await expect(engine.lookup({
|
|
470
|
+
service: 'HelloWorld',
|
|
471
|
+
query: { name: 'Bob' }
|
|
472
|
+
})).rejects.toThrow()
|
|
473
|
+
})
|
|
474
|
+
it('Calls the lookup function from the lookup service', async () => {
|
|
475
|
+
// TODO: Make the default storage engine return something...?
|
|
476
|
+
mockLookupService.lookup = jest.fn(async () => [{ txid: exampleTXID, outputIndex: 0 }])
|
|
477
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
478
|
+
const engine = new Engine(
|
|
479
|
+
{
|
|
480
|
+
Hello: mockTopicManager
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
Hello: mockLookupService
|
|
484
|
+
},
|
|
485
|
+
mockStorageEngine,
|
|
486
|
+
mockChainTracker
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
// Perform a lookup request
|
|
490
|
+
await engine.lookup({
|
|
491
|
+
service: 'Hello',
|
|
492
|
+
query: { name: 'Bob' }
|
|
493
|
+
})
|
|
494
|
+
expect(mockLookupService.lookup).toHaveBeenCalledWith({
|
|
495
|
+
service: 'Hello',
|
|
496
|
+
query: { name: 'Bob' }
|
|
497
|
+
})
|
|
498
|
+
})
|
|
499
|
+
describe('For each returned result', () => {
|
|
500
|
+
it('Finds the identified UTXO by its txid and vout', async () => {
|
|
501
|
+
// TODO: Make the default storage engine return something...?
|
|
502
|
+
mockLookupService.lookup = jest.fn(async () => [{
|
|
503
|
+
txid: 'mockTXID',
|
|
504
|
+
outputIndex: 0,
|
|
505
|
+
history: undefined
|
|
506
|
+
}])
|
|
507
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
508
|
+
const engine = new Engine(
|
|
509
|
+
{
|
|
510
|
+
Hello: mockTopicManager
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
Hello: mockLookupService
|
|
514
|
+
},
|
|
515
|
+
mockStorageEngine,
|
|
516
|
+
mockChainTracker
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
// Perform a lookup request
|
|
520
|
+
await engine.lookup({
|
|
521
|
+
service: 'Hello',
|
|
522
|
+
query: { name: 'Bob' }
|
|
523
|
+
})
|
|
524
|
+
expect(mockStorageEngine.findOutput).toHaveBeenCalledWith(
|
|
525
|
+
"mockTXID", 0, undefined, false
|
|
526
|
+
)
|
|
527
|
+
})
|
|
528
|
+
it('Calls getUTXOHistory with the correct UTXO and history parameters', async () => {
|
|
529
|
+
mockLookupService.lookup = jest.fn(async () => [{
|
|
530
|
+
txid: 'mockTXID',
|
|
531
|
+
outputIndex: 0,
|
|
532
|
+
history: undefined
|
|
533
|
+
}])
|
|
534
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
535
|
+
const engine = new Engine(
|
|
536
|
+
{
|
|
537
|
+
Hello: mockTopicManager
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
Hello: mockLookupService
|
|
541
|
+
},
|
|
542
|
+
mockStorageEngine,
|
|
543
|
+
mockChainTracker
|
|
544
|
+
)
|
|
545
|
+
engine.getUTXOHistory = jest.fn(async () => {
|
|
546
|
+
return mockOutput
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
// Perform a lookup request
|
|
550
|
+
await engine.lookup({
|
|
551
|
+
service: 'Hello',
|
|
552
|
+
query: { name: 'Bob' }
|
|
553
|
+
})
|
|
554
|
+
expect(engine.getUTXOHistory).toHaveBeenCalledWith(
|
|
555
|
+
mockOutput,
|
|
556
|
+
undefined,
|
|
557
|
+
0
|
|
558
|
+
)
|
|
559
|
+
})
|
|
560
|
+
})
|
|
561
|
+
it('Returns the correct set of hydrated results', async () => {
|
|
562
|
+
mockLookupService.lookup = jest.fn(async () => [{
|
|
563
|
+
txid: 'mockTXID',
|
|
564
|
+
outputIndex: 0,
|
|
565
|
+
history: undefined
|
|
566
|
+
}])
|
|
567
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
568
|
+
const engine = new Engine(
|
|
569
|
+
{
|
|
570
|
+
Hello: mockTopicManager
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
Hello: mockLookupService
|
|
574
|
+
},
|
|
575
|
+
mockStorageEngine,
|
|
576
|
+
mockChainTracker
|
|
577
|
+
)
|
|
578
|
+
engine.getUTXOHistory = jest.fn(async () => {
|
|
579
|
+
return mockOutput
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
// Perform a lookup request
|
|
583
|
+
const results = await engine.lookup({
|
|
584
|
+
service: 'Hello',
|
|
585
|
+
query: { name: 'Bob' }
|
|
586
|
+
})
|
|
587
|
+
expect(results).toEqual({
|
|
588
|
+
outputs: [{
|
|
589
|
+
beef: mockOutput.beef,
|
|
590
|
+
outputIndex: mockOutput.outputIndex
|
|
591
|
+
}], type: 'output-list'
|
|
592
|
+
})
|
|
593
|
+
})
|
|
594
|
+
})
|
|
595
|
+
describe('getUTXOHistory', () => {
|
|
596
|
+
it('Returns the given output if there is no history selector', async () => {
|
|
597
|
+
// Already tested above
|
|
598
|
+
return true
|
|
599
|
+
})
|
|
600
|
+
it('Invokes the history selector function with the correct data', async () => {
|
|
601
|
+
const mockedHistorySelector = jest.fn(async (beef, outputIndex, currentDepth) => {
|
|
602
|
+
if (currentDepth !== 2) {
|
|
603
|
+
return true
|
|
604
|
+
}
|
|
605
|
+
return false
|
|
606
|
+
})
|
|
607
|
+
mockLookupService.lookup = jest.fn(async () => [{
|
|
608
|
+
txid: 'mockTXID',
|
|
609
|
+
outputIndex: 0,
|
|
610
|
+
history: mockedHistorySelector
|
|
611
|
+
}])
|
|
612
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
613
|
+
const engine = new Engine(
|
|
614
|
+
{
|
|
615
|
+
Hello: mockTopicManager
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
Hello: mockLookupService
|
|
619
|
+
},
|
|
620
|
+
mockStorageEngine,
|
|
621
|
+
mockChainTracker
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
// Perform a lookup request
|
|
625
|
+
await engine.lookup({
|
|
626
|
+
service: 'Hello',
|
|
627
|
+
query: { name: 'Bob' }
|
|
628
|
+
})
|
|
629
|
+
expect(mockedHistorySelector).toHaveBeenCalledWith(
|
|
630
|
+
mockOutput.beef,
|
|
631
|
+
mockOutput.outputIndex,
|
|
632
|
+
0
|
|
633
|
+
)
|
|
634
|
+
expect(mockedHistorySelector).toReturnWith(Promise.resolve(true))
|
|
635
|
+
})
|
|
636
|
+
it('Returns undefined if history should not be traversed', async () => {
|
|
637
|
+
const mockedHistorySelector = jest.fn(async (beef, outputIndex, currentDepth) => {
|
|
638
|
+
return false
|
|
639
|
+
})
|
|
640
|
+
mockLookupService.lookup = jest.fn(async () => [{
|
|
641
|
+
txid: 'mockTXID',
|
|
642
|
+
outputIndex: 0,
|
|
643
|
+
history: mockedHistorySelector
|
|
644
|
+
}])
|
|
645
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
646
|
+
const engine = new Engine(
|
|
647
|
+
{
|
|
648
|
+
Hello: mockTopicManager
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
Hello: mockLookupService
|
|
652
|
+
},
|
|
653
|
+
mockStorageEngine,
|
|
654
|
+
mockChainTracker
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
// Perform a lookup request
|
|
658
|
+
const results = await engine.lookup({
|
|
659
|
+
service: 'Hello',
|
|
660
|
+
query: { name: 'Bob' }
|
|
661
|
+
})
|
|
662
|
+
expect(mockedHistorySelector).toHaveBeenCalledWith(
|
|
663
|
+
mockOutput.beef,
|
|
664
|
+
mockOutput.outputIndex,
|
|
665
|
+
0
|
|
666
|
+
)
|
|
667
|
+
expect(results).toEqual({
|
|
668
|
+
outputs: [],
|
|
669
|
+
type: "output-list"
|
|
670
|
+
})
|
|
671
|
+
})
|
|
672
|
+
it('Returns undefined if the history selector is a number, and less than the current depth', async () => {
|
|
673
|
+
mockLookupService.lookup = jest.fn(async () => [{
|
|
674
|
+
txid: 'mockTXID',
|
|
675
|
+
outputIndex: 0,
|
|
676
|
+
history: -1
|
|
677
|
+
}])
|
|
678
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
679
|
+
const engine = new Engine(
|
|
680
|
+
{
|
|
681
|
+
Hello: mockTopicManager
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
Hello: mockLookupService
|
|
685
|
+
},
|
|
686
|
+
mockStorageEngine,
|
|
687
|
+
mockChainTracker
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
// Perform a lookup request
|
|
691
|
+
const results = await engine.lookup({
|
|
692
|
+
service: 'Hello',
|
|
693
|
+
query: { name: 'Bob' }
|
|
694
|
+
})
|
|
695
|
+
expect(results).toEqual({
|
|
696
|
+
outputs: [],
|
|
697
|
+
type: "output-list"
|
|
698
|
+
})
|
|
699
|
+
})
|
|
700
|
+
it('Returns the current output even if history should be traversed, if the current output is part of a transaction that does not consume any previous topical UTXOs', async () => {
|
|
701
|
+
mockLookupService.lookup = jest.fn(async () => [{
|
|
702
|
+
txid: 'mockTXID',
|
|
703
|
+
outputIndex: 0,
|
|
704
|
+
history: 1
|
|
705
|
+
}])
|
|
706
|
+
mockStorageEngine.findOutput = jest.fn(async () => mockOutput)
|
|
707
|
+
const engine = new Engine(
|
|
708
|
+
{
|
|
709
|
+
Hello: mockTopicManager
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
Hello: mockLookupService
|
|
713
|
+
},
|
|
714
|
+
mockStorageEngine,
|
|
715
|
+
mockChainTracker
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
// Perform a lookup request
|
|
719
|
+
const results = await engine.lookup({
|
|
720
|
+
service: 'Hello',
|
|
721
|
+
query: { name: 'Bob' }
|
|
722
|
+
})
|
|
723
|
+
|
|
724
|
+
expect(results).toEqual({
|
|
725
|
+
outputs: [{
|
|
726
|
+
beef: mockOutput.beef,
|
|
727
|
+
outputIndex: mockOutput.outputIndex
|
|
728
|
+
}], type: 'output-list'
|
|
729
|
+
})
|
|
730
|
+
})
|
|
731
|
+
// it('Traversing history, calls findOutput with any output consumed by this UTXO', async () => {
|
|
732
|
+
// mockLookupService.lookup = jest.fn(async () => [{
|
|
733
|
+
// txid: 'mockTXID',
|
|
734
|
+
// outputIndex: 0,
|
|
735
|
+
// history: 1
|
|
736
|
+
// }])
|
|
737
|
+
// mockStorageEngine.findOutput = jest.fn(async () => ({
|
|
738
|
+
// ...mockOutput,
|
|
739
|
+
// outputsConsumed: [{
|
|
740
|
+
// txid: examplePreviousTXID,
|
|
741
|
+
// outputIndex: 0
|
|
742
|
+
// }]
|
|
743
|
+
// }))
|
|
744
|
+
// const engine = new Engine(
|
|
745
|
+
// {
|
|
746
|
+
// Hello: mockTopicManager
|
|
747
|
+
// },
|
|
748
|
+
// {
|
|
749
|
+
// Hello: mockLookupService
|
|
750
|
+
// },
|
|
751
|
+
// mockStorageEngine,
|
|
752
|
+
// mockChainTracker
|
|
753
|
+
// )
|
|
754
|
+
|
|
755
|
+
// // Perform a lookup request
|
|
756
|
+
// await engine.lookup({
|
|
757
|
+
// service: 'Hello',
|
|
758
|
+
// query: { name: 'Bob' }
|
|
759
|
+
// })
|
|
760
|
+
|
|
761
|
+
// expect(mockStorageEngine.findOutput).toHaveBeenCalledWith()
|
|
762
|
+
// })
|
|
763
|
+
// it('Returns the correct envelope based on the history traversal process', async () => {
|
|
764
|
+
// // TODO: Come up with some test data for a simple case, but different than the above code.
|
|
765
|
+
// })
|
|
766
|
+
// it('Returns the correct envelope based on the history traversal process for a more complex multi-layer multi-output graph', async () => {
|
|
767
|
+
// // TODO: Come up with some test data to test the history traversal process better
|
|
768
|
+
// })
|
|
769
|
+
})
|
|
770
|
+
// describe('deleteUTXODeep', () => {
|
|
771
|
+
// it('Finds UTXO by ID if no output was provided', async () => {
|
|
772
|
+
|
|
773
|
+
// })
|
|
774
|
+
// it('Finds UTXO by TXID and VOUT if no ID was provided', async () => {
|
|
775
|
+
|
|
776
|
+
// })
|
|
777
|
+
// it('Throws an error if no output can be found', async () => {
|
|
778
|
+
|
|
779
|
+
// })
|
|
780
|
+
// describe('When no more UTXOs are consumed by this UTXO', () => {
|
|
781
|
+
// it('Deletes the UTXO by ID from the storage engine', async () => {
|
|
782
|
+
|
|
783
|
+
// })
|
|
784
|
+
// it('Notifies all lookup services about the deletion of the UTXO', async () => {
|
|
785
|
+
|
|
786
|
+
// })
|
|
787
|
+
// })
|
|
788
|
+
// it('For each UTXO that this UTXO consumes, finds the consumed UTXO by its ID and removes reference to this one', async () => {
|
|
789
|
+
|
|
790
|
+
// })
|
|
791
|
+
})
|
|
792
|
+
})
|