@bsv/sdk 1.1.30 → 1.1.32
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/transaction/Beef.js +161 -45
- package/dist/cjs/src/transaction/Beef.js.map +1 -1
- package/dist/cjs/src/transaction/BeefParty.js +16 -0
- package/dist/cjs/src/transaction/BeefParty.js.map +1 -1
- package/dist/cjs/src/transaction/BeefTx.js +6 -1
- package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +12 -6
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/transaction/Beef.js +160 -44
- package/dist/esm/src/transaction/Beef.js.map +1 -1
- package/dist/esm/src/transaction/BeefParty.js +16 -0
- package/dist/esm/src/transaction/BeefParty.js.map +1 -1
- package/dist/esm/src/transaction/BeefTx.js +6 -1
- package/dist/esm/src/transaction/BeefTx.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +12 -6
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/transaction/Beef.d.ts +44 -3
- package/dist/types/src/transaction/Beef.d.ts.map +1 -1
- package/dist/types/src/transaction/BeefParty.d.ts +11 -0
- package/dist/types/src/transaction/BeefParty.d.ts.map +1 -1
- package/dist/types/src/transaction/BeefTx.d.ts +6 -1
- package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +8 -2
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/docs/transaction.md +141 -9
- package/package.json +1 -1
- package/src/transaction/Beef.ts +186 -47
- package/src/transaction/BeefParty.ts +17 -0
- package/src/transaction/BeefTx.ts +6 -1
- package/src/transaction/Transaction.ts +12 -6
- package/src/transaction/__tests/Beef.test.ts +55 -10
package/docs/transaction.md
CHANGED
|
@@ -857,7 +857,7 @@ export default class BeefTx {
|
|
|
857
857
|
_rawTx?: number[];
|
|
858
858
|
_txid?: string;
|
|
859
859
|
inputTxids: string[] = [];
|
|
860
|
-
|
|
860
|
+
isValid?: boolean = undefined;
|
|
861
861
|
get bumpIndex(): number | undefined
|
|
862
862
|
set bumpIndex(v: number | undefined)
|
|
863
863
|
get hasProof(): boolean
|
|
@@ -888,6 +888,16 @@ Argument Details
|
|
|
888
888
|
+ **bumpIndex**
|
|
889
889
|
+ If transaction already has a proof in the beef to which it will be added.
|
|
890
890
|
|
|
891
|
+
#### Property isValid
|
|
892
|
+
|
|
893
|
+
true if `hasProof` or all inputs chain to `hasProof`.
|
|
894
|
+
|
|
895
|
+
Typically set by sorting transactions by proven dependency chains.
|
|
896
|
+
|
|
897
|
+
```ts
|
|
898
|
+
isValid?: boolean = undefined
|
|
899
|
+
```
|
|
900
|
+
|
|
891
901
|
</details>
|
|
892
902
|
|
|
893
903
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
@@ -900,9 +910,13 @@ export class Beef {
|
|
|
900
910
|
bumps: MerklePath[] = [];
|
|
901
911
|
txs: BeefTx[] = [];
|
|
902
912
|
version: BeefVersion = undefined;
|
|
913
|
+
atomicTxid: string | undefined = undefined;
|
|
903
914
|
constructor(version?: BeefVersion)
|
|
904
915
|
get magic(): number
|
|
905
916
|
findTxid(txid: string): BeefTx | undefined
|
|
917
|
+
findBump(txid: string): MerklePath | undefined
|
|
918
|
+
findTransactionForSigning(txid: string): Transaction | undefined
|
|
919
|
+
findAtomicTransaction(txid: string): Transaction | undefined
|
|
906
920
|
mergeBump(bump: MerklePath): number
|
|
907
921
|
mergeRawTx(rawTx: number[], bumpIndex?: number): BeefTx
|
|
908
922
|
mergeTransaction(tx: Transaction): BeefTx
|
|
@@ -917,9 +931,16 @@ export class Beef {
|
|
|
917
931
|
static fromReader(br: Reader): Beef
|
|
918
932
|
static fromBinary(bin: number[]): Beef
|
|
919
933
|
static fromString(s: string, enc?: "hex" | "utf8" | "base64"): Beef
|
|
920
|
-
sortTxs():
|
|
934
|
+
sortTxs(): {
|
|
935
|
+
missingInputs: string[];
|
|
936
|
+
notValid: string[];
|
|
937
|
+
valid: string[];
|
|
938
|
+
withMissingInputs: string[];
|
|
939
|
+
txidOnly: string[];
|
|
940
|
+
}
|
|
921
941
|
clone(): Beef
|
|
922
942
|
trimKnownTxids(knownTxids: string[])
|
|
943
|
+
getValidTxids(): string[]
|
|
923
944
|
toLogString(): string
|
|
924
945
|
}
|
|
925
946
|
```
|
|
@@ -938,6 +959,55 @@ Returns
|
|
|
938
959
|
|
|
939
960
|
a shallow copy of this beef
|
|
940
961
|
|
|
962
|
+
#### Method findAtomicTransaction
|
|
963
|
+
|
|
964
|
+
Builds the proof tree rooted at a specific `Transaction`.
|
|
965
|
+
|
|
966
|
+
To succeed, the Beef must contain all the required transaction and merkle path data.
|
|
967
|
+
|
|
968
|
+
```ts
|
|
969
|
+
findAtomicTransaction(txid: string): Transaction | undefined
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
Returns
|
|
973
|
+
|
|
974
|
+
Transaction with input `SourceTransaction` and `MerklePath` populated from this Beef.
|
|
975
|
+
|
|
976
|
+
Argument Details
|
|
977
|
+
|
|
978
|
+
+ **txid**
|
|
979
|
+
+ The id of the target transaction.
|
|
980
|
+
|
|
981
|
+
#### Method findBump
|
|
982
|
+
|
|
983
|
+
```ts
|
|
984
|
+
findBump(txid: string): MerklePath | undefined
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
Returns
|
|
988
|
+
|
|
989
|
+
`MerklePath` with level zero hash equal to txid or undefined.
|
|
990
|
+
|
|
991
|
+
#### Method findTransactionForSigning
|
|
992
|
+
|
|
993
|
+
Finds a Transaction in this `Beef`
|
|
994
|
+
and adds any missing input SourceTransactions from this `Beef`.
|
|
995
|
+
|
|
996
|
+
The result is suitable for signing.
|
|
997
|
+
|
|
998
|
+
```ts
|
|
999
|
+
findTransactionForSigning(txid: string): Transaction | undefined
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
Returns
|
|
1003
|
+
|
|
1004
|
+
Transaction with all available input `SourceTransaction`s from this Beef.
|
|
1005
|
+
|
|
1006
|
+
Argument Details
|
|
1007
|
+
|
|
1008
|
+
+ **txid**
|
|
1009
|
+
+ The id of the target transaction.
|
|
1010
|
+
|
|
941
1011
|
#### Method findTxid
|
|
942
1012
|
|
|
943
1013
|
```ts
|
|
@@ -989,6 +1059,16 @@ Argument Details
|
|
|
989
1059
|
+ **enc**
|
|
990
1060
|
+ The encoding of the string value from which BEEF should be constructed
|
|
991
1061
|
|
|
1062
|
+
#### Method getValidTxids
|
|
1063
|
+
|
|
1064
|
+
```ts
|
|
1065
|
+
getValidTxids(): string[]
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
Returns
|
|
1069
|
+
|
|
1070
|
+
array of transaction txids that either have a proof or whose inputs chain back to a proven transaction.
|
|
1071
|
+
|
|
992
1072
|
#### Method isValid
|
|
993
1073
|
|
|
994
1074
|
Sorts `txs` and checks structural validity of beef.
|
|
@@ -1074,15 +1154,26 @@ Argument Details
|
|
|
1074
1154
|
|
|
1075
1155
|
#### Method sortTxs
|
|
1076
1156
|
|
|
1077
|
-
Sort the `txs` by input txid dependency order
|
|
1157
|
+
Sort the `txs` by input txid dependency order:
|
|
1158
|
+
- Oldest Tx Anchored by Path
|
|
1159
|
+
- Newer Txs depending on Older parents
|
|
1160
|
+
- Newest Tx
|
|
1161
|
+
|
|
1162
|
+
with proof (MerklePath) last, longest chain of dependencies first
|
|
1078
1163
|
|
|
1079
1164
|
```ts
|
|
1080
|
-
sortTxs():
|
|
1165
|
+
sortTxs(): {
|
|
1166
|
+
missingInputs: string[];
|
|
1167
|
+
notValid: string[];
|
|
1168
|
+
valid: string[];
|
|
1169
|
+
withMissingInputs: string[];
|
|
1170
|
+
txidOnly: string[];
|
|
1171
|
+
}
|
|
1081
1172
|
```
|
|
1082
1173
|
|
|
1083
1174
|
Returns
|
|
1084
1175
|
|
|
1085
|
-
|
|
1176
|
+
`{ missingInputs, notValid, valid, withMissingInputs }`
|
|
1086
1177
|
|
|
1087
1178
|
#### Method toBinary
|
|
1088
1179
|
|
|
@@ -1219,8 +1310,8 @@ export default class Transaction {
|
|
|
1219
1310
|
id(enc: "hex"): string;
|
|
1220
1311
|
id(enc?: "hex"): number[] | string
|
|
1221
1312
|
async verify(chainTracker: ChainTracker | "scripts only" = defaultChainTracker(), feeModel?: FeeModel): Promise<boolean>
|
|
1222
|
-
toBEEF(): number[]
|
|
1223
|
-
toAtomicBEEF(): number[]
|
|
1313
|
+
toBEEF(allowPartial?: boolean): number[]
|
|
1314
|
+
toAtomicBEEF(allowPartial?: boolean): number[]
|
|
1224
1315
|
}
|
|
1225
1316
|
```
|
|
1226
1317
|
|
|
@@ -1554,25 +1645,43 @@ and then the BEEF data containing only the subject transaction and its dependenc
|
|
|
1554
1645
|
This format ensures that the BEEF structure is atomic and contains no unrelated transactions.
|
|
1555
1646
|
|
|
1556
1647
|
```ts
|
|
1557
|
-
toAtomicBEEF(): number[]
|
|
1648
|
+
toAtomicBEEF(allowPartial?: boolean): number[]
|
|
1558
1649
|
```
|
|
1559
1650
|
|
|
1560
1651
|
Returns
|
|
1561
1652
|
|
|
1562
1653
|
- The serialized Atomic BEEF structure.
|
|
1563
1654
|
|
|
1655
|
+
Argument Details
|
|
1656
|
+
|
|
1657
|
+
+ **allowPartial**
|
|
1658
|
+
+ If true, error will not be thrown if there are any missing sourceTransactions.
|
|
1659
|
+
|
|
1660
|
+
Throws
|
|
1661
|
+
|
|
1662
|
+
Error if there are any missing sourceTransactions unless `allowPartial` is true.
|
|
1663
|
+
|
|
1564
1664
|
#### Method toBEEF
|
|
1565
1665
|
|
|
1566
1666
|
Serializes this transaction, together with its inputs and the respective merkle proofs, into the BEEF (BRC-62) format. This enables efficient verification of its compliance with the rules of SPV.
|
|
1567
1667
|
|
|
1568
1668
|
```ts
|
|
1569
|
-
toBEEF(): number[]
|
|
1669
|
+
toBEEF(allowPartial?: boolean): number[]
|
|
1570
1670
|
```
|
|
1571
1671
|
|
|
1572
1672
|
Returns
|
|
1573
1673
|
|
|
1574
1674
|
The serialized BEEF structure
|
|
1575
1675
|
|
|
1676
|
+
Argument Details
|
|
1677
|
+
|
|
1678
|
+
+ **allowPartial**
|
|
1679
|
+
+ If true, error will not be thrown if there are any missing sourceTransactions.
|
|
1680
|
+
|
|
1681
|
+
Throws
|
|
1682
|
+
|
|
1683
|
+
Error if there are any missing sourceTransactions unless `allowPartial` is true.
|
|
1684
|
+
|
|
1576
1685
|
#### Method toBinary
|
|
1577
1686
|
|
|
1578
1687
|
Converts the transaction to a binary array format.
|
|
@@ -1716,6 +1825,7 @@ export class BeefParty extends Beef {
|
|
|
1716
1825
|
getKnownTxidsForParty(party: string): string[]
|
|
1717
1826
|
getTrimmedBeefForParty(party: string): Beef
|
|
1718
1827
|
addKnownTxidsForParty(party: string, knownTxids: string[])
|
|
1828
|
+
mergeBeefFromParty(party: string, beef: number[] | Beef)
|
|
1719
1829
|
}
|
|
1720
1830
|
```
|
|
1721
1831
|
|
|
@@ -1794,6 +1904,18 @@ Returns
|
|
|
1794
1904
|
|
|
1795
1905
|
`true` if `party` has already beed added to this `BeefParty`.
|
|
1796
1906
|
|
|
1907
|
+
#### Method mergeBeefFromParty
|
|
1908
|
+
|
|
1909
|
+
Merge a `beef` received from a specific `party`.
|
|
1910
|
+
|
|
1911
|
+
Updates this `BeefParty` to track all the txids
|
|
1912
|
+
corresponding to transactions for which `party`
|
|
1913
|
+
has raw transaction and validity proof data.
|
|
1914
|
+
|
|
1915
|
+
```ts
|
|
1916
|
+
mergeBeefFromParty(party: string, beef: number[] | Beef)
|
|
1917
|
+
```
|
|
1918
|
+
|
|
1797
1919
|
</details>
|
|
1798
1920
|
|
|
1799
1921
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
@@ -1925,6 +2047,7 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
|
|
|
1925
2047
|
|
|
1926
2048
|
| |
|
|
1927
2049
|
| --- |
|
|
2050
|
+
| [ATOMIC_BEEF](#variable-atomic_beef) |
|
|
1928
2051
|
| [BEEF_MAGIC](#variable-beef_magic) |
|
|
1929
2052
|
| [BEEF_MAGIC_TXID_ONLY_EXTENSION](#variable-beef_magic_txid_only_extension) |
|
|
1930
2053
|
| [BEEF_MAGIC_V2](#variable-beef_magic_v2) |
|
|
@@ -1960,3 +2083,12 @@ BEEF_MAGIC_TXID_ONLY_EXTENSION = 4022206465
|
|
|
1960
2083
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
1961
2084
|
|
|
1962
2085
|
---
|
|
2086
|
+
### Variable: ATOMIC_BEEF
|
|
2087
|
+
|
|
2088
|
+
```ts
|
|
2089
|
+
ATOMIC_BEEF = 16843009
|
|
2090
|
+
```
|
|
2091
|
+
|
|
2092
|
+
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
2093
|
+
|
|
2094
|
+
---
|
package/package.json
CHANGED
package/src/transaction/Beef.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Reader, Writer, toHex, toArray } from '../primitives/utils.js'
|
|
|
7
7
|
export const BEEF_MAGIC = 4022206465 // 0100BEEF in LE order
|
|
8
8
|
export const BEEF_MAGIC_V2 = 4022206466 // 0200BEEF in LE order
|
|
9
9
|
export const BEEF_MAGIC_TXID_ONLY_EXTENSION = 4022206465 // 0100BEEF in LE order
|
|
10
|
+
export const ATOMIC_BEEF = 0x01010101 // Atomic Beef serialization prefix
|
|
10
11
|
|
|
11
12
|
export type BeefVersion = undefined | 'V1' | 'V2'
|
|
12
13
|
|
|
@@ -16,6 +17,14 @@ export type BeefVersion = undefined | 'V1' | 'V2'
|
|
|
16
17
|
*
|
|
17
18
|
* BUMP standard: BRC-74: BSV Unified Merkle Path (BUMP) Format
|
|
18
19
|
* https://github.com/bitcoin-sv/BRCs/blob/master/transactions/0074.md
|
|
20
|
+
*
|
|
21
|
+
* BRC-95: Atomic BEEF Transactions
|
|
22
|
+
* https://github.com/bitcoin-sv/BRCs/blob/master/transactions/0095.md
|
|
23
|
+
*
|
|
24
|
+
* The Atomic BEEF format is supported by the binary deserialization static method `fromBinary`.
|
|
25
|
+
*
|
|
26
|
+
* BRC-96: BEEF V2, Txid Only Extension
|
|
27
|
+
* https://github.com/bitcoin-sv/BRCs/blob/master/transactions/0096.md
|
|
19
28
|
*
|
|
20
29
|
* A valid serialized BEEF is the cornerstone of Simplified Payment Validation (SPV)
|
|
21
30
|
* where they are exchanged between two non-trusting parties to establish the
|
|
@@ -48,11 +57,18 @@ export type BeefVersion = undefined | 'V1' | 'V2'
|
|
|
48
57
|
*
|
|
49
58
|
* A valid `Beef` is only required when sent to a party with no shared history,
|
|
50
59
|
* such as a transaction processor.
|
|
60
|
+
*
|
|
61
|
+
* IMPORTANT NOTE:
|
|
62
|
+
* It is fundamental to the BEEF value proposition that only valid transactions and valid
|
|
63
|
+
* merkle path (BUMP) data be added to it. Merging invalid data breaks the `verify` and `isValid`
|
|
64
|
+
* functions. There is no support for removing invalid data. A `Beef` that becomes invalid
|
|
65
|
+
* must be discarded.
|
|
51
66
|
*/
|
|
52
67
|
export class Beef {
|
|
53
68
|
bumps: MerklePath[] = []
|
|
54
69
|
txs: BeefTx[] = []
|
|
55
70
|
version: BeefVersion = undefined
|
|
71
|
+
atomicTxid: string | undefined = undefined
|
|
56
72
|
|
|
57
73
|
constructor (version?: BeefVersion) {
|
|
58
74
|
this.version = version
|
|
@@ -82,6 +98,81 @@ export class Beef {
|
|
|
82
98
|
return this.txs.find(tx => tx.txid === txid)
|
|
83
99
|
}
|
|
84
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @returns `MerklePath` with level zero hash equal to txid or undefined.
|
|
103
|
+
*/
|
|
104
|
+
findBump (txid: string): MerklePath | undefined {
|
|
105
|
+
return this.bumps.find(b => b.path[0].find(leaf => leaf.hash === txid))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Finds a Transaction in this `Beef`
|
|
110
|
+
* and adds any missing input SourceTransactions from this `Beef`.
|
|
111
|
+
*
|
|
112
|
+
* The result is suitable for signing.
|
|
113
|
+
*
|
|
114
|
+
* @param txid The id of the target transaction.
|
|
115
|
+
* @returns Transaction with all available input `SourceTransaction`s from this Beef.
|
|
116
|
+
*/
|
|
117
|
+
findTransactionForSigning (txid: string): Transaction | undefined {
|
|
118
|
+
|
|
119
|
+
const beefTx = this.findTxid(txid)
|
|
120
|
+
if (!beefTx) return undefined
|
|
121
|
+
|
|
122
|
+
for (const i of beefTx.tx.inputs) {
|
|
123
|
+
if (!i.sourceTransaction) {
|
|
124
|
+
const itx = this.findTxid(i.sourceTXID)
|
|
125
|
+
if (itx) {
|
|
126
|
+
i.sourceTransaction = itx.tx
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return beefTx.tx
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Builds the proof tree rooted at a specific `Transaction`.
|
|
135
|
+
*
|
|
136
|
+
* To succeed, the Beef must contain all the required transaction and merkle path data.
|
|
137
|
+
*
|
|
138
|
+
* @param txid The id of the target transaction.
|
|
139
|
+
* @returns Transaction with input `SourceTransaction` and `MerklePath` populated from this Beef.
|
|
140
|
+
*/
|
|
141
|
+
findAtomicTransaction (txid: string): Transaction | undefined {
|
|
142
|
+
|
|
143
|
+
const beefTx = this.findTxid(txid)
|
|
144
|
+
if (!beefTx) return undefined
|
|
145
|
+
|
|
146
|
+
const addInputProof = (beef: Beef, tx: Transaction) => {
|
|
147
|
+
const mp = beef.findBump(tx.id('hex'))
|
|
148
|
+
if (mp)
|
|
149
|
+
tx.merklePath = mp
|
|
150
|
+
else {
|
|
151
|
+
for (const i of tx.inputs) {
|
|
152
|
+
if (!i.sourceTransaction) {
|
|
153
|
+
const itx = beef.findTxid(i.sourceTXID)
|
|
154
|
+
if (itx) {
|
|
155
|
+
i.sourceTransaction = itx.tx
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (i.sourceTransaction) {
|
|
159
|
+
const mp = beef.findBump(i.sourceTransaction.id('hex'))
|
|
160
|
+
if (mp) {
|
|
161
|
+
i.sourceTransaction.merklePath = mp
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
addInputProof(beef, i.sourceTransaction)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
addInputProof(this, beefTx.tx)
|
|
172
|
+
|
|
173
|
+
return beefTx.tx
|
|
174
|
+
}
|
|
175
|
+
|
|
85
176
|
/**
|
|
86
177
|
* Merge a MerklePath that is assumed to be fully valid.
|
|
87
178
|
* @param bump
|
|
@@ -334,7 +425,13 @@ export class Beef {
|
|
|
334
425
|
}
|
|
335
426
|
|
|
336
427
|
static fromReader (br: Reader): Beef {
|
|
337
|
-
|
|
428
|
+
let version = br.readUInt32LE()
|
|
429
|
+
let atomicTxid: string | undefined = undefined
|
|
430
|
+
if (version === ATOMIC_BEEF) {
|
|
431
|
+
// Skip the txid and re-read the BEEF version
|
|
432
|
+
atomicTxid = toHex(br.readReverse(32))
|
|
433
|
+
version = br.readUInt32LE()
|
|
434
|
+
}
|
|
338
435
|
if (version !== BEEF_MAGIC && version !== BEEF_MAGIC_V2) { throw new Error(`Serialized BEEF must start with ${BEEF_MAGIC} or ${BEEF_MAGIC_V2} but starts with ${version}`) }
|
|
339
436
|
const beef = new Beef(version === BEEF_MAGIC_V2 ? 'V2' : undefined)
|
|
340
437
|
const bumpsLength = br.readVarIntNum()
|
|
@@ -347,6 +444,7 @@ export class Beef {
|
|
|
347
444
|
const beefTx = BeefTx.fromReader(br, version)
|
|
348
445
|
beef.txs.push(beefTx)
|
|
349
446
|
}
|
|
447
|
+
beef.atomicTxid = atomicTxid
|
|
350
448
|
return beef
|
|
351
449
|
}
|
|
352
450
|
|
|
@@ -395,71 +493,104 @@ export class Beef {
|
|
|
395
493
|
}
|
|
396
494
|
|
|
397
495
|
/**
|
|
398
|
-
* Sort the `txs` by input txid dependency order
|
|
399
|
-
*
|
|
496
|
+
* Sort the `txs` by input txid dependency order:
|
|
497
|
+
* - Oldest Tx Anchored by Path
|
|
498
|
+
* - Newer Txs depending on Older parents
|
|
499
|
+
* - Newest Tx
|
|
500
|
+
*
|
|
501
|
+
* with proof (MerklePath) last, longest chain of dependencies first
|
|
502
|
+
*
|
|
503
|
+
* @returns `{ missingInputs, notValid, valid, withMissingInputs }`
|
|
400
504
|
*/
|
|
401
|
-
sortTxs ()
|
|
402
|
-
|
|
403
|
-
|
|
505
|
+
sortTxs ()
|
|
506
|
+
: {
|
|
507
|
+
missingInputs: string[],
|
|
508
|
+
notValid: string[],
|
|
509
|
+
valid: string[],
|
|
510
|
+
withMissingInputs: string[],
|
|
511
|
+
txidOnly: string[]
|
|
512
|
+
} {
|
|
513
|
+
// Hashtable of valid txids (with proof or all inputs chain to proof)
|
|
514
|
+
const validTxids: Record<string, boolean> = {}
|
|
515
|
+
|
|
516
|
+
// Hashtable of all transaction txids to transaction
|
|
404
517
|
const txidToTx: Record<string, BeefTx> = {}
|
|
405
518
|
|
|
519
|
+
// queue of unsorted transactions ...
|
|
520
|
+
let queue: BeefTx[] = []
|
|
521
|
+
|
|
522
|
+
// sorted transactions: hasProof to with longest dependency chain
|
|
523
|
+
const result: BeefTx[] = []
|
|
524
|
+
|
|
525
|
+
const txidOnly: BeefTx[] = []
|
|
526
|
+
|
|
406
527
|
for (const tx of this.txs) {
|
|
407
528
|
txidToTx[tx.txid] = tx
|
|
408
|
-
|
|
409
|
-
tx.
|
|
529
|
+
tx.isValid = tx.hasProof
|
|
530
|
+
if (tx.isValid) {
|
|
531
|
+
validTxids[tx.txid] = true
|
|
532
|
+
result.push(tx)
|
|
533
|
+
} else if (tx.isTxidOnly)
|
|
534
|
+
txidOnly.push(tx)
|
|
535
|
+
else
|
|
536
|
+
queue.push(tx)
|
|
410
537
|
}
|
|
411
538
|
|
|
412
|
-
|
|
413
|
-
|
|
539
|
+
// Hashtable of unknown input txids used to fund transactions without their own proof.
|
|
540
|
+
const missingInputs: Record<string, boolean> = {}
|
|
541
|
+
// transactions with one or more missing inputs
|
|
542
|
+
const txsMissingInputs: BeefTx[] = []
|
|
543
|
+
|
|
544
|
+
const possiblyMissingInputs = queue
|
|
545
|
+
queue = []
|
|
546
|
+
|
|
547
|
+
for (const tx of possiblyMissingInputs) {
|
|
548
|
+
let hasMissingInput = false
|
|
549
|
+
if (!tx.isValid) {
|
|
414
550
|
// For all the unproven transactions,
|
|
415
551
|
// link their inputs that exist in this beef,
|
|
416
552
|
// make a note of missing inputs.
|
|
417
553
|
for (const inputTxid of tx.inputTxids) {
|
|
418
|
-
if (!txidToTx[inputTxid]) {
|
|
554
|
+
if (!txidToTx[inputTxid]) {
|
|
555
|
+
missingInputs[inputTxid] = true
|
|
556
|
+
hasMissingInput = true
|
|
557
|
+
}
|
|
419
558
|
}
|
|
420
559
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const queue: BeefTx[] = []
|
|
425
|
-
// sorted transactions
|
|
426
|
-
const result: BeefTx[] = []
|
|
427
|
-
|
|
428
|
-
// Increment each txid's degree for every input reference to it by another txid
|
|
429
|
-
for (const tx of this.txs) {
|
|
430
|
-
for (const inputTxid of tx.inputTxids) {
|
|
431
|
-
const tx = txidToTx[inputTxid]
|
|
432
|
-
if (tx) { tx.degree++ }
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
// Since circular dependencies aren't possible, start with the txids no one depends on.
|
|
436
|
-
// These are the transactions that should be sent last...
|
|
437
|
-
for (const tx of this.txs) {
|
|
438
|
-
if (tx.degree === 0) {
|
|
560
|
+
if (hasMissingInput)
|
|
561
|
+
txsMissingInputs.push(tx)
|
|
562
|
+
else
|
|
439
563
|
queue.push(tx)
|
|
440
|
-
}
|
|
441
564
|
}
|
|
442
|
-
|
|
565
|
+
|
|
566
|
+
// As long as we have unsorted transactions...
|
|
443
567
|
while (queue.length > 0) {
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
if (inputTx) {
|
|
453
|
-
inputTx.degree--
|
|
454
|
-
if (inputTx.degree === 0) {
|
|
455
|
-
queue.push(inputTx)
|
|
456
|
-
}
|
|
457
|
-
}
|
|
568
|
+
const oldQueue = queue
|
|
569
|
+
queue = []
|
|
570
|
+
for (const tx of oldQueue) {
|
|
571
|
+
if (tx.inputTxids.every(txid => validTxids[txid])) {
|
|
572
|
+
validTxids[tx.txid] = true
|
|
573
|
+
result.push(tx)
|
|
574
|
+
} else
|
|
575
|
+
queue.push(tx)
|
|
458
576
|
}
|
|
577
|
+
if (oldQueue.length === queue.length)
|
|
578
|
+
break;
|
|
459
579
|
}
|
|
460
|
-
this.txs = result
|
|
461
580
|
|
|
462
|
-
|
|
581
|
+
// transactions that don't have proofs and don't chain to proofs
|
|
582
|
+
const txsNotValid = queue
|
|
583
|
+
|
|
584
|
+
// New order of txs is sorted, unsortable, txidOnly (no raw transaction)
|
|
585
|
+
this.txs = result.concat(queue).concat(txidOnly).concat(txsMissingInputs)
|
|
586
|
+
|
|
587
|
+
return {
|
|
588
|
+
missingInputs: Object.keys(missingInputs),
|
|
589
|
+
notValid: txsNotValid.map(tx => tx.txid),
|
|
590
|
+
valid: Object.keys(validTxids),
|
|
591
|
+
withMissingInputs: txsMissingInputs.map(tx => tx.txid),
|
|
592
|
+
txidOnly: txidOnly.map(tx => tx.txid)
|
|
593
|
+
}
|
|
463
594
|
}
|
|
464
595
|
|
|
465
596
|
/**
|
|
@@ -488,6 +619,14 @@ export class Beef {
|
|
|
488
619
|
// TODO: bumps could be trimmed to eliminate unreferenced proofs.
|
|
489
620
|
}
|
|
490
621
|
|
|
622
|
+
/**
|
|
623
|
+
* @returns array of transaction txids that either have a proof or whose inputs chain back to a proven transaction.
|
|
624
|
+
*/
|
|
625
|
+
getValidTxids() : string[] {
|
|
626
|
+
const r = this.sortTxs()
|
|
627
|
+
return r.valid
|
|
628
|
+
}
|
|
629
|
+
|
|
491
630
|
/**
|
|
492
631
|
* @returns Summary of `Beef` contents as multi-line string.
|
|
493
632
|
*/
|
|
@@ -91,6 +91,23 @@ export class BeefParty extends Beef {
|
|
|
91
91
|
this.mergeTxidOnly(txid)
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Merge a `beef` received from a specific `party`.
|
|
97
|
+
*
|
|
98
|
+
* Updates this `BeefParty` to track all the txids
|
|
99
|
+
* corresponding to transactions for which `party`
|
|
100
|
+
* has raw transaction and validity proof data.
|
|
101
|
+
*
|
|
102
|
+
* @param party
|
|
103
|
+
* @param beef
|
|
104
|
+
*/
|
|
105
|
+
mergeBeefFromParty (party: string, beef: number[] | Beef) {
|
|
106
|
+
const b: Beef = Array.isArray(beef) ? Beef.fromBinary(beef) : beef
|
|
107
|
+
const knownTxids = b.getValidTxids()
|
|
108
|
+
this.mergeBeef(b)
|
|
109
|
+
this.addKnownTxidsForParty(party, knownTxids)
|
|
110
|
+
}
|
|
94
111
|
}
|
|
95
112
|
|
|
96
113
|
export default BeefParty
|
|
@@ -18,7 +18,12 @@ export default class BeefTx {
|
|
|
18
18
|
_rawTx?: number[]
|
|
19
19
|
_txid?: string
|
|
20
20
|
inputTxids: string[] = []
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* true if `hasProof` or all inputs chain to `hasProof`.
|
|
23
|
+
*
|
|
24
|
+
* Typically set by sorting transactions by proven dependency chains.
|
|
25
|
+
*/
|
|
26
|
+
isValid?: boolean = undefined
|
|
22
27
|
|
|
23
28
|
get bumpIndex (): number | undefined { return this._bumpIndex }
|
|
24
29
|
|
|
@@ -859,10 +859,13 @@ export default class Transaction {
|
|
|
859
859
|
|
|
860
860
|
/**
|
|
861
861
|
* Serializes this transaction, together with its inputs and the respective merkle proofs, into the BEEF (BRC-62) format. This enables efficient verification of its compliance with the rules of SPV.
|
|
862
|
+
*
|
|
863
|
+
* @param allowPartial If true, error will not be thrown if there are any missing sourceTransactions.
|
|
862
864
|
*
|
|
863
865
|
* @returns The serialized BEEF structure
|
|
866
|
+
* @throws Error if there are any missing sourceTransactions unless `allowPartial` is true.
|
|
864
867
|
*/
|
|
865
|
-
toBEEF (): number[] {
|
|
868
|
+
toBEEF (allowPartial?: boolean): number[] {
|
|
866
869
|
const writer = new Writer()
|
|
867
870
|
writer.writeUInt32LE(4022206465)
|
|
868
871
|
const BUMPs: MerklePath[] = []
|
|
@@ -907,10 +910,10 @@ export default class Transaction {
|
|
|
907
910
|
if (!hasProof) {
|
|
908
911
|
for (let i = 0; i < tx.inputs.length; i++) {
|
|
909
912
|
const input = tx.inputs[i]
|
|
910
|
-
if (typeof input.sourceTransaction
|
|
913
|
+
if (typeof input.sourceTransaction === 'object')
|
|
914
|
+
addPathsAndInputs(input.sourceTransaction)
|
|
915
|
+
else if (!allowPartial)
|
|
911
916
|
throw new Error('A required source transaction is missing!')
|
|
912
|
-
}
|
|
913
|
-
addPathsAndInputs(input.sourceTransaction)
|
|
914
917
|
}
|
|
915
918
|
}
|
|
916
919
|
}
|
|
@@ -940,16 +943,19 @@ export default class Transaction {
|
|
|
940
943
|
* and then the BEEF data containing only the subject transaction and its dependencies.
|
|
941
944
|
* This format ensures that the BEEF structure is atomic and contains no unrelated transactions.
|
|
942
945
|
*
|
|
946
|
+
* @param allowPartial If true, error will not be thrown if there are any missing sourceTransactions.
|
|
947
|
+
*
|
|
943
948
|
* @returns {number[]} - The serialized Atomic BEEF structure.
|
|
949
|
+
* @throws Error if there are any missing sourceTransactions unless `allowPartial` is true.
|
|
944
950
|
*/
|
|
945
|
-
toAtomicBEEF (): number[] {
|
|
951
|
+
toAtomicBEEF (allowPartial?: boolean): number[] {
|
|
946
952
|
const writer = new Writer()
|
|
947
953
|
// Write the Atomic BEEF prefix
|
|
948
954
|
writer.writeUInt32LE(0x01010101)
|
|
949
955
|
// Write the subject TXID (big-endian)
|
|
950
956
|
writer.write(this.id())
|
|
951
957
|
// Append the BEEF data
|
|
952
|
-
const beefData = this.toBEEF()
|
|
958
|
+
const beefData = this.toBEEF(allowPartial)
|
|
953
959
|
writer.write(beefData)
|
|
954
960
|
return writer.toArray()
|
|
955
961
|
}
|