@bsv/sdk 1.2.8 → 1.2.10
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/dist/cjs/package.json +1 -1
- package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js +2 -2
- package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js.map +1 -1
- package/dist/cjs/src/primitives/PublicKey.js +1 -1
- package/dist/cjs/src/primitives/PublicKey.js.map +1 -1
- package/dist/cjs/src/transaction/Beef.js +38 -13
- package/dist/cjs/src/transaction/Beef.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/overlay-tools/SHIPBroadcaster.js +2 -2
- package/dist/esm/src/overlay-tools/SHIPBroadcaster.js.map +1 -1
- package/dist/esm/src/primitives/PublicKey.js +1 -1
- package/dist/esm/src/primitives/PublicKey.js.map +1 -1
- package/dist/esm/src/transaction/Beef.js +38 -13
- package/dist/esm/src/transaction/Beef.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts +9 -4
- package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts.map +1 -1
- package/dist/types/src/primitives/PublicKey.d.ts.map +1 -1
- package/dist/types/src/transaction/Beef.d.ts +12 -1
- package/dist/types/src/transaction/Beef.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/docs/overlay-tools.md +11 -1
- package/docs/transaction.md +19 -1
- package/package.json +1 -1
- package/src/overlay-tools/SHIPBroadcaster.ts +13 -6
- package/src/overlay-tools/__tests/SHIPBroadcaster.test.ts +82 -0
- package/src/primitives/PublicKey.ts +12 -12
- package/src/transaction/Beef.ts +39 -13
- package/src/transaction/__tests/Beef.test.ts +18 -0
package/docs/overlay-tools.md
CHANGED
|
@@ -26,6 +26,7 @@ Instructs the Overlay Services Engine about which outputs to admit and which pre
|
|
|
26
26
|
export interface AdmittanceInstructions {
|
|
27
27
|
outputsToAdmit: number[];
|
|
28
28
|
coinsToRetain: number[];
|
|
29
|
+
coinsRemoved?: number[];
|
|
29
30
|
}
|
|
30
31
|
```
|
|
31
32
|
|
|
@@ -33,6 +34,15 @@ export interface AdmittanceInstructions {
|
|
|
33
34
|
|
|
34
35
|
<summary>Interface AdmittanceInstructions Details</summary>
|
|
35
36
|
|
|
37
|
+
#### Property coinsRemoved
|
|
38
|
+
|
|
39
|
+
The indices of all inputs from the provided transaction which reference previously-admitted outputs,
|
|
40
|
+
which are now considered spent and have been removed from the managed topic.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
coinsRemoved?: number[]
|
|
44
|
+
```
|
|
45
|
+
|
|
36
46
|
#### Property coinsToRetain
|
|
37
47
|
|
|
38
48
|
The indices of all inputs from the provided transaction which spend previously-admitted outputs that should be retained for historical record-keeping.
|
|
@@ -43,7 +53,7 @@ coinsToRetain: number[]
|
|
|
43
53
|
|
|
44
54
|
#### Property outputsToAdmit
|
|
45
55
|
|
|
46
|
-
The indices of all
|
|
56
|
+
The indices of all admissible outputs into the managed topic from the provided transaction.
|
|
47
57
|
|
|
48
58
|
```ts
|
|
49
59
|
outputsToAdmit: number[]
|
package/docs/transaction.md
CHANGED
|
@@ -592,6 +592,7 @@ export class Beef {
|
|
|
592
592
|
constructor(version?: BeefVersion)
|
|
593
593
|
get magic(): number
|
|
594
594
|
findTxid(txid: string): BeefTx | undefined
|
|
595
|
+
makeTxidOnly(txid: string): BeefTx | undefined
|
|
595
596
|
findBump(txid: string): MerklePath | undefined
|
|
596
597
|
findTransactionForSigning(txid: string): Transaction | undefined
|
|
597
598
|
findAtomicTransaction(txid: string): Transaction | undefined
|
|
@@ -779,6 +780,23 @@ Argument Details
|
|
|
779
780
|
+ **allowTxidOnly**
|
|
780
781
|
+ optional. If true, transaction txid only is assumed valid
|
|
781
782
|
|
|
783
|
+
#### Method makeTxidOnly
|
|
784
|
+
|
|
785
|
+
Replaces `BeefTx` for this txid with txidOnly.
|
|
786
|
+
|
|
787
|
+
Replacement is done so that a `clone()` can be
|
|
788
|
+
updated by this method without affecting the
|
|
789
|
+
original.
|
|
790
|
+
|
|
791
|
+
```ts
|
|
792
|
+
makeTxidOnly(txid: string): BeefTx | undefined
|
|
793
|
+
```
|
|
794
|
+
See also: [BeefTx](#class-beeftx)
|
|
795
|
+
|
|
796
|
+
Returns
|
|
797
|
+
|
|
798
|
+
undefined if txid is unknown.
|
|
799
|
+
|
|
782
800
|
#### Method mergeBump
|
|
783
801
|
|
|
784
802
|
Merge a MerklePath that is assumed to be fully valid.
|
|
@@ -847,7 +865,7 @@ Argument Details
|
|
|
847
865
|
#### Method sortTxs
|
|
848
866
|
|
|
849
867
|
Sort the `txs` by input txid dependency order:
|
|
850
|
-
- Oldest Tx Anchored by Path
|
|
868
|
+
- Oldest Tx Anchored by Path or txid only
|
|
851
869
|
- Newer Txs depending on Older parents
|
|
852
870
|
- Newest Tx
|
|
853
871
|
|
package/package.json
CHANGED
|
@@ -18,14 +18,20 @@ export interface TaggedBEEF {
|
|
|
18
18
|
*/
|
|
19
19
|
export interface AdmittanceInstructions {
|
|
20
20
|
/**
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
* The indices of all admissible outputs into the managed topic from the provided transaction.
|
|
22
|
+
*/
|
|
23
23
|
outputsToAdmit: number[]
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
* The indices of all inputs from the provided transaction which spend previously-admitted outputs that should be retained for historical record-keeping.
|
|
27
|
+
*/
|
|
28
28
|
coinsToRetain: number[]
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The indices of all inputs from the provided transaction which reference previously-admitted outputs,
|
|
32
|
+
* which are now considered spent and have been removed from the managed topic.
|
|
33
|
+
*/
|
|
34
|
+
coinsRemoved?: number[]
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
/**
|
|
@@ -180,8 +186,9 @@ export default class SHIPCast implements Broadcaster {
|
|
|
180
186
|
for (const [topic, instructions] of Object.entries(steak)) {
|
|
181
187
|
const outputsToAdmit = instructions.outputsToAdmit
|
|
182
188
|
const coinsToRetain = instructions.coinsToRetain
|
|
183
|
-
|
|
184
|
-
|
|
189
|
+
const coinsRemoved = instructions.coinsRemoved
|
|
190
|
+
|
|
191
|
+
if ((outputsToAdmit?.length > 0) || (coinsToRetain?.length > 0) || (coinsRemoved?.length > 0)) {
|
|
185
192
|
acknowledgedTopics.add(topic)
|
|
186
193
|
}
|
|
187
194
|
}
|
|
@@ -652,6 +652,88 @@ describe('SHIPCast', () => {
|
|
|
652
652
|
})
|
|
653
653
|
})
|
|
654
654
|
|
|
655
|
+
it('should succeed when interested hosts only remove coins in a transaction broadcast', async () => {
|
|
656
|
+
const shipHostKey1 = new PrivateKey(42)
|
|
657
|
+
const shipWallet1 = new ProtoWallet(shipHostKey1)
|
|
658
|
+
const shipLib1 = new OverlayAdminTokenTemplate(shipWallet1)
|
|
659
|
+
const shipScript1 = await shipLib1.lock('SHIP', 'https://shiphost1.com', 'tm_foo')
|
|
660
|
+
const shipTx1 = new Transaction(1, [], [{
|
|
661
|
+
lockingScript: shipScript1,
|
|
662
|
+
satoshis: 1
|
|
663
|
+
}], 0)
|
|
664
|
+
|
|
665
|
+
const shipHostKey2 = new PrivateKey(43)
|
|
666
|
+
const shipWallet2 = new ProtoWallet(shipHostKey2)
|
|
667
|
+
const shipLib2 = new OverlayAdminTokenTemplate(shipWallet2)
|
|
668
|
+
const shipScript2 = await shipLib2.lock('SHIP', 'https://shiphost2.com', 'tm_bar')
|
|
669
|
+
const shipTx2 = new Transaction(1, [], [{
|
|
670
|
+
lockingScript: shipScript2,
|
|
671
|
+
satoshis: 1
|
|
672
|
+
}], 0)
|
|
673
|
+
|
|
674
|
+
// Resolver returns two hosts
|
|
675
|
+
mockResolver.query.mockReturnValueOnce({
|
|
676
|
+
type: 'output-list',
|
|
677
|
+
outputs: [
|
|
678
|
+
{ beef: shipTx1.toBEEF(), outputIndex: 0 },
|
|
679
|
+
{ beef: shipTx2.toBEEF(), outputIndex: 0 }
|
|
680
|
+
]
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
// First host acknowledges 'tm_foo' with coinsRemoved
|
|
684
|
+
mockFacilitator.send.mockImplementationOnce(async (host, { beef, topics }) => {
|
|
685
|
+
const steak = {}
|
|
686
|
+
for (const topic of topics) {
|
|
687
|
+
steak[topic] = {
|
|
688
|
+
outputsToAdmit: [],
|
|
689
|
+
coinsToRetain: [],
|
|
690
|
+
coinsRemoved: topic === 'tm_foo' ? [0] : []
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return steak
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
// Second host does not acknowledge 'tm_bar'
|
|
697
|
+
mockFacilitator.send.mockImplementationOnce(async (host, { beef, topics }) => {
|
|
698
|
+
const steak = {}
|
|
699
|
+
for (const topic of topics) {
|
|
700
|
+
steak[topic] = {
|
|
701
|
+
outputsToAdmit: [],
|
|
702
|
+
coinsToRetain: [],
|
|
703
|
+
coinsRemoved: []
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return steak
|
|
707
|
+
})
|
|
708
|
+
|
|
709
|
+
const b = new SHIPCast(['tm_foo', 'tm_bar'], {
|
|
710
|
+
facilitator: mockFacilitator,
|
|
711
|
+
resolver: mockResolver as unknown as LookupResolver,
|
|
712
|
+
requireAcknowledgmentFromSpecificHostsForTopics: {
|
|
713
|
+
'https://shiphost1.com': ['tm_foo']
|
|
714
|
+
},
|
|
715
|
+
requireAcknowledgmentFromAllHostsForTopics: [],
|
|
716
|
+
requireAcknowledgmentFromAnyHostForTopics: []
|
|
717
|
+
})
|
|
718
|
+
|
|
719
|
+
const testTx = new Transaction(1, [], [], 0)
|
|
720
|
+
const response = await b.broadcast(testTx)
|
|
721
|
+
|
|
722
|
+
expect(response).toEqual({
|
|
723
|
+
status: 'success',
|
|
724
|
+
txid: testTx.id('hex'),
|
|
725
|
+
message: 'Sent to 2 Overlay Services hosts.'
|
|
726
|
+
})
|
|
727
|
+
|
|
728
|
+
// Verify the resolver was queried correctly
|
|
729
|
+
expect(mockResolver.query).toHaveBeenCalledWith({
|
|
730
|
+
service: 'ls_ship',
|
|
731
|
+
query: {
|
|
732
|
+
topics: ['tm_foo', 'tm_bar']
|
|
733
|
+
}
|
|
734
|
+
}, 1000)
|
|
735
|
+
})
|
|
736
|
+
|
|
655
737
|
it('should fail when specific hosts do not acknowledge required topics', async () => {
|
|
656
738
|
const shipHostKey1 = new PrivateKey(42)
|
|
657
739
|
const shipWallet1 = new ProtoWallet(shipHostKey1)
|
|
@@ -30,7 +30,7 @@ export default class PublicKey extends Point {
|
|
|
30
30
|
* const myPrivKey = new PrivateKey(...)
|
|
31
31
|
* const myPubKey = PublicKey.fromPrivateKey(myPrivKey)
|
|
32
32
|
*/
|
|
33
|
-
static fromPrivateKey(key: PrivateKey): PublicKey {
|
|
33
|
+
static fromPrivateKey (key: PrivateKey): PublicKey {
|
|
34
34
|
const c = new Curve()
|
|
35
35
|
const p = c.g.mul(key)
|
|
36
36
|
return new PublicKey(p.x, p.y)
|
|
@@ -46,7 +46,7 @@ export default class PublicKey extends Point {
|
|
|
46
46
|
* @example
|
|
47
47
|
* const myPubKey = PublicKey.fromString("03....")
|
|
48
48
|
*/
|
|
49
|
-
static fromString(str: string): PublicKey {
|
|
49
|
+
static fromString (str: string): PublicKey {
|
|
50
50
|
const p = Point.fromString(str)
|
|
51
51
|
return new PublicKey(p.x, p.y)
|
|
52
52
|
}
|
|
@@ -61,7 +61,7 @@ export default class PublicKey extends Point {
|
|
|
61
61
|
* @example
|
|
62
62
|
* const myPubKey = PublicKey.fromString("03....")
|
|
63
63
|
*/
|
|
64
|
-
static fromDER(bytes: number[]): PublicKey {
|
|
64
|
+
static fromDER (bytes: number[]): PublicKey {
|
|
65
65
|
const p = Point.fromDER(bytes)
|
|
66
66
|
return new PublicKey(p.x, p.y)
|
|
67
67
|
}
|
|
@@ -76,7 +76,7 @@ export default class PublicKey extends Point {
|
|
|
76
76
|
* new PublicKey(point1);
|
|
77
77
|
* new PublicKey('abc123', 'def456');
|
|
78
78
|
*/
|
|
79
|
-
constructor(
|
|
79
|
+
constructor (
|
|
80
80
|
x: Point | BigNumber | number | number[] | string | null,
|
|
81
81
|
y: BigNumber | number | number[] | string | null = null,
|
|
82
82
|
isRed: boolean = true
|
|
@@ -85,7 +85,7 @@ export default class PublicKey extends Point {
|
|
|
85
85
|
super(x.getX(), x.getY())
|
|
86
86
|
} else {
|
|
87
87
|
// Common gotcha: constructing PublicKey with a DER value when you should use .fromString()
|
|
88
|
-
if (y === null && isRed
|
|
88
|
+
if (y === null && isRed && typeof x === 'string') {
|
|
89
89
|
if (x.length === 66 || x.length === 130) {
|
|
90
90
|
throw new Error('You are using the "new PublicKey()" constructor with a DER hex string. You need to use "PublicKey.fromString()" instead.')
|
|
91
91
|
}
|
|
@@ -108,7 +108,7 @@ export default class PublicKey extends Point {
|
|
|
108
108
|
* const myPrivKey = new PrivateKey(...)
|
|
109
109
|
* const sharedSecret = myPubKey.deriveSharedSecret(myPrivKey)
|
|
110
110
|
*/
|
|
111
|
-
deriveSharedSecret(priv: PrivateKey): Point {
|
|
111
|
+
deriveSharedSecret (priv: PrivateKey): Point {
|
|
112
112
|
if (!this.validate()) {
|
|
113
113
|
throw new Error('Public key not valid for ECDH secret derivation')
|
|
114
114
|
}
|
|
@@ -129,7 +129,7 @@ export default class PublicKey extends Point {
|
|
|
129
129
|
* const mySignature = new Signature(...)
|
|
130
130
|
* const isVerified = myPubKey.verify(myMessage, mySignature)
|
|
131
131
|
*/
|
|
132
|
-
verify(msg: number[] | string, sig: Signature, enc?: 'hex' | 'utf8'): boolean {
|
|
132
|
+
verify (msg: number[] | string, sig: Signature, enc?: 'hex' | 'utf8'): boolean {
|
|
133
133
|
const msgHash = new BigNumber(sha256(msg, enc), 16)
|
|
134
134
|
return verify(msgHash, sig, this)
|
|
135
135
|
}
|
|
@@ -144,7 +144,7 @@ export default class PublicKey extends Point {
|
|
|
144
144
|
* @example
|
|
145
145
|
* const derPublicKey = myPubKey.toDER()
|
|
146
146
|
*/
|
|
147
|
-
toDER(enc?: 'hex' | undefined): number[] | string {
|
|
147
|
+
toDER (enc?: 'hex' | undefined): number[] | string {
|
|
148
148
|
if (enc === 'hex') return this.encode(true, enc) as string
|
|
149
149
|
return this.encode(true) as number[]
|
|
150
150
|
}
|
|
@@ -157,7 +157,7 @@ export default class PublicKey extends Point {
|
|
|
157
157
|
* @example
|
|
158
158
|
* const publicKeyHash = pubkey.toHash()
|
|
159
159
|
*/
|
|
160
|
-
toHash(enc?: 'hex'): number[] | string {
|
|
160
|
+
toHash (enc?: 'hex'): number[] | string {
|
|
161
161
|
const pkh = hash160(this.encode(true))
|
|
162
162
|
if (enc === 'hex') {
|
|
163
163
|
return toHex(pkh)
|
|
@@ -179,7 +179,7 @@ export default class PublicKey extends Point {
|
|
|
179
179
|
* const testnetAddress = pubkey.toAddress([0x6f])
|
|
180
180
|
* const testnetAddress = pubkey.toAddress('testnet')
|
|
181
181
|
*/
|
|
182
|
-
toAddress(prefix: number[] | string = [0x00]): string {
|
|
182
|
+
toAddress (prefix: number[] | string = [0x00]): string {
|
|
183
183
|
if (typeof prefix === 'string') {
|
|
184
184
|
if (prefix === 'testnet' || prefix === 'test') {
|
|
185
185
|
prefix = [0x6f]
|
|
@@ -198,7 +198,7 @@ export default class PublicKey extends Point {
|
|
|
198
198
|
* @param invoiceNumber The invoice number used to derive the child key
|
|
199
199
|
* @returns The derived child key.
|
|
200
200
|
*/
|
|
201
|
-
deriveChild(privateKey: PrivateKey, invoiceNumber: string): PublicKey {
|
|
201
|
+
deriveChild (privateKey: PrivateKey, invoiceNumber: string): PublicKey {
|
|
202
202
|
const sharedSecret = this.deriveSharedSecret(privateKey)
|
|
203
203
|
const invoiceNumberBin = toArray(invoiceNumber, 'utf8')
|
|
204
204
|
const hmac = sha256hmac(sharedSecret.encode(true), invoiceNumberBin)
|
|
@@ -225,7 +225,7 @@ export default class PublicKey extends Point {
|
|
|
225
225
|
* @example
|
|
226
226
|
* const publicKey = Signature.fromMsgHashAndCompactSignature(msgHash, 'IMOl2mVKfDgsSsHT4uIYBNN4e...', 'base64');
|
|
227
227
|
*/
|
|
228
|
-
static fromMsgHashAndCompactSignature(msgHash: BigNumber, signature: number[] | string, enc?: 'hex' | 'base64'): PublicKey {
|
|
228
|
+
static fromMsgHashAndCompactSignature (msgHash: BigNumber, signature: number[] | string, enc?: 'hex' | 'base64'): PublicKey {
|
|
229
229
|
const data = toArray(signature, enc)
|
|
230
230
|
if (data.length !== 65) {
|
|
231
231
|
throw new Error('Invalid Compact Signature')
|
package/src/transaction/Beef.ts
CHANGED
|
@@ -98,6 +98,26 @@ export class Beef {
|
|
|
98
98
|
return this.txs.find(tx => tx.txid === txid)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Replaces `BeefTx` for this txid with txidOnly.
|
|
103
|
+
*
|
|
104
|
+
* Replacement is done so that a `clone()` can be
|
|
105
|
+
* updated by this method without affecting the
|
|
106
|
+
* original.
|
|
107
|
+
*
|
|
108
|
+
* @param txid
|
|
109
|
+
* @returns undefined if txid is unknown.
|
|
110
|
+
*/
|
|
111
|
+
makeTxidOnly (txid: string): BeefTx | undefined {
|
|
112
|
+
const i = this.txs.findIndex(tx => tx.txid === txid)
|
|
113
|
+
if (i === -1) return undefined
|
|
114
|
+
let btx = this.txs[i]
|
|
115
|
+
if (btx.isTxidOnly) { return btx }
|
|
116
|
+
this.txs.slice(i, i + 1)
|
|
117
|
+
btx = this.mergeTxidOnly(txid)
|
|
118
|
+
return btx
|
|
119
|
+
}
|
|
120
|
+
|
|
101
121
|
/**
|
|
102
122
|
* @returns `MerklePath` with level zero hash equal to txid or undefined.
|
|
103
123
|
*/
|
|
@@ -518,7 +538,7 @@ export class Beef {
|
|
|
518
538
|
|
|
519
539
|
/**
|
|
520
540
|
* Sort the `txs` by input txid dependency order:
|
|
521
|
-
* - Oldest Tx Anchored by Path
|
|
541
|
+
* - Oldest Tx Anchored by Path or txid only
|
|
522
542
|
* - Newer Txs depending on Older parents
|
|
523
543
|
* - Newest Tx
|
|
524
544
|
*
|
|
@@ -553,7 +573,10 @@ export class Beef {
|
|
|
553
573
|
if (tx.isValid) {
|
|
554
574
|
validTxids[tx.txid] = true
|
|
555
575
|
result.push(tx)
|
|
556
|
-
} else if (tx.isTxidOnly
|
|
576
|
+
} else if (tx.isTxidOnly && tx.inputTxids.length === 0) {
|
|
577
|
+
validTxids[tx.txid] = true
|
|
578
|
+
txidOnly.push(tx)
|
|
579
|
+
} else { queue.push(tx) }
|
|
557
580
|
}
|
|
558
581
|
|
|
559
582
|
// Hashtable of unknown input txids used to fund transactions without their own proof.
|
|
@@ -564,17 +587,18 @@ export class Beef {
|
|
|
564
587
|
const possiblyMissingInputs = queue
|
|
565
588
|
queue = []
|
|
566
589
|
|
|
590
|
+
// all tx are isValid false, hasProof false.
|
|
591
|
+
// if isTxidOnly then has inputTxids
|
|
567
592
|
for (const tx of possiblyMissingInputs) {
|
|
568
593
|
let hasMissingInput = false
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
594
|
+
|
|
595
|
+
// For all the unproven transactions,
|
|
596
|
+
// link their inputs that exist in this beef,
|
|
597
|
+
// make a note of missing inputs.
|
|
598
|
+
for (const inputTxid of tx.inputTxids) {
|
|
599
|
+
if (!txidToTx[inputTxid]) {
|
|
600
|
+
missingInputs[inputTxid] = true
|
|
601
|
+
hasMissingInput = true
|
|
578
602
|
}
|
|
579
603
|
}
|
|
580
604
|
if (hasMissingInput) { txsMissingInputs.push(tx) } else { queue.push(tx) }
|
|
@@ -584,6 +608,8 @@ export class Beef {
|
|
|
584
608
|
while (queue.length > 0) {
|
|
585
609
|
const oldQueue = queue
|
|
586
610
|
queue = []
|
|
611
|
+
// all tx are isValid false, hasProof false.
|
|
612
|
+
// if isTxidOnly then has inputTxids
|
|
587
613
|
for (const tx of oldQueue) {
|
|
588
614
|
if (tx.inputTxids.every(txid => validTxids[txid])) {
|
|
589
615
|
validTxids[tx.txid] = true
|
|
@@ -596,8 +622,8 @@ export class Beef {
|
|
|
596
622
|
// transactions that don't have proofs and don't chain to proofs
|
|
597
623
|
const txsNotValid = queue
|
|
598
624
|
|
|
599
|
-
// New order of txs is
|
|
600
|
-
this.txs =
|
|
625
|
+
// New order of txs is unsortable (missing inputs or depends on missing inputs), txidOnly, sorted (so newest sorted is last)
|
|
626
|
+
this.txs = txsMissingInputs.concat(txsNotValid).concat(txidOnly).concat(result)
|
|
601
627
|
|
|
602
628
|
return {
|
|
603
629
|
missingInputs: Object.keys(missingInputs),
|
|
@@ -305,6 +305,24 @@ describe('Beef tests', () => {
|
|
|
305
305
|
expect(atomic).toEqual(beef2)
|
|
306
306
|
}
|
|
307
307
|
})
|
|
308
|
+
test('9_sortTxs', async () => {
|
|
309
|
+
{
|
|
310
|
+
const beef = new Beef()
|
|
311
|
+
beef.mergeTxidOnly('a')
|
|
312
|
+
const btx = beef.mergeTxidOnly('b')
|
|
313
|
+
btx.inputTxids = ['a']
|
|
314
|
+
beef.sortTxs()
|
|
315
|
+
expect(beef.txs[1].txid).toBe('b')
|
|
316
|
+
}
|
|
317
|
+
{
|
|
318
|
+
const beef = new Beef()
|
|
319
|
+
const atx = beef.mergeTxidOnly('a')
|
|
320
|
+
const btx = beef.mergeTxidOnly('b')
|
|
321
|
+
atx.inputTxids = ['b']
|
|
322
|
+
beef.sortTxs()
|
|
323
|
+
expect(beef.txs[1].txid).toBe('a')
|
|
324
|
+
}
|
|
325
|
+
})
|
|
308
326
|
})
|
|
309
327
|
|
|
310
328
|
const txs: string[] = [
|