@bsv/wallet-toolbox 1.3.17 → 1.3.19
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/out/src/storage/StorageReaderWriter.js +2 -2
- package/out/src/storage/StorageReaderWriter.js.map +1 -1
- package/out/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.js +82 -51
- package/out/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.js.map +1 -1
- package/out/src/storage/methods/generateChange.d.ts.map +1 -1
- package/out/src/storage/methods/generateChange.js +12 -0
- package/out/src/storage/methods/generateChange.js.map +1 -1
- package/out/test/Wallet/support/operations.man.test.js +55 -3
- package/out/test/Wallet/support/operations.man.test.js.map +1 -1
- package/out/tsconfig.all.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/storage/StorageReaderWriter.ts +2 -2
- package/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts +83 -51
- package/src/storage/methods/generateChange.ts +11 -0
- package/test/Wallet/support/operations.man.test.ts +65 -2
package/package.json
CHANGED
|
@@ -168,8 +168,8 @@ export abstract class StorageReaderWriter extends StorageReader {
|
|
|
168
168
|
basketId: 0,
|
|
169
169
|
userId: user.userId,
|
|
170
170
|
name: 'default',
|
|
171
|
-
numberOfDesiredUTXOs:
|
|
172
|
-
minimumDesiredUTXOValue:
|
|
171
|
+
numberOfDesiredUTXOs: 144,
|
|
172
|
+
minimumDesiredUTXOValue: 32,
|
|
173
173
|
isDeleted: false
|
|
174
174
|
})
|
|
175
175
|
break
|
|
@@ -174,7 +174,7 @@ describe('generateChange tests', () => {
|
|
|
174
174
|
|
|
175
175
|
const r = await generateChangeSdk(params, allocateChangeInput, releaseChangeInput)
|
|
176
176
|
expect(JSON.stringify(r)).toBe(
|
|
177
|
-
'{"allocatedChangeInputs":[{"satoshis":1004,"outputId":15011,"spendable":false},{"satoshis":1000,"outputId":15017,"spendable":false}
|
|
177
|
+
'{"allocatedChangeInputs":[{"satoshis":1004,"outputId":15011,"spendable":false},{"satoshis":1000,"outputId":15017,"spendable":false}],"changeOutputs":[{"satoshis":689,"lockingScriptLength":25}],"size":39476,"fee":79,"satsPerKb":2}'
|
|
178
178
|
)
|
|
179
179
|
expectTransactionSize(params, r)
|
|
180
180
|
})
|
|
@@ -195,7 +195,7 @@ describe('generateChange tests', () => {
|
|
|
195
195
|
|
|
196
196
|
const r = await generateChangeSdk(params, allocateChangeInput, releaseChangeInput)
|
|
197
197
|
expect(JSON.stringify(r)).toBe(
|
|
198
|
-
'{"allocatedChangeInputs":[{"satoshis":1004,"outputId":15011,"spendable":false},{"satoshis":1000,"outputId":15017,"spendable":false}
|
|
198
|
+
'{"allocatedChangeInputs":[{"satoshis":1004,"outputId":15011,"spendable":false},{"satoshis":1000,"outputId":15017,"spendable":false}],"changeOutputs":[{"satoshis":570,"lockingScriptLength":25}],"size":39476,"fee":198,"satsPerKb":5}'
|
|
199
199
|
)
|
|
200
200
|
expectTransactionSize(params, r)
|
|
201
201
|
})
|
|
@@ -216,7 +216,7 @@ describe('generateChange tests', () => {
|
|
|
216
216
|
|
|
217
217
|
const r = await generateChangeSdk(params, allocateChangeInput, releaseChangeInput)
|
|
218
218
|
expect(JSON.stringify(r)).toBe(
|
|
219
|
-
'{"allocatedChangeInputs":[{"satoshis":1004,"outputId":15011,"spendable":false},{"satoshis":1000,"outputId":15017,"spendable":false}
|
|
219
|
+
'{"allocatedChangeInputs":[{"satoshis":1004,"outputId":15011,"spendable":false},{"satoshis":1000,"outputId":15017,"spendable":false}],"changeOutputs":[{"satoshis":728,"lockingScriptLength":25}],"size":39476,"fee":40,"satsPerKb":1}'
|
|
220
220
|
)
|
|
221
221
|
expectTransactionSize(params, r)
|
|
222
222
|
})
|
|
@@ -887,55 +887,87 @@ describe('generateChange tests', () => {
|
|
|
887
887
|
{ satoshis: 1034, outputId: 18492 }
|
|
888
888
|
]
|
|
889
889
|
}
|
|
890
|
+
const d14: TestCase = {
|
|
891
|
+
p: {
|
|
892
|
+
fixedInputs: [],
|
|
893
|
+
fixedOutputs: [{ satoshis: 3, lockingScriptLength: 141 }],
|
|
894
|
+
feeModel: { model: 'sat/kb', value: 2 },
|
|
895
|
+
changeInitialSatoshis: 1000,
|
|
896
|
+
changeFirstSatoshis: 285,
|
|
897
|
+
changeLockingScriptLength: 25,
|
|
898
|
+
changeUnlockingScriptLength: 107,
|
|
899
|
+
targetNetCount: 22
|
|
900
|
+
},
|
|
901
|
+
availableChange: [
|
|
902
|
+
{ satoshis: 995, outputId: 16190 },
|
|
903
|
+
{ satoshis: 1000, outputId: 18487 },
|
|
904
|
+
{ satoshis: 1000, outputId: 18488 },
|
|
905
|
+
{ satoshis: 1000, outputId: 18489 },
|
|
906
|
+
{ satoshis: 1000, outputId: 18490 },
|
|
907
|
+
{ satoshis: 1000, outputId: 18491 },
|
|
908
|
+
{ satoshis: 1000, outputId: 18492 },
|
|
909
|
+
{ satoshis: 1000, outputId: 18493 },
|
|
910
|
+
{ satoshis: 1000, outputId: 18494 },
|
|
911
|
+
{ satoshis: 1000, outputId: 18495 },
|
|
912
|
+
{ satoshis: 100, outputId: 18496 }
|
|
913
|
+
]
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const test8Cases = [
|
|
917
|
+
{
|
|
918
|
+
n: 14,
|
|
919
|
+
d: d14,
|
|
920
|
+
er: '{"allocatedChangeInputs":[{"satoshis":1000,"outputId":18495,"spendable":false}],"changeOutputs":[{"satoshis":996,"lockingScriptLength":25}],"size":342,"fee":1,"satsPerKb":2}'
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
n: 13,
|
|
924
|
+
d: d13,
|
|
925
|
+
er: '{"allocatedChangeInputs":[{"satoshis":1000,"outputId":18489,"spendable":false}],"changeOutputs":[{"satoshis":497,"lockingScriptLength":25}],"size":429,"fee":1,"satsPerKb":2}'
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
n: 12,
|
|
929
|
+
d: d12,
|
|
930
|
+
er: '{"allocatedChangeInputs":[{"satoshis":14030,"outputId":21194,"spendable":false}],"changeOutputs":[{"satoshis":13800,"lockingScriptLength":25}],"size":260,"fee":29,"satsPerKb":110}'
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
n: 11,
|
|
934
|
+
d: d11,
|
|
935
|
+
er: '{"allocatedChangeInputs":[{"satoshis":285,"outputId":21319,"spendable":false},{"satoshis":1000,"outputId":21309,"spendable":false}],"changeOutputs":[{"satoshis":1039,"lockingScriptLength":25}],"size":408,"fee":45,"satsPerKb":110}'
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
n: 10,
|
|
939
|
+
d: d10,
|
|
940
|
+
er: '{"allocatedChangeInputs":[{"satoshis":14030,"outputId":21194,"spendable":false}],"changeOutputs":[{"satoshis":13800,"lockingScriptLength":25}],"size":260,"fee":29,"satsPerKb":110}'
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
n: 9,
|
|
944
|
+
d: d9,
|
|
945
|
+
er: '{"allocatedChangeInputs":[{"satoshis":4571,"outputId":15626,"spendable":false}],"changeOutputs":[{"satoshis":3368,"lockingScriptLength":25}],"size":1373,"fee":3,"satsPerKb":2}'
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
n: 8,
|
|
949
|
+
d: d8,
|
|
950
|
+
er: '{"allocatedChangeInputs":[{"satoshis":1817,"outputId":15658,"spendable":false}],"changeOutputs":[{"satoshis":52,"lockingScriptLength":25}],"size":260,"fee":1,"satsPerKb":2}'
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
n: 7,
|
|
954
|
+
d: d7,
|
|
955
|
+
er: '{"allocatedChangeInputs":[{"satoshis":1817,"outputId":15658,"spendable":false}],"changeOutputs":[{"satoshis":46,"lockingScriptLength":25}],"size":260,"fee":1,"satsPerKb":2}'
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
n: 6,
|
|
959
|
+
d: d6,
|
|
960
|
+
er: '{"allocatedChangeInputs":[{"satoshis":1000,"outputId":16346,"spendable":false}],"changeOutputs":[{"satoshis":298,"lockingScriptLength":25}],"size":673,"fee":2,"satsPerKb":2}'
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
n: 5,
|
|
964
|
+
d: d5,
|
|
965
|
+
er: '{"allocatedChangeInputs":[],"changeOutputs":[{"satoshis":799,"lockingScriptLength":25}],"size":191,"fee":1,"satsPerKb":2}'
|
|
966
|
+
}
|
|
967
|
+
]
|
|
890
968
|
|
|
891
|
-
test('8 paramsText d5 d6 d7 d8 d9 d10 d11 d12 d13', async () => {
|
|
892
|
-
for (const { n, d, er } of
|
|
893
|
-
{
|
|
894
|
-
n: 13,
|
|
895
|
-
d: d13,
|
|
896
|
-
er: '{"allocatedChangeInputs":[{"satoshis":1000,"outputId":18489,"spendable":false}],"changeOutputs":[{"satoshis":497,"lockingScriptLength":25}],"size":429,"fee":1,"satsPerKb":2}'
|
|
897
|
-
},
|
|
898
|
-
{
|
|
899
|
-
n: 12,
|
|
900
|
-
d: d12,
|
|
901
|
-
er: '{"allocatedChangeInputs":[{"satoshis":14030,"outputId":21194,"spendable":false}],"changeOutputs":[{"satoshis":13800,"lockingScriptLength":25}],"size":260,"fee":29,"satsPerKb":110}'
|
|
902
|
-
},
|
|
903
|
-
{
|
|
904
|
-
n: 11,
|
|
905
|
-
d: d11,
|
|
906
|
-
er: '{"allocatedChangeInputs":[{"satoshis":285,"outputId":21319,"spendable":false},{"satoshis":1000,"outputId":21309,"spendable":false}],"changeOutputs":[{"satoshis":1039,"lockingScriptLength":25}],"size":408,"fee":45,"satsPerKb":110}'
|
|
907
|
-
},
|
|
908
|
-
{
|
|
909
|
-
n: 10,
|
|
910
|
-
d: d10,
|
|
911
|
-
er: '{"allocatedChangeInputs":[{"satoshis":14030,"outputId":21194,"spendable":false}],"changeOutputs":[{"satoshis":13800,"lockingScriptLength":25}],"size":260,"fee":29,"satsPerKb":110}'
|
|
912
|
-
},
|
|
913
|
-
{
|
|
914
|
-
n: 9,
|
|
915
|
-
d: d9,
|
|
916
|
-
er: '{"allocatedChangeInputs":[{"satoshis":4571,"outputId":15626,"spendable":false}],"changeOutputs":[{"satoshis":3368,"lockingScriptLength":25}],"size":1373,"fee":3,"satsPerKb":2}'
|
|
917
|
-
},
|
|
918
|
-
{
|
|
919
|
-
n: 8,
|
|
920
|
-
d: d8,
|
|
921
|
-
er: '{"allocatedChangeInputs":[{"satoshis":1817,"outputId":15658,"spendable":false}],"changeOutputs":[{"satoshis":52,"lockingScriptLength":25}],"size":260,"fee":1,"satsPerKb":2}'
|
|
922
|
-
},
|
|
923
|
-
{
|
|
924
|
-
n: 7,
|
|
925
|
-
d: d7,
|
|
926
|
-
er: '{"allocatedChangeInputs":[{"satoshis":1817,"outputId":15658,"spendable":false}],"changeOutputs":[{"satoshis":46,"lockingScriptLength":25}],"size":260,"fee":1,"satsPerKb":2}'
|
|
927
|
-
},
|
|
928
|
-
{
|
|
929
|
-
n: 6,
|
|
930
|
-
d: d6,
|
|
931
|
-
er: '{"allocatedChangeInputs":[{"satoshis":1000,"outputId":16346,"spendable":false}],"changeOutputs":[{"satoshis":298,"lockingScriptLength":25}],"size":673,"fee":2,"satsPerKb":2}'
|
|
932
|
-
},
|
|
933
|
-
{
|
|
934
|
-
n: 5,
|
|
935
|
-
d: d5,
|
|
936
|
-
er: '{"allocatedChangeInputs":[],"changeOutputs":[{"satoshis":799,"lockingScriptLength":25}],"size":191,"fee":1,"satsPerKb":2}'
|
|
937
|
-
}
|
|
938
|
-
]) {
|
|
969
|
+
test('8 paramsText d5 d6 d7 d8 d9 d10 d11 d12 d13 d14', async () => {
|
|
970
|
+
for (const { n, d, er } of test8Cases) {
|
|
939
971
|
const params: GenerateChangeSdkParams = { ...d.p }
|
|
940
972
|
const availableChange: GenerateChangeSdkChangeInput[] = [...d.availableChange]
|
|
941
973
|
|
|
@@ -236,6 +236,17 @@ export async function generateChangeSdk(
|
|
|
236
236
|
if (feeExcess() < 0)
|
|
237
237
|
// Not enough available funding even if no change outputs
|
|
238
238
|
break
|
|
239
|
+
// At this point we have a funded transaction, but there may be change outputs that are each costing as change input,
|
|
240
|
+
// resulting in pointless churn of change outputs.
|
|
241
|
+
// And remove change inputs that funded only a single change output (along with that output)...
|
|
242
|
+
const changeInputs = [...r.allocatedChangeInputs]
|
|
243
|
+
while (changeInputs.length > 1 && r.changeOutputs.length > 1) {
|
|
244
|
+
const lastOutput = r.changeOutputs.slice(-1)[0]
|
|
245
|
+
const i = changeInputs.findIndex(ci => ci.satoshis <= lastOutput.satoshis)
|
|
246
|
+
if (i < 0) break
|
|
247
|
+
r.changeOutputs.pop()
|
|
248
|
+
changeInputs.splice(i, 1)
|
|
249
|
+
}
|
|
239
250
|
// and try again...
|
|
240
251
|
}
|
|
241
252
|
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import { WalletOutput } from '@bsv/sdk'
|
|
2
|
-
import {
|
|
1
|
+
import { Transaction, WalletOutput } from '@bsv/sdk'
|
|
2
|
+
import {
|
|
3
|
+
sdk,
|
|
4
|
+
Services,
|
|
5
|
+
Setup,
|
|
6
|
+
StorageKnex,
|
|
7
|
+
TableOutput,
|
|
8
|
+
TableTransaction,
|
|
9
|
+
TableUser,
|
|
10
|
+
verifyOne,
|
|
11
|
+
verifyOneOrNone
|
|
12
|
+
} from '../../../src'
|
|
3
13
|
import { _tu, TuEnv } from '../../utils/TestUtilsWalletStorage'
|
|
4
14
|
import { specOpInvalidChange, ValidListOutputsArgs } from '../../../src/sdk'
|
|
5
15
|
import { LocalWalletTestOptions } from '../../utils/localWalletMethods'
|
|
@@ -194,6 +204,29 @@ describe('operations.man tests', () => {
|
|
|
194
204
|
*/
|
|
195
205
|
await storage.destroy()
|
|
196
206
|
})
|
|
207
|
+
test('9 review recent transaction change use', async () => {
|
|
208
|
+
const { env, storage, services } = await createMainReviewSetup()
|
|
209
|
+
const countTxs = await storage.countTransactions({
|
|
210
|
+
partial: { userId: 311 },
|
|
211
|
+
status: ['completed', 'unproven', 'failed']
|
|
212
|
+
})
|
|
213
|
+
const txs = await storage.findTransactions({
|
|
214
|
+
partial: { userId: 311 },
|
|
215
|
+
status: ['unproven', 'completed', 'failed'],
|
|
216
|
+
paged: { limit: 100, offset: Math.max(0, countTxs - 100) }
|
|
217
|
+
})
|
|
218
|
+
for (const tx of txs) {
|
|
219
|
+
const ls = await toLogString(tx, storage)
|
|
220
|
+
console.log(ls)
|
|
221
|
+
}
|
|
222
|
+
const countReqs = await storage.countProvenTxReqs({ partial: {}, status: ['completed', 'unmined'] })
|
|
223
|
+
const reqs = await storage.findProvenTxReqs({
|
|
224
|
+
partial: {},
|
|
225
|
+
status: ['unmined', 'completed'],
|
|
226
|
+
paged: { limit: 100, offset: countReqs - 100 }
|
|
227
|
+
})
|
|
228
|
+
await storage.destroy()
|
|
229
|
+
})
|
|
197
230
|
})
|
|
198
231
|
|
|
199
232
|
async function createMainReviewSetup(): Promise<{
|
|
@@ -217,3 +250,33 @@ async function createMainReviewSetup(): Promise<{
|
|
|
217
250
|
await storage.makeAvailable()
|
|
218
251
|
return { env, storage, services }
|
|
219
252
|
}
|
|
253
|
+
|
|
254
|
+
async function toLogString(tx: TableTransaction, storage: StorageKnex): Promise<string> {
|
|
255
|
+
const rawTx = await storage.getRawTxOfKnownValidTransaction(tx.txid)
|
|
256
|
+
const btx = Transaction.fromBinary(rawTx!)
|
|
257
|
+
let log = `tx ${tx.txid} ${tx.status} s:${tx.satoshis} uid:${tx.userId} tid:${tx.transactionId}\n`
|
|
258
|
+
for (let i = 0; i < Math.max(btx.inputs.length, btx.outputs.length); i++) {
|
|
259
|
+
let ilog: string = ''
|
|
260
|
+
let olog: string = ''
|
|
261
|
+
if (i < btx.inputs.length) {
|
|
262
|
+
const input = btx.inputs[i]
|
|
263
|
+
ilog = `${logTxid(input.sourceTXID!)}.${input.sourceOutputIndex}`
|
|
264
|
+
}
|
|
265
|
+
if (i < btx.outputs.length) {
|
|
266
|
+
const output = btx.outputs[i]
|
|
267
|
+
olog = `${ar('' + output.satoshis, 9)} ${output.lockingScript.toHex().length / 2}`
|
|
268
|
+
}
|
|
269
|
+
log += `${ar('' + i, 5)} ${al(ilog, 20)} ${olog}\n`
|
|
270
|
+
}
|
|
271
|
+
return log
|
|
272
|
+
|
|
273
|
+
function logTxid(txid: string): string {
|
|
274
|
+
return `${txid.slice(0, 6)}..${txid.slice(-6)}`
|
|
275
|
+
}
|
|
276
|
+
function al(v: string | number, w: number): string {
|
|
277
|
+
return v.toString().padEnd(w)
|
|
278
|
+
}
|
|
279
|
+
function ar(v: string | number, w: number): string {
|
|
280
|
+
return v.toString().padStart(w)
|
|
281
|
+
}
|
|
282
|
+
}
|