@bsv/sdk 1.1.30 → 1.1.33
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 +195 -50
- 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 +25 -10
- 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 +194 -49
- 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 +25 -10
- 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 +60 -4
- 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 +166 -9
- package/package.json +1 -1
- package/src/transaction/Beef.ts +221 -52
- package/src/transaction/BeefParty.ts +17 -0
- package/src/transaction/BeefTx.ts +6 -1
- package/src/transaction/Transaction.ts +25 -10
- package/src/transaction/__tests/Beef.test.ts +74 -15
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
|
|
@@ -912,14 +926,23 @@ export class Beef {
|
|
|
912
926
|
mergeBeef(beef: number[] | Beef)
|
|
913
927
|
isValid(allowTxidOnly?: boolean): boolean
|
|
914
928
|
async verify(chainTracker: ChainTracker, allowTxidOnly?: boolean): Promise<boolean>
|
|
929
|
+
toWriter(writer: Writer)
|
|
915
930
|
toBinary(): number[]
|
|
931
|
+
toBinaryAtomic(txid: string)
|
|
916
932
|
toHex(): string
|
|
917
933
|
static fromReader(br: Reader): Beef
|
|
918
934
|
static fromBinary(bin: number[]): Beef
|
|
919
935
|
static fromString(s: string, enc?: "hex" | "utf8" | "base64"): Beef
|
|
920
|
-
sortTxs():
|
|
936
|
+
sortTxs(): {
|
|
937
|
+
missingInputs: string[];
|
|
938
|
+
notValid: string[];
|
|
939
|
+
valid: string[];
|
|
940
|
+
withMissingInputs: string[];
|
|
941
|
+
txidOnly: string[];
|
|
942
|
+
}
|
|
921
943
|
clone(): Beef
|
|
922
944
|
trimKnownTxids(knownTxids: string[])
|
|
945
|
+
getValidTxids(): string[]
|
|
923
946
|
toLogString(): string
|
|
924
947
|
}
|
|
925
948
|
```
|
|
@@ -938,6 +961,55 @@ Returns
|
|
|
938
961
|
|
|
939
962
|
a shallow copy of this beef
|
|
940
963
|
|
|
964
|
+
#### Method findAtomicTransaction
|
|
965
|
+
|
|
966
|
+
Builds the proof tree rooted at a specific `Transaction`.
|
|
967
|
+
|
|
968
|
+
To succeed, the Beef must contain all the required transaction and merkle path data.
|
|
969
|
+
|
|
970
|
+
```ts
|
|
971
|
+
findAtomicTransaction(txid: string): Transaction | undefined
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
Returns
|
|
975
|
+
|
|
976
|
+
Transaction with input `SourceTransaction` and `MerklePath` populated from this Beef.
|
|
977
|
+
|
|
978
|
+
Argument Details
|
|
979
|
+
|
|
980
|
+
+ **txid**
|
|
981
|
+
+ The id of the target transaction.
|
|
982
|
+
|
|
983
|
+
#### Method findBump
|
|
984
|
+
|
|
985
|
+
```ts
|
|
986
|
+
findBump(txid: string): MerklePath | undefined
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
Returns
|
|
990
|
+
|
|
991
|
+
`MerklePath` with level zero hash equal to txid or undefined.
|
|
992
|
+
|
|
993
|
+
#### Method findTransactionForSigning
|
|
994
|
+
|
|
995
|
+
Finds a Transaction in this `Beef`
|
|
996
|
+
and adds any missing input SourceTransactions from this `Beef`.
|
|
997
|
+
|
|
998
|
+
The result is suitable for signing.
|
|
999
|
+
|
|
1000
|
+
```ts
|
|
1001
|
+
findTransactionForSigning(txid: string): Transaction | undefined
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
Returns
|
|
1005
|
+
|
|
1006
|
+
Transaction with all available input `SourceTransaction`s from this Beef.
|
|
1007
|
+
|
|
1008
|
+
Argument Details
|
|
1009
|
+
|
|
1010
|
+
+ **txid**
|
|
1011
|
+
+ The id of the target transaction.
|
|
1012
|
+
|
|
941
1013
|
#### Method findTxid
|
|
942
1014
|
|
|
943
1015
|
```ts
|
|
@@ -989,6 +1061,16 @@ Argument Details
|
|
|
989
1061
|
+ **enc**
|
|
990
1062
|
+ The encoding of the string value from which BEEF should be constructed
|
|
991
1063
|
|
|
1064
|
+
#### Method getValidTxids
|
|
1065
|
+
|
|
1066
|
+
```ts
|
|
1067
|
+
getValidTxids(): string[]
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
Returns
|
|
1071
|
+
|
|
1072
|
+
array of transaction txids that either have a proof or whose inputs chain back to a proven transaction.
|
|
1073
|
+
|
|
992
1074
|
#### Method isValid
|
|
993
1075
|
|
|
994
1076
|
Sorts `txs` and checks structural validity of beef.
|
|
@@ -1074,15 +1156,26 @@ Argument Details
|
|
|
1074
1156
|
|
|
1075
1157
|
#### Method sortTxs
|
|
1076
1158
|
|
|
1077
|
-
Sort the `txs` by input txid dependency order
|
|
1159
|
+
Sort the `txs` by input txid dependency order:
|
|
1160
|
+
- Oldest Tx Anchored by Path
|
|
1161
|
+
- Newer Txs depending on Older parents
|
|
1162
|
+
- Newest Tx
|
|
1163
|
+
|
|
1164
|
+
with proof (MerklePath) last, longest chain of dependencies first
|
|
1078
1165
|
|
|
1079
1166
|
```ts
|
|
1080
|
-
sortTxs():
|
|
1167
|
+
sortTxs(): {
|
|
1168
|
+
missingInputs: string[];
|
|
1169
|
+
notValid: string[];
|
|
1170
|
+
valid: string[];
|
|
1171
|
+
withMissingInputs: string[];
|
|
1172
|
+
txidOnly: string[];
|
|
1173
|
+
}
|
|
1081
1174
|
```
|
|
1082
1175
|
|
|
1083
1176
|
Returns
|
|
1084
1177
|
|
|
1085
|
-
|
|
1178
|
+
`{ missingInputs, notValid, valid, withMissingInputs }`
|
|
1086
1179
|
|
|
1087
1180
|
#### Method toBinary
|
|
1088
1181
|
|
|
@@ -1096,6 +1189,21 @@ Returns
|
|
|
1096
1189
|
|
|
1097
1190
|
A binary array representing the BEEF
|
|
1098
1191
|
|
|
1192
|
+
#### Method toBinaryAtomic
|
|
1193
|
+
|
|
1194
|
+
Serialize this Beef as AtomicBEEF.
|
|
1195
|
+
|
|
1196
|
+
`txid` must exist and be the last transaction
|
|
1197
|
+
in sorted (dependency) order.
|
|
1198
|
+
|
|
1199
|
+
```ts
|
|
1200
|
+
toBinaryAtomic(txid: string)
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
Returns
|
|
1204
|
+
|
|
1205
|
+
serialized contents of this Beef with AtomicBEEF prefix.
|
|
1206
|
+
|
|
1099
1207
|
#### Method toHex
|
|
1100
1208
|
|
|
1101
1209
|
Returns a hex string representing the serialized BEEF
|
|
@@ -1118,6 +1226,14 @@ Returns
|
|
|
1118
1226
|
|
|
1119
1227
|
Summary of `Beef` contents as multi-line string.
|
|
1120
1228
|
|
|
1229
|
+
#### Method toWriter
|
|
1230
|
+
|
|
1231
|
+
Serializes this data to `writer`
|
|
1232
|
+
|
|
1233
|
+
```ts
|
|
1234
|
+
toWriter(writer: Writer)
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1121
1237
|
#### Method trimKnownTxids
|
|
1122
1238
|
|
|
1123
1239
|
Ensure that all the txids in `knownTxids` are txidOnly
|
|
@@ -1219,8 +1335,8 @@ export default class Transaction {
|
|
|
1219
1335
|
id(enc: "hex"): string;
|
|
1220
1336
|
id(enc?: "hex"): number[] | string
|
|
1221
1337
|
async verify(chainTracker: ChainTracker | "scripts only" = defaultChainTracker(), feeModel?: FeeModel): Promise<boolean>
|
|
1222
|
-
toBEEF(): number[]
|
|
1223
|
-
toAtomicBEEF(): number[]
|
|
1338
|
+
toBEEF(allowPartial?: boolean): number[]
|
|
1339
|
+
toAtomicBEEF(allowPartial?: boolean): number[]
|
|
1224
1340
|
}
|
|
1225
1341
|
```
|
|
1226
1342
|
|
|
@@ -1554,25 +1670,43 @@ and then the BEEF data containing only the subject transaction and its dependenc
|
|
|
1554
1670
|
This format ensures that the BEEF structure is atomic and contains no unrelated transactions.
|
|
1555
1671
|
|
|
1556
1672
|
```ts
|
|
1557
|
-
toAtomicBEEF(): number[]
|
|
1673
|
+
toAtomicBEEF(allowPartial?: boolean): number[]
|
|
1558
1674
|
```
|
|
1559
1675
|
|
|
1560
1676
|
Returns
|
|
1561
1677
|
|
|
1562
1678
|
- The serialized Atomic BEEF structure.
|
|
1563
1679
|
|
|
1680
|
+
Argument Details
|
|
1681
|
+
|
|
1682
|
+
+ **allowPartial**
|
|
1683
|
+
+ If true, error will not be thrown if there are any missing sourceTransactions.
|
|
1684
|
+
|
|
1685
|
+
Throws
|
|
1686
|
+
|
|
1687
|
+
Error if there are any missing sourceTransactions unless `allowPartial` is true.
|
|
1688
|
+
|
|
1564
1689
|
#### Method toBEEF
|
|
1565
1690
|
|
|
1566
1691
|
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
1692
|
|
|
1568
1693
|
```ts
|
|
1569
|
-
toBEEF(): number[]
|
|
1694
|
+
toBEEF(allowPartial?: boolean): number[]
|
|
1570
1695
|
```
|
|
1571
1696
|
|
|
1572
1697
|
Returns
|
|
1573
1698
|
|
|
1574
1699
|
The serialized BEEF structure
|
|
1575
1700
|
|
|
1701
|
+
Argument Details
|
|
1702
|
+
|
|
1703
|
+
+ **allowPartial**
|
|
1704
|
+
+ If true, error will not be thrown if there are any missing sourceTransactions.
|
|
1705
|
+
|
|
1706
|
+
Throws
|
|
1707
|
+
|
|
1708
|
+
Error if there are any missing sourceTransactions unless `allowPartial` is true.
|
|
1709
|
+
|
|
1576
1710
|
#### Method toBinary
|
|
1577
1711
|
|
|
1578
1712
|
Converts the transaction to a binary array format.
|
|
@@ -1716,6 +1850,7 @@ export class BeefParty extends Beef {
|
|
|
1716
1850
|
getKnownTxidsForParty(party: string): string[]
|
|
1717
1851
|
getTrimmedBeefForParty(party: string): Beef
|
|
1718
1852
|
addKnownTxidsForParty(party: string, knownTxids: string[])
|
|
1853
|
+
mergeBeefFromParty(party: string, beef: number[] | Beef)
|
|
1719
1854
|
}
|
|
1720
1855
|
```
|
|
1721
1856
|
|
|
@@ -1794,6 +1929,18 @@ Returns
|
|
|
1794
1929
|
|
|
1795
1930
|
`true` if `party` has already beed added to this `BeefParty`.
|
|
1796
1931
|
|
|
1932
|
+
#### Method mergeBeefFromParty
|
|
1933
|
+
|
|
1934
|
+
Merge a `beef` received from a specific `party`.
|
|
1935
|
+
|
|
1936
|
+
Updates this `BeefParty` to track all the txids
|
|
1937
|
+
corresponding to transactions for which `party`
|
|
1938
|
+
has raw transaction and validity proof data.
|
|
1939
|
+
|
|
1940
|
+
```ts
|
|
1941
|
+
mergeBeefFromParty(party: string, beef: number[] | Beef)
|
|
1942
|
+
```
|
|
1943
|
+
|
|
1797
1944
|
</details>
|
|
1798
1945
|
|
|
1799
1946
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
@@ -1925,6 +2072,7 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
|
|
|
1925
2072
|
|
|
1926
2073
|
| |
|
|
1927
2074
|
| --- |
|
|
2075
|
+
| [ATOMIC_BEEF](#variable-atomic_beef) |
|
|
1928
2076
|
| [BEEF_MAGIC](#variable-beef_magic) |
|
|
1929
2077
|
| [BEEF_MAGIC_TXID_ONLY_EXTENSION](#variable-beef_magic_txid_only_extension) |
|
|
1930
2078
|
| [BEEF_MAGIC_V2](#variable-beef_magic_v2) |
|
|
@@ -1960,3 +2108,12 @@ BEEF_MAGIC_TXID_ONLY_EXTENSION = 4022206465
|
|
|
1960
2108
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
1961
2109
|
|
|
1962
2110
|
---
|
|
2111
|
+
### Variable: ATOMIC_BEEF
|
|
2112
|
+
|
|
2113
|
+
```ts
|
|
2114
|
+
ATOMIC_BEEF = 16843009
|
|
2115
|
+
```
|
|
2116
|
+
|
|
2117
|
+
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
2118
|
+
|
|
2119
|
+
---
|
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
|
|
@@ -305,11 +396,10 @@ export class Beef {
|
|
|
305
396
|
}
|
|
306
397
|
|
|
307
398
|
/**
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const writer = new Writer()
|
|
399
|
+
* Serializes this data to `writer`
|
|
400
|
+
* @param writer
|
|
401
|
+
*/
|
|
402
|
+
toWriter (writer: Writer) {
|
|
313
403
|
writer.writeUInt32LE(this.magic)
|
|
314
404
|
|
|
315
405
|
writer.writeVarIntNum(this.bumps.length)
|
|
@@ -321,7 +411,38 @@ export class Beef {
|
|
|
321
411
|
for (const tx of this.txs) {
|
|
322
412
|
tx.toWriter(writer, this.magic)
|
|
323
413
|
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Returns a binary array representing the serialized BEEF
|
|
418
|
+
* @returns A binary array representing the BEEF
|
|
419
|
+
*/
|
|
420
|
+
toBinary (): number[] {
|
|
421
|
+
const writer = new Writer()
|
|
422
|
+
this.toWriter(writer)
|
|
423
|
+
return writer.toArray()
|
|
424
|
+
}
|
|
324
425
|
|
|
426
|
+
/**
|
|
427
|
+
* Serialize this Beef as AtomicBEEF.
|
|
428
|
+
*
|
|
429
|
+
* `txid` must exist and be the last transaction
|
|
430
|
+
* in sorted (dependency) order.
|
|
431
|
+
*
|
|
432
|
+
* @param txid
|
|
433
|
+
* @returns serialized contents of this Beef with AtomicBEEF prefix.
|
|
434
|
+
*/
|
|
435
|
+
toBinaryAtomic(txid: string) {
|
|
436
|
+
this.sortTxs()
|
|
437
|
+
const tx = this.findTxid(txid)
|
|
438
|
+
if (!tx)
|
|
439
|
+
throw new Error(`${txid} does not exist in this Beef`)
|
|
440
|
+
if (this.txs[this.txs.length - 1] !== tx)
|
|
441
|
+
throw new Error(`${txid} is not the last transaction in this Beef`)
|
|
442
|
+
const writer = new Writer()
|
|
443
|
+
writer.writeUInt32LE(ATOMIC_BEEF)
|
|
444
|
+
writer.write(toArray(txid, 'hex'))
|
|
445
|
+
this.toWriter(writer)
|
|
325
446
|
return writer.toArray()
|
|
326
447
|
}
|
|
327
448
|
|
|
@@ -334,7 +455,13 @@ export class Beef {
|
|
|
334
455
|
}
|
|
335
456
|
|
|
336
457
|
static fromReader (br: Reader): Beef {
|
|
337
|
-
|
|
458
|
+
let version = br.readUInt32LE()
|
|
459
|
+
let atomicTxid: string | undefined = undefined
|
|
460
|
+
if (version === ATOMIC_BEEF) {
|
|
461
|
+
// Skip the txid and re-read the BEEF version
|
|
462
|
+
atomicTxid = toHex(br.read(32))
|
|
463
|
+
version = br.readUInt32LE()
|
|
464
|
+
}
|
|
338
465
|
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
466
|
const beef = new Beef(version === BEEF_MAGIC_V2 ? 'V2' : undefined)
|
|
340
467
|
const bumpsLength = br.readVarIntNum()
|
|
@@ -347,6 +474,7 @@ export class Beef {
|
|
|
347
474
|
const beefTx = BeefTx.fromReader(br, version)
|
|
348
475
|
beef.txs.push(beefTx)
|
|
349
476
|
}
|
|
477
|
+
beef.atomicTxid = atomicTxid
|
|
350
478
|
return beef
|
|
351
479
|
}
|
|
352
480
|
|
|
@@ -395,71 +523,104 @@ export class Beef {
|
|
|
395
523
|
}
|
|
396
524
|
|
|
397
525
|
/**
|
|
398
|
-
* Sort the `txs` by input txid dependency order
|
|
399
|
-
*
|
|
526
|
+
* Sort the `txs` by input txid dependency order:
|
|
527
|
+
* - Oldest Tx Anchored by Path
|
|
528
|
+
* - Newer Txs depending on Older parents
|
|
529
|
+
* - Newest Tx
|
|
530
|
+
*
|
|
531
|
+
* with proof (MerklePath) last, longest chain of dependencies first
|
|
532
|
+
*
|
|
533
|
+
* @returns `{ missingInputs, notValid, valid, withMissingInputs }`
|
|
400
534
|
*/
|
|
401
|
-
sortTxs ()
|
|
402
|
-
|
|
403
|
-
|
|
535
|
+
sortTxs ()
|
|
536
|
+
: {
|
|
537
|
+
missingInputs: string[],
|
|
538
|
+
notValid: string[],
|
|
539
|
+
valid: string[],
|
|
540
|
+
withMissingInputs: string[],
|
|
541
|
+
txidOnly: string[]
|
|
542
|
+
} {
|
|
543
|
+
// Hashtable of valid txids (with proof or all inputs chain to proof)
|
|
544
|
+
const validTxids: Record<string, boolean> = {}
|
|
545
|
+
|
|
546
|
+
// Hashtable of all transaction txids to transaction
|
|
404
547
|
const txidToTx: Record<string, BeefTx> = {}
|
|
405
548
|
|
|
549
|
+
// queue of unsorted transactions ...
|
|
550
|
+
let queue: BeefTx[] = []
|
|
551
|
+
|
|
552
|
+
// sorted transactions: hasProof to with longest dependency chain
|
|
553
|
+
const result: BeefTx[] = []
|
|
554
|
+
|
|
555
|
+
const txidOnly: BeefTx[] = []
|
|
556
|
+
|
|
406
557
|
for (const tx of this.txs) {
|
|
407
558
|
txidToTx[tx.txid] = tx
|
|
408
|
-
|
|
409
|
-
tx.
|
|
559
|
+
tx.isValid = tx.hasProof
|
|
560
|
+
if (tx.isValid) {
|
|
561
|
+
validTxids[tx.txid] = true
|
|
562
|
+
result.push(tx)
|
|
563
|
+
} else if (tx.isTxidOnly)
|
|
564
|
+
txidOnly.push(tx)
|
|
565
|
+
else
|
|
566
|
+
queue.push(tx)
|
|
410
567
|
}
|
|
411
568
|
|
|
412
|
-
|
|
413
|
-
|
|
569
|
+
// Hashtable of unknown input txids used to fund transactions without their own proof.
|
|
570
|
+
const missingInputs: Record<string, boolean> = {}
|
|
571
|
+
// transactions with one or more missing inputs
|
|
572
|
+
const txsMissingInputs: BeefTx[] = []
|
|
573
|
+
|
|
574
|
+
const possiblyMissingInputs = queue
|
|
575
|
+
queue = []
|
|
576
|
+
|
|
577
|
+
for (const tx of possiblyMissingInputs) {
|
|
578
|
+
let hasMissingInput = false
|
|
579
|
+
if (!tx.isValid) {
|
|
414
580
|
// For all the unproven transactions,
|
|
415
581
|
// link their inputs that exist in this beef,
|
|
416
582
|
// make a note of missing inputs.
|
|
417
583
|
for (const inputTxid of tx.inputTxids) {
|
|
418
|
-
if (!txidToTx[inputTxid]) {
|
|
584
|
+
if (!txidToTx[inputTxid]) {
|
|
585
|
+
missingInputs[inputTxid] = true
|
|
586
|
+
hasMissingInput = true
|
|
587
|
+
}
|
|
419
588
|
}
|
|
420
589
|
}
|
|
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) {
|
|
590
|
+
if (hasMissingInput)
|
|
591
|
+
txsMissingInputs.push(tx)
|
|
592
|
+
else
|
|
439
593
|
queue.push(tx)
|
|
440
|
-
}
|
|
441
594
|
}
|
|
442
|
-
|
|
595
|
+
|
|
596
|
+
// As long as we have unsorted transactions...
|
|
443
597
|
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
|
-
}
|
|
598
|
+
const oldQueue = queue
|
|
599
|
+
queue = []
|
|
600
|
+
for (const tx of oldQueue) {
|
|
601
|
+
if (tx.inputTxids.every(txid => validTxids[txid])) {
|
|
602
|
+
validTxids[tx.txid] = true
|
|
603
|
+
result.push(tx)
|
|
604
|
+
} else
|
|
605
|
+
queue.push(tx)
|
|
458
606
|
}
|
|
607
|
+
if (oldQueue.length === queue.length)
|
|
608
|
+
break;
|
|
459
609
|
}
|
|
460
|
-
this.txs = result
|
|
461
610
|
|
|
462
|
-
|
|
611
|
+
// transactions that don't have proofs and don't chain to proofs
|
|
612
|
+
const txsNotValid = queue
|
|
613
|
+
|
|
614
|
+
// New order of txs is sorted, unsortable, txidOnly (no raw transaction)
|
|
615
|
+
this.txs = result.concat(queue).concat(txidOnly).concat(txsMissingInputs)
|
|
616
|
+
|
|
617
|
+
return {
|
|
618
|
+
missingInputs: Object.keys(missingInputs),
|
|
619
|
+
notValid: txsNotValid.map(tx => tx.txid),
|
|
620
|
+
valid: Object.keys(validTxids),
|
|
621
|
+
withMissingInputs: txsMissingInputs.map(tx => tx.txid),
|
|
622
|
+
txidOnly: txidOnly.map(tx => tx.txid)
|
|
623
|
+
}
|
|
463
624
|
}
|
|
464
625
|
|
|
465
626
|
/**
|
|
@@ -488,6 +649,14 @@ export class Beef {
|
|
|
488
649
|
// TODO: bumps could be trimmed to eliminate unreferenced proofs.
|
|
489
650
|
}
|
|
490
651
|
|
|
652
|
+
/**
|
|
653
|
+
* @returns array of transaction txids that either have a proof or whose inputs chain back to a proven transaction.
|
|
654
|
+
*/
|
|
655
|
+
getValidTxids() : string[] {
|
|
656
|
+
const r = this.sortTxs()
|
|
657
|
+
return r.valid
|
|
658
|
+
}
|
|
659
|
+
|
|
491
660
|
/**
|
|
492
661
|
* @returns Summary of `Beef` contents as multi-line string.
|
|
493
662
|
*/
|
|
@@ -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
|