@bsv/wallet-toolbox 1.2.22 → 1.2.24
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/docs/client.md +3 -8
- package/docs/wallet.md +3 -8
- package/out/src/Wallet.d.ts +7 -8
- package/out/src/Wallet.d.ts.map +1 -1
- package/out/src/Wallet.js +13 -14
- package/out/src/Wallet.js.map +1 -1
- package/out/src/services/__tests/postBeef.test.js +1 -2
- package/out/src/services/__tests/postBeef.test.js.map +1 -1
- package/out/src/storage/methods/attemptToPostReqsToNetwork.d.ts.map +1 -1
- package/out/src/storage/methods/attemptToPostReqsToNetwork.js +26 -14
- package/out/src/storage/methods/attemptToPostReqsToNetwork.js.map +1 -1
- package/out/test/Wallet/local/localWallet2.man.test.js +8 -2
- package/out/test/Wallet/local/localWallet2.man.test.js.map +1 -1
- package/out/test/Wallet/signAction/mountaintop.man.test.d.ts +2 -0
- package/out/test/Wallet/signAction/mountaintop.man.test.d.ts.map +1 -0
- package/out/test/Wallet/signAction/mountaintop.man.test.js +109 -0
- package/out/test/Wallet/signAction/mountaintop.man.test.js.map +1 -0
- package/out/test/Wallet/specOps/specOps.man.test.js +1 -1
- package/out/test/Wallet/specOps/specOps.man.test.js.map +1 -1
- package/out/tsconfig.all.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Wallet.ts +14 -15
- package/src/services/__tests/postBeef.test.ts +1 -1
- package/src/storage/methods/attemptToPostReqsToNetwork.ts +24 -13
- package/test/Wallet/local/localWallet2.man.test.ts +9 -2
- package/test/Wallet/signAction/mountaintop.man.test.ts +129 -0
- package/test/Wallet/specOps/specOps.man.test.ts +1 -1
package/package.json
CHANGED
package/src/Wallet.ts
CHANGED
|
@@ -848,21 +848,6 @@ export class Wallet implements WalletInterface, ProtoWallet {
|
|
|
848
848
|
})
|
|
849
849
|
}
|
|
850
850
|
|
|
851
|
-
/**
|
|
852
|
-
* Uses `listOutputs` special operation to compute the total value (of satoshis) for
|
|
853
|
-
* all spendable outputs in a basket (which defaults to the change 'default' basket).
|
|
854
|
-
*
|
|
855
|
-
* @param {string} basket - Optional. Defaults to 'default', the wallet change basket.
|
|
856
|
-
* @returns {number} sum of output satoshis
|
|
857
|
-
*/
|
|
858
|
-
async balance(basket: string = 'default'): Promise<number> {
|
|
859
|
-
const args: ListOutputsArgs = {
|
|
860
|
-
basket: specOpWalletBalance
|
|
861
|
-
}
|
|
862
|
-
const r = await this.listOutputs(args)
|
|
863
|
-
return r.totalOutputs
|
|
864
|
-
}
|
|
865
|
-
|
|
866
851
|
/**
|
|
867
852
|
* Uses `listOutputs` to iterate over chunks of up to 1000 outputs to
|
|
868
853
|
* compute the sum of output satoshis.
|
|
@@ -889,6 +874,20 @@ export class Wallet implements WalletInterface, ProtoWallet {
|
|
|
889
874
|
return r
|
|
890
875
|
}
|
|
891
876
|
|
|
877
|
+
/**
|
|
878
|
+
* Uses `listOutputs` special operation to compute the total value (of satoshis) for
|
|
879
|
+
* all spendable outputs in the 'default' basket.
|
|
880
|
+
*
|
|
881
|
+
* @returns {number} sum of output satoshis
|
|
882
|
+
*/
|
|
883
|
+
async balance(): Promise<number> {
|
|
884
|
+
const args: ListOutputsArgs = {
|
|
885
|
+
basket: specOpWalletBalance
|
|
886
|
+
}
|
|
887
|
+
const r = await this.listOutputs(args)
|
|
888
|
+
return r.totalOutputs
|
|
889
|
+
}
|
|
890
|
+
|
|
892
891
|
/**
|
|
893
892
|
* Uses `listOutputs` special operation to review the spendability via `Services` of
|
|
894
893
|
* outputs currently considered spendable. Returns the outputs that fail to verify.
|
|
@@ -79,7 +79,7 @@ async function postBeefTest(services: Services) {
|
|
|
79
79
|
} else {
|
|
80
80
|
expect(tr!.status).toBe('error')
|
|
81
81
|
expect(tr!.doubleSpend).toBe(true)
|
|
82
|
-
if (tr!.competingTxs
|
|
82
|
+
if (tr!.competingTxs !== undefined) expect(tr!.competingTxs).toEqual([c.txidUndo])
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -50,20 +50,31 @@ async function validateReqsAndMergeBeefs(
|
|
|
50
50
|
const vreqs: PostReqsToNetworkDetails[] = []
|
|
51
51
|
|
|
52
52
|
for (const req of reqs) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
try {
|
|
54
|
+
const noRawTx = !req.rawTx
|
|
55
|
+
const noTxIds = !req.notify.transactionIds || req.notify.transactionIds.length < 1
|
|
56
|
+
const noInputBEEF = !req.inputBEEF
|
|
57
|
+
if (noRawTx || noTxIds || noInputBEEF) {
|
|
58
|
+
// This should have happened earlier...
|
|
59
|
+
req.addHistoryNote({ when: new Date().toISOString(), what: 'validateReqFailed', noRawTx, noTxIds, noInputBEEF })
|
|
60
|
+
req.status = 'invalid'
|
|
61
|
+
await req.updateStorageDynamicProperties(storage, trx)
|
|
62
|
+
r.details.push({ txid: req.txid, req, status: 'invalid' })
|
|
63
|
+
} else {
|
|
64
|
+
const vreq: PostReqsToNetworkDetails = { txid: req.txid, req, status: 'unknown' }
|
|
65
|
+
await storage.mergeReqToBeefToShareExternally(req.api, r.beef, [], trx)
|
|
66
|
+
vreqs.push(vreq)
|
|
67
|
+
r.details.push(vreq)
|
|
68
|
+
}
|
|
69
|
+
} catch (eu: unknown) {
|
|
70
|
+
const { code, message } = sdk.WalletError.fromUnknown(eu)
|
|
71
|
+
req.addHistoryNote({ when: new Date().toISOString(), what: 'validateReqError', txid: req.txid, code, message })
|
|
72
|
+
req.attempts++
|
|
73
|
+
if (req.attempts > 6) {
|
|
74
|
+
req.status = 'invalid'
|
|
75
|
+
r.details.push({ txid: req.txid, req, status: 'invalid' })
|
|
76
|
+
}
|
|
60
77
|
await req.updateStorageDynamicProperties(storage, trx)
|
|
61
|
-
r.details.push({ txid: req.txid, req, status: 'invalid' })
|
|
62
|
-
} else {
|
|
63
|
-
const vreq: PostReqsToNetworkDetails = { txid: req.txid, req, status: 'unknown' }
|
|
64
|
-
vreqs.push(vreq)
|
|
65
|
-
r.details.push(vreq)
|
|
66
|
-
await storage.mergeReqToBeefToShareExternally(req.api, r.beef, [], trx)
|
|
67
78
|
}
|
|
68
79
|
}
|
|
69
80
|
return { r, vreqs, txids: vreqs.map(r => r.txid) }
|
|
@@ -11,12 +11,12 @@ import {
|
|
|
11
11
|
} from './localWalletMethods'
|
|
12
12
|
import { abort } from 'process'
|
|
13
13
|
|
|
14
|
-
const chain: sdk.Chain = '
|
|
14
|
+
const chain: sdk.Chain = 'main'
|
|
15
15
|
|
|
16
16
|
const options: LocalWalletTestOptions = {
|
|
17
17
|
setActiveClient: true,
|
|
18
18
|
useMySQLConnectionForClient: true,
|
|
19
|
-
useTestIdentityKey:
|
|
19
|
+
useTestIdentityKey: false,
|
|
20
20
|
useIdentityKey2: false
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -41,6 +41,13 @@ describe('localWallet2 tests', () => {
|
|
|
41
41
|
await setup.wallet.destroy()
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
+
test('2 create 1 sat delayed', async () => {
|
|
45
|
+
const setup = await createSetup(chain, options)
|
|
46
|
+
const car = await createOneSatTestOutput(setup, {}, 1)
|
|
47
|
+
//await trackReqByTxid(setup, car.txid!)
|
|
48
|
+
await setup.wallet.destroy()
|
|
49
|
+
})
|
|
50
|
+
|
|
44
51
|
test('2a create 1 sat immediate', async () => {
|
|
45
52
|
const setup = await createSetup(chain, options)
|
|
46
53
|
const car = await createOneSatTestOutput(setup, { acceptDelayedBroadcast: false }, 1)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Beef,
|
|
3
|
+
BigNumber,
|
|
4
|
+
Hash,
|
|
5
|
+
P2PKH,
|
|
6
|
+
PublicKey,
|
|
7
|
+
Signature,
|
|
8
|
+
Transaction,
|
|
9
|
+
TransactionSignature,
|
|
10
|
+
UnlockingScript,
|
|
11
|
+
Utils
|
|
12
|
+
} from '@bsv/sdk'
|
|
13
|
+
import { Setup, verifyInteger, verifyTruthy, Wallet } from '../../../src'
|
|
14
|
+
import { parseWalletOutpoint } from '../../../src/sdk'
|
|
15
|
+
|
|
16
|
+
describe('mountaintop tests', () => {
|
|
17
|
+
jest.setTimeout(99999999)
|
|
18
|
+
|
|
19
|
+
test('0 signature validity', async () => {
|
|
20
|
+
// obtain the secrets environment for the testnet network.
|
|
21
|
+
const env = Setup.getEnv('main')
|
|
22
|
+
const setup = await Setup.createWalletClient({ env })
|
|
23
|
+
|
|
24
|
+
const privKey = await setup.wallet.keyDeriver.derivePrivateKey([1, 'mountaintops'], '1', 'anyone')
|
|
25
|
+
const a2 = privKey.toAddress()
|
|
26
|
+
|
|
27
|
+
const { publicKey } = await setup.wallet.getPublicKey({
|
|
28
|
+
protocolID: [1, 'mountaintops'],
|
|
29
|
+
keyID: '1',
|
|
30
|
+
counterparty: 'anyone',
|
|
31
|
+
forSelf: true
|
|
32
|
+
})
|
|
33
|
+
const address = PublicKey.fromString(publicKey).toAddress()
|
|
34
|
+
expect(address).toBe('1BSMQ1PxMbzMqjB47EYaSNBAD7Qme1dXuk')
|
|
35
|
+
expect(a2).toBe(address)
|
|
36
|
+
|
|
37
|
+
const msg = [
|
|
38
|
+
111, 16, 29, 104, 166, 199, 108, 36, 242, 153, 242, 104, 10, 198, 151, 176, 170, 69, 98, 209, 88, 105, 113, 199,
|
|
39
|
+
124, 56, 189, 3, 104, 214, 94, 76
|
|
40
|
+
]
|
|
41
|
+
const msgHash = new BigNumber(msg, 16)
|
|
42
|
+
const h1 = msgHash.toHex()
|
|
43
|
+
const msgHash2 = new BigNumber(msg)
|
|
44
|
+
const h2 = msgHash2.toHex()
|
|
45
|
+
|
|
46
|
+
const beef = Beef.fromString(beef0, 'hex')
|
|
47
|
+
const o = parseWalletOutpoint(outpoints[0])
|
|
48
|
+
const tx = new Transaction()
|
|
49
|
+
tx.addInput({
|
|
50
|
+
sourceOutputIndex: o.vout,
|
|
51
|
+
sourceTXID: o.txid,
|
|
52
|
+
sourceTransaction: beef.findAtomicTransaction(o.txid)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const p2pkh = new P2PKH().unlock(privKey, 'all')
|
|
56
|
+
tx.inputs[0].unlockingScript = await p2pkh.sign(tx, 0)
|
|
57
|
+
const ok2 = await tx.verify('scripts only')
|
|
58
|
+
expect(ok2).toBe(true)
|
|
59
|
+
|
|
60
|
+
tx.inputs[0].unlockingScript = await sign(setup.wallet, tx, 0)
|
|
61
|
+
const ok = await tx.verify('scripts only')
|
|
62
|
+
expect(ok).toBe(true)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const outpoints = [
|
|
67
|
+
'30c83f8b84864fd4497414980faadf074192e7193602ef96ca18705876ce74b1.0',
|
|
68
|
+
'797bd197f2328931231f2834c5dc8036fe9990981c368df27d3b55fa926863be.0',
|
|
69
|
+
'08d47d844e81d751691f6b4a39ce378e9d0f70a3a0606c87995f0f28399552e2.0'
|
|
70
|
+
]
|
|
71
|
+
const beef0 =
|
|
72
|
+
'0200beef01fe068d0d0008027a001b501758910c83d8e2c839cfc133245510f5ddbbd28202c331bb9feccc261c287b02b174ce76587018ca96ef023619e7924107dfaa0f98147449d44f86848b3fc830013c006174b69497f770d46604b177a98ff8b8a693a5cec19cd145b3b32abab71676f8011f001f86947779e8e749fd439f037d93733c2ea0734a17cdf1c32f87278b80c7ff72010e00161e280d8481978b9d2696c58d634beda36265ceef9faaa351566afc2c8ab2f0010600e250ce168ac74d432a14df5669f337cd44a8c2cfc8709b955174dd57e2354399010200fd976461d8c0ed097e32ae79afefb35e89daa7289daf7b01bde8bb1481762f590100005fb474d7ddaf5a299509a165cabfe0b6dbea56ed56d1f0d3acf1d3d89531a21e01010029f89d48414f66b9bfd8d711f51d6db0a712cfff2d641f66f83dd2e5e452e5c601010001000000014289ced528197deb6980d634200d333d9983c45ef46893affd027914fdc02cf7000000006a473044022019e70e4325f95b3d5f9f0569123b23c6bff7ef4197fcb15fa50ac3537b8546de0220100d181429245e0349903a0784ad61bfa022d864fca8ce6ba13b0de99fd39eb641210327c7cb8afcd1adce5b26055d70cad9fb1045976a6f99b2ee61ed36295d5802a7ffffffff020a000000000000001976a914727caee3e1178da2ca0b48786171f23695a4ccd088ac1d000000000000001976a9148419faaf7a5e97dcc62002e2415cb51bdb91937e88ac00000000'
|
|
73
|
+
|
|
74
|
+
async function sign(client: Wallet, tx: Transaction, inputIndex: number): Promise<UnlockingScript> {
|
|
75
|
+
let signatureScope = TransactionSignature.SIGHASH_FORKID
|
|
76
|
+
signatureScope |= TransactionSignature.SIGHASH_ALL
|
|
77
|
+
const input = tx.inputs[inputIndex]
|
|
78
|
+
const otherInputs = tx.inputs.filter((_, index) => index !== inputIndex)
|
|
79
|
+
const sourceTXID = input.sourceTXID ?? input.sourceTransaction?.id('hex')
|
|
80
|
+
if (sourceTXID == null || sourceTXID === undefined) {
|
|
81
|
+
throw new Error('The input sourceTXID or sourceTransaction is required for transaction signing.')
|
|
82
|
+
}
|
|
83
|
+
if (sourceTXID === '') {
|
|
84
|
+
throw new Error('The input sourceTXID or sourceTransaction is required for transaction signing.')
|
|
85
|
+
}
|
|
86
|
+
const sourceSatoshis = input.sourceTransaction?.outputs[input.sourceOutputIndex].satoshis
|
|
87
|
+
if (sourceSatoshis == null || sourceSatoshis === undefined) {
|
|
88
|
+
throw new Error('The sourceSatoshis or input sourceTransaction is required for transaction signing.')
|
|
89
|
+
}
|
|
90
|
+
const lockingScript = input.sourceTransaction?.outputs[input.sourceOutputIndex].lockingScript
|
|
91
|
+
if (lockingScript == null) {
|
|
92
|
+
throw new Error('The lockingScript or input sourceTransaction is required for transaction signing.')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const preimage = TransactionSignature.format({
|
|
96
|
+
sourceTXID,
|
|
97
|
+
sourceOutputIndex: verifyInteger(input.sourceOutputIndex),
|
|
98
|
+
sourceSatoshis,
|
|
99
|
+
transactionVersion: tx.version,
|
|
100
|
+
otherInputs,
|
|
101
|
+
inputIndex,
|
|
102
|
+
outputs: tx.outputs,
|
|
103
|
+
inputSequence: verifyTruthy(input.sequence),
|
|
104
|
+
subscript: lockingScript,
|
|
105
|
+
lockTime: tx.lockTime,
|
|
106
|
+
scope: signatureScope
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const { signature } = await client.createSignature({
|
|
110
|
+
hashToDirectlySign: Hash.sha256(Hash.sha256(preimage)),
|
|
111
|
+
protocolID: [1, 'mountaintops'],
|
|
112
|
+
keyID: '1',
|
|
113
|
+
counterparty: 'anyone'
|
|
114
|
+
})
|
|
115
|
+
const rawSignature = Signature.fromDER(signature)
|
|
116
|
+
const sig = new TransactionSignature(rawSignature.r, rawSignature.s, signatureScope)
|
|
117
|
+
const sigForScript = sig.toChecksigFormat()
|
|
118
|
+
const { publicKey } = await client.getPublicKey({
|
|
119
|
+
protocolID: [1, 'mountaintops'],
|
|
120
|
+
keyID: '1',
|
|
121
|
+
counterparty: 'anyone',
|
|
122
|
+
forSelf: true
|
|
123
|
+
})
|
|
124
|
+
const pubkeyForScript = Utils.toArray(publicKey, 'hex')
|
|
125
|
+
return new UnlockingScript([
|
|
126
|
+
{ op: sigForScript.length, data: sigForScript },
|
|
127
|
+
{ op: pubkeyForScript.length, data: pubkeyForScript }
|
|
128
|
+
])
|
|
129
|
+
}
|