@smartledger/bsv 9.11.2 → 9.12.0
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/CHANGELOG.md +52 -0
- package/README.md +19 -19
- package/bsv-gdaf.min.js +50 -50
- package/bsv-smartcontract.min.js +1 -1
- package/bsv.bundle.js +50 -50
- package/bsv.d.ts +35 -2
- package/bsv.min.js +50 -50
- package/docs/AUDIT_SCOPE.md +7 -7
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/audit-rfq/cure53.txt +2 -2
- package/docs/audit-rfq/ncc-group.txt +2 -2
- package/docs/audit-rfq/trail-of-bits.txt +2 -2
- package/docs/getting-started/INSTALLATION.md +23 -23
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/lib/gdaf/smartledger-anchor.js +4 -1
- package/lib/notaryhash/index.js +64 -12
- package/lib/spv/headerchain.js +31 -10
- package/lib/spv/index.js +1 -0
- package/lib/spv/merkleproof.js +168 -16
- package/package.json +1 -1
- package/version.js +1 -1
package/lib/spv/merkleproof.js
CHANGED
|
@@ -16,10 +16,132 @@
|
|
|
16
16
|
* working hash" — Bitcoin's odd-node rule, per the TSC Merkle Proof standard.
|
|
17
17
|
*/
|
|
18
18
|
var BlockHeader = require('../block/blockheader')
|
|
19
|
+
var BN = require('../crypto/bn')
|
|
19
20
|
var Hash = require('../crypto/hash')
|
|
20
21
|
|
|
21
22
|
var HEX32 = /^[0-9a-fA-F]{64}$/
|
|
22
23
|
|
|
24
|
+
// 2^256, for turning a target into the work it represents.
|
|
25
|
+
var TWO_256 = BlockHeader.Constants.LARGEST_HASH
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The easiest target a header may declare and still be believed: difficulty 1,
|
|
29
|
+
* `0x1d00ffff`, which is the proof-of-work limit of both mainnet and testnet. Regtest
|
|
30
|
+
* headers declare `0x207fffff` and are only checked when a caller asks for that limit.
|
|
31
|
+
*/
|
|
32
|
+
var POW_LIMIT_BITS = 0x1d00ffff
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A compact target (`bits`) as a BN, or null if the encoding is one no header may use.
|
|
36
|
+
*
|
|
37
|
+
* Transcribed from Bitcoin's arith_uint256::SetCompact and CheckProofOfWork: a negative
|
|
38
|
+
* target, a zero target and an overflowing target are all refused. `getTargetDifficulty`
|
|
39
|
+
* on BlockHeader implements none of those rules and shifts the wrong way for a size
|
|
40
|
+
* below 4, so it is not used here.
|
|
41
|
+
*
|
|
42
|
+
* @param {Number} bits
|
|
43
|
+
* @returns {BN|null}
|
|
44
|
+
*/
|
|
45
|
+
function targetFromBits (bits) {
|
|
46
|
+
var size = bits >>> 24
|
|
47
|
+
var word = bits & 0x007fffff
|
|
48
|
+
var target
|
|
49
|
+
// SetCompact shifts BEFORE its zero, negative and overflow tests, so a small size whose
|
|
50
|
+
// shifted word is zero gives a target of zero and is refused. Testing the raw word
|
|
51
|
+
// instead returned a target of 0, and 0 made workFromTarget report 2^256 of work.
|
|
52
|
+
if (size <= 3) {
|
|
53
|
+
word = word >>> (8 * (3 - size))
|
|
54
|
+
target = new BN(word)
|
|
55
|
+
} else {
|
|
56
|
+
target = new BN(word).shln(8 * (size - 3))
|
|
57
|
+
}
|
|
58
|
+
if (word === 0) return null // zero: no hash can be at or below it
|
|
59
|
+
if ((bits & 0x00800000) !== 0) return null // negative
|
|
60
|
+
if ((size > 34) || (word > 0xff && size > 33) || (word > 0xffff && size > 32)) return null
|
|
61
|
+
return target
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The work a target represents: 2^256 / (target + 1). Difficulty 1 is about 4.295e9. */
|
|
65
|
+
function workFromTarget (target) {
|
|
66
|
+
return TWO_256.div(target.add(new BN(1)))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function targetLimit (powLimit) {
|
|
70
|
+
if (powLimit === undefined || powLimit === null) powLimit = POW_LIMIT_BITS
|
|
71
|
+
// A BN is taken as the target itself. The node's limit is a uint256 (2^224 - 1 on
|
|
72
|
+
// mainnet), and for compact bits the two agree, because 0x1d00ffff is the largest
|
|
73
|
+
// compact target at or below it; a BN limit can sit anywhere between them.
|
|
74
|
+
if (BN.isBN(powLimit)) return powLimit
|
|
75
|
+
var bits
|
|
76
|
+
if (typeof powLimit === 'string') {
|
|
77
|
+
// parseInt stops at the first character it dislikes: '1d00ffzz' would silently
|
|
78
|
+
// become 0x1d00ff, a different limit.
|
|
79
|
+
if (!/^(0x)?[0-9a-fA-F]{1,8}$/.test(powLimit)) {
|
|
80
|
+
throw new Error('powLimit must be compact bits as hex, a uint32, or a BN target, not ' +
|
|
81
|
+
JSON.stringify(powLimit))
|
|
82
|
+
}
|
|
83
|
+
bits = parseInt(powLimit.replace(/^0x/, ''), 16)
|
|
84
|
+
} else if (typeof powLimit === 'number' && Number.isInteger(powLimit) &&
|
|
85
|
+
powLimit >= 0 && powLimit <= 0xffffffff) {
|
|
86
|
+
bits = powLimit
|
|
87
|
+
} else {
|
|
88
|
+
throw new Error('powLimit must be compact bits as hex, a uint32, or a BN target, not ' +
|
|
89
|
+
JSON.stringify(powLimit))
|
|
90
|
+
}
|
|
91
|
+
var target = targetFromBits(bits)
|
|
92
|
+
if (!target) throw new Error('powLimit is not a usable compact target: ' + powLimit)
|
|
93
|
+
return target
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* `minWork` as a BN, refusing anything bn.js would read loosely. It parses `1e21` as
|
|
98
|
+
* 23521, `'4.3e9'` as 4 and `'abc'` as 1122, so a floor set from a Number near the
|
|
99
|
+
* chain's work would silently become a few thousand — a floor nothing fails.
|
|
100
|
+
*/
|
|
101
|
+
function minWorkBN (minWork) {
|
|
102
|
+
if (BN.isBN(minWork)) return minWork
|
|
103
|
+
if (typeof minWork === 'number') {
|
|
104
|
+
if (!Number.isSafeInteger(minWork) || minWork < 0) {
|
|
105
|
+
throw new Error('minWork as a number must be a non-negative safe integer; for larger ' +
|
|
106
|
+
'floors pass a decimal string or a BN, not ' + minWork)
|
|
107
|
+
}
|
|
108
|
+
return new BN(String(minWork), 10)
|
|
109
|
+
}
|
|
110
|
+
if (typeof minWork === 'string' && /^[0-9]+$/.test(minWork)) return new BN(minWork, 10)
|
|
111
|
+
throw new Error('minWork must be a non-negative integer, a decimal string or a BN, not ' +
|
|
112
|
+
JSON.stringify(minWork))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Exactly 80 bytes of header, and a BlockHeader parsed from those same bytes.
|
|
117
|
+
*
|
|
118
|
+
* Every check reads this one snapshot, so a caller cannot pass an object whose stated
|
|
119
|
+
* merkle root differs from the bytes its hash is taken over. An object that merely
|
|
120
|
+
* states a root is refused outright: believing it is the trust the protocol removes.
|
|
121
|
+
*/
|
|
122
|
+
function headerSnapshot (header) {
|
|
123
|
+
var buf
|
|
124
|
+
if (Buffer.isBuffer(header)) buf = header
|
|
125
|
+
else if (typeof header === 'string') {
|
|
126
|
+
// Buffer.from stops at the first non-hex character, so 160 good characters followed
|
|
127
|
+
// by junk would arrive as a clean 80 bytes.
|
|
128
|
+
if (!/^[0-9a-fA-F]{160}$/.test(header)) {
|
|
129
|
+
throw new Error('a block header as hex must be exactly 160 hex characters')
|
|
130
|
+
}
|
|
131
|
+
buf = Buffer.from(header, 'hex')
|
|
132
|
+
} else if (header instanceof BlockHeader) buf = header.toBuffer()
|
|
133
|
+
else if (header && typeof header.toBuffer === 'function') buf = header.toBuffer()
|
|
134
|
+
else {
|
|
135
|
+
throw new Error('a block header must be 80 bytes, as hex, a Buffer or a BlockHeader; ' +
|
|
136
|
+
'an object stating a merkleRoot is not a header and cannot be checked')
|
|
137
|
+
}
|
|
138
|
+
if (!Buffer.isBuffer(buf) || buf.length !== 80) {
|
|
139
|
+
throw new Error('a block header must be exactly 80 bytes, not ' +
|
|
140
|
+
(Buffer.isBuffer(buf) ? buf.length : typeof buf))
|
|
141
|
+
}
|
|
142
|
+
return BlockHeader.fromBuffer(buf)
|
|
143
|
+
}
|
|
144
|
+
|
|
23
145
|
function rev (buf) { return Buffer.from(buf).reverse() }
|
|
24
146
|
function toInternal (hex) { return rev(Buffer.from(hex, 'hex')) } // display -> internal LE
|
|
25
147
|
function toDisplay (buf) { return rev(buf).toString('hex') } // internal LE -> display
|
|
@@ -67,41 +189,71 @@ function verifyMerkleProof (proof) {
|
|
|
67
189
|
}
|
|
68
190
|
|
|
69
191
|
/**
|
|
70
|
-
* Verify a transaction is included in a block: branch -> root, root ==
|
|
71
|
-
* header
|
|
192
|
+
* Verify a transaction is included in a block: branch -> root, root == the root in the
|
|
193
|
+
* header's own bytes, and (unless disabled) the header's proof of work.
|
|
194
|
+
*
|
|
195
|
+
* A header's work is only as good as the target it declares, and a header declares its
|
|
196
|
+
* own. `hash <= target` alone therefore proves nothing: with `bits` of 0x2100ffff nearly
|
|
197
|
+
* every hash passes (65535 in 65536), so a forged header costs one attempt. The declared
|
|
198
|
+
* target is capped at
|
|
199
|
+
* `powLimit` — difficulty 1 by default, the limit of mainnet and testnet — and
|
|
200
|
+
* `minWork` can demand more.
|
|
72
201
|
*
|
|
73
|
-
* NOTE: this proves inclusion in the SUPPLIED header.
|
|
74
|
-
*
|
|
75
|
-
* the caller
|
|
202
|
+
* NOTE: this proves inclusion in the SUPPLIED header. Even a genuine, fully worked
|
|
203
|
+
* header can belong to an orphaned block; only a chain source knows which block is the
|
|
204
|
+
* chain's at a height. That check is the caller's, out of band.
|
|
76
205
|
*
|
|
77
|
-
* @param {object} params { txid, index, nodes, header, requirePow=true }
|
|
78
|
-
* header:
|
|
79
|
-
*
|
|
206
|
+
* @param {object} params { txid, index, nodes, header, requirePow=true, powLimit, minWork }
|
|
207
|
+
* header: a bsv.BlockHeader, an 80-byte Buffer, or 80-byte hex.
|
|
208
|
+
* powLimit: the easiest target a header may declare, as compact bits (number or hex
|
|
209
|
+
* string) or a BN target. Default 0x1d00ffff. Regtest needs 0x207fffff.
|
|
210
|
+
* minWork: minimum work the header must represent (number, decimal string or BN);
|
|
211
|
+
* 2^256 / (target + 1). Difficulty 1 is about 4.295e9.
|
|
212
|
+
* With `requirePow: false` the work checks are not performed at all: `targetAllowed` and
|
|
213
|
+
* `workSufficient` report true because nothing objected, and `powLimit`/`minWork` are not
|
|
214
|
+
* even read.
|
|
215
|
+
*
|
|
216
|
+
* @returns {{ valid:boolean, rootMatches:boolean, powValid:boolean, targetAllowed:boolean,
|
|
217
|
+
* workSufficient:boolean, work:string, merkleRoot:string, blockHash:string }}
|
|
80
218
|
*/
|
|
81
219
|
function verifyTxInclusion (params) {
|
|
82
|
-
var header = params.header
|
|
83
|
-
if (Buffer.isBuffer(header) || typeof header === 'string') {
|
|
84
|
-
header = BlockHeader.fromBuffer(Buffer.isBuffer(header) ? header : Buffer.from(header, 'hex'))
|
|
85
|
-
}
|
|
86
|
-
if (!header || !header.merkleRoot) throw new Error('a valid block header is required')
|
|
220
|
+
var header = headerSnapshot(params.header)
|
|
87
221
|
|
|
88
|
-
var headerRoot = toDisplay(header.merkleRoot) //
|
|
222
|
+
var headerRoot = toDisplay(header.merkleRoot) // the root as the header's own bytes give it
|
|
89
223
|
var computed = merkleRootFromBranch(params.txid, params.index, params.nodes)
|
|
90
224
|
var rootMatches = computed.toLowerCase() === headerRoot.toLowerCase()
|
|
91
225
|
|
|
92
226
|
var requirePow = params.requirePow !== false
|
|
93
|
-
var
|
|
227
|
+
var target = targetFromBits(header.bits)
|
|
228
|
+
var powValid = target !== null && new BN(header.id, 'hex').cmp(target) <= 0
|
|
229
|
+
var work = target === null ? new BN(0) : workFromTarget(target)
|
|
230
|
+
// Both are policy the caller supplies, so neither is read — nor rejected as malformed —
|
|
231
|
+
// when the work checks are off.
|
|
232
|
+
var targetAllowed = target !== null &&
|
|
233
|
+
(!requirePow || target.cmp(targetLimit(params.powLimit)) <= 0)
|
|
234
|
+
var workSufficient = !requirePow || params.minWork === undefined || params.minWork === null ||
|
|
235
|
+
work.cmp(minWorkBN(params.minWork)) >= 0
|
|
94
236
|
|
|
95
237
|
return {
|
|
96
|
-
valid: rootMatches && (!requirePow || powValid),
|
|
238
|
+
valid: rootMatches && (!requirePow || (powValid && targetAllowed && workSufficient)),
|
|
97
239
|
rootMatches: rootMatches,
|
|
98
240
|
powValid: powValid,
|
|
241
|
+
targetAllowed: targetAllowed,
|
|
242
|
+
workSufficient: workSufficient,
|
|
243
|
+
work: work.toString(10),
|
|
99
244
|
merkleRoot: computed,
|
|
100
245
|
blockHash: header.id
|
|
101
246
|
}
|
|
102
247
|
}
|
|
103
248
|
|
|
104
249
|
module.exports = {
|
|
250
|
+
POW_LIMIT_BITS: POW_LIMIT_BITS,
|
|
251
|
+
// Internal to lib/spv: headerchain applies the same cap. Not re-exported by lib/spv,
|
|
252
|
+
// because the public surface is deliberately hard to grow (see test/api_surface.js).
|
|
253
|
+
targetFromBits: targetFromBits,
|
|
254
|
+
targetLimit: targetLimit,
|
|
255
|
+
workFromTarget: workFromTarget,
|
|
256
|
+
headerSnapshot: headerSnapshot,
|
|
105
257
|
merkleRootFromBranch: merkleRootFromBranch,
|
|
106
258
|
verifyMerkleProof: verifyMerkleProof,
|
|
107
259
|
verifyTxInclusion: verifyTxInclusion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartledger/bsv",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.12.0",
|
|
4
4
|
"description": "Bitcoin SV library with an interpreter-verified script engine: OP_PUSH_TX covenants, BIP-143 preimage tooling, and consensus flags that match what miners actually enforce. Also ships DID:web / W3C VC-JWT credentials and the Legal Token Protocol.",
|
|
5
5
|
"author": "SmartLedger Technology <hello@smartledger.technology> (https://smartledger.technology)",
|
|
6
6
|
"homepage": "https://github.com/codenlighten/smartledger-bsv#readme",
|
package/version.js
CHANGED