@smartledger/bsv 4.1.0 → 4.4.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.
@@ -0,0 +1,61 @@
1
+ 'use strict'
2
+ /**
3
+ * SmartContract.PELS — a Perpetually Enforcing Locking Script (nChain WP1605).
4
+ *
5
+ * A covenant that forces every spend to recreate itself: the single output must
6
+ * pay (inputValue - fee) back into the SAME locking script. Coins never leave
7
+ * the covenant — they flow forward through identical UTXOs, shrinking by the fee.
8
+ *
9
+ * It avoids self-hash circularity by reading its own script out of the
10
+ * authenticated BIP-143 preimage's `scriptCode` field. With layout
11
+ * version(4) hashPrevouts(32) hashSequence(32) outpoint(36) = 104
12
+ * scriptCode(varint||script)
13
+ * value(8) nSequence(4) hashOutputs(32) nLockTime(4) sighash(4) = 52 (tail)
14
+ * the slice preimage[104 : len-52] is exactly the `scriptlen||script` half of a
15
+ * TxOut, so the next output is `<newValue:8-LE> || preimage[104:len-52]`.
16
+ *
17
+ * Requires post-Genesis limits (SmartContract.enableGenesis()).
18
+ */
19
+
20
+ var bsv = require('../..')
21
+ var P = require('./pushtx')
22
+ var H = require('./covenant_helpers')
23
+
24
+ var Script = bsv.Script
25
+ var Opcode = bsv.Opcode
26
+ var n = H.scriptNum
27
+
28
+ /**
29
+ * @param {number} fee satoshis deducted at each hop.
30
+ * @returns {Script} the perpetual locking script.
31
+ */
32
+ function pelsCovenant (fee) {
33
+ var s = new Script()
34
+
35
+ // 1. authenticate the pushed preimage
36
+ s.add(Opcode.OP_DUP)
37
+ P.pushTxCore(s)
38
+ s.add(Opcode.OP_VERIFY) // stack: [preimage]
39
+
40
+ // 2. scriptChunk = preimage[104 : len-52]
41
+ s.add(Opcode.OP_DUP)
42
+ s.add(n(104)).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
43
+ s.add(Opcode.OP_SIZE).add(n(52)).add(Opcode.OP_SUB).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP)
44
+
45
+ // 3. value (8B @ offsetFromEnd 52); newValue = value - fee, as 8-byte LE
46
+ s.add(Opcode.OP_OVER)
47
+ s.add(n(52)).add(Opcode.OP_RIGHT).add(n(8)).add(Opcode.OP_LEFT) // last 52 bytes, first 8 = value
48
+ s.add(Opcode.OP_BIN2NUM).add(n(fee)).add(Opcode.OP_SUB)
49
+ s.add(n(8)).add(Opcode.OP_NUM2BIN)
50
+
51
+ // 4. nextOutput = newValue8 || scriptChunk ; require HASH256(it) == hashOutputs
52
+ s.add(Opcode.OP_SWAP).add(Opcode.OP_CAT)
53
+ s.add(Opcode.OP_HASH256)
54
+ s.add(Opcode.OP_SWAP)
55
+ P.extractHashOutputs(s)
56
+ s.add(Opcode.OP_EQUAL)
57
+
58
+ return s
59
+ }
60
+
61
+ module.exports = { pelsCovenant: pelsCovenant }
@@ -0,0 +1,155 @@
1
+ 'use strict'
2
+ /**
3
+ * SmartContract.PushTx — a correct, interpreter-verified OP_PUSH_TX (nChain
4
+ * WP1605) for Bitcoin SV.
5
+ *
6
+ * The locking script GENERATES an ECDSA signature in-script from a preimage the
7
+ * spender pushed, then OP_CHECKSIG verifies it against a fixed public key.
8
+ * OP_CHECKSIG only passes if the message it derives internally (the genuine
9
+ * BIP-143 sighash of THIS spend) equals HASH256(preimage) — so a passing check
10
+ * proves the pushed preimage IS this transaction, letting a script read and
11
+ * constrain its own spending transaction.
12
+ *
13
+ * Optimal parameters: private key a = 1, ephemeral k = 1 => r = Gx,
14
+ * s = (e + Gx) mod n, pubkey P = 02||Gx, e = HASH256(preimage).
15
+ *
16
+ * Requires post-Genesis limits — call SmartContract.enableGenesis() (a.k.a
17
+ * Interpreter.useGenesisLimits()) before verifying these scripts.
18
+ */
19
+
20
+ var bsv = require('../..')
21
+ var BN = require('../crypto/bn')
22
+ var Hash = require('../crypto/hash')
23
+ var H = require('./covenant_helpers')
24
+
25
+ var Script = bsv.Script
26
+ var Opcode = bsv.Opcode
27
+ var SIGHASH = H.SIGHASH
28
+ var scriptNum = H.scriptNum
29
+
30
+ // secp256k1 constants (big-endian)
31
+ var Gx = Buffer.from('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 'hex')
32
+ var N = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16)
33
+ var PUBKEY = Buffer.concat([Buffer.from([0x02]), Gx]) // compressed P = G (y even)
34
+
35
+ // script-number (little-endian) forms
36
+ var gxLe = Buffer.from(Gx).reverse() // 32B, top 0x79 => positive
37
+ var N_LE = Buffer.concat([Buffer.from(N.toBuffer()).reverse(), Buffer.from([0x00])]) // 33B positive
38
+
39
+ // fixed DER prefix: SEQUENCE(0x44) INTEGER(0x20) r=Gx INTEGER(0x20)
40
+ var DER_PREFIX = Buffer.concat([Buffer.from([0x30, 0x44, 0x02, 0x20]), Gx, Buffer.from([0x02, 0x20])])
41
+ var SIGHASH_BYTE = Buffer.from([SIGHASH]) // 0x41
42
+
43
+ /** Reverse a fixed n-byte buffer on top of the stack (big-endian <-> little-endian). */
44
+ function reverseBytes (script, n) {
45
+ var i
46
+ for (i = 0; i < n - 1; i++) script.add(Opcode.OP_1).add(Opcode.OP_SPLIT)
47
+ for (i = 0; i < n - 1; i++) script.add(Opcode.OP_SWAP).add(Opcode.OP_CAT)
48
+ return script
49
+ }
50
+
51
+ /**
52
+ * Append the in-script signature generator + verifier ("PUSH_TX core").
53
+ * Pre: top of stack = preimage. Post: top = OP_CHECKSIG result.
54
+ */
55
+ function pushTxCore (script) {
56
+ script.add(Opcode.OP_HASH256) // z = HASH256(preimage), 32B BE
57
+ reverseBytes(script, 32) // -> little-endian
58
+ script.add(Buffer.from([0x00])).add(Opcode.OP_CAT).add(Opcode.OP_BIN2NUM) // e (positive)
59
+ script.add(gxLe).add(Opcode.OP_ADD) // e + Gx
60
+ script.add(N_LE).add(Opcode.OP_MOD) // s = (e + Gx) mod n
61
+ script.add(scriptNum(32)).add(Opcode.OP_NUM2BIN) // s -> 32-byte LE
62
+ reverseBytes(script, 32) // -> big-endian (DER INTEGER body) ; stack: [s_be]
63
+ // Build the DER signature and the pubkey from a SINGLE Gx push (Gx is both the
64
+ // r-value inside the DER prefix and the body of the 02||Gx pubkey). Sharing it
65
+ // via the altstack saves a 32-byte constant vs. embedding Gx twice.
66
+ script.add(Gx).add(Opcode.OP_DUP) // [s_be, Gx, Gx]
67
+ script.add(Buffer.from([0x02])).add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // pubkey = 02||Gx
68
+ script.add(Opcode.OP_TOALTSTACK) // park pubkey ; [s_be, Gx]
69
+ script.add(Buffer.from([0x30, 0x44, 0x02, 0x20])).add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // 30440220||Gx
70
+ script.add(Buffer.from([0x02, 0x20])).add(Opcode.OP_CAT) // ||0220 => DER prefix
71
+ script.add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // DER prefix || s_be
72
+ script.add(SIGHASH_BYTE).add(Opcode.OP_CAT) // || sighash flag => full DER sig
73
+ script.add(Opcode.OP_FROMALTSTACK).add(Opcode.OP_CHECKSIG) // pubkey ; verify against P = G
74
+ return script
75
+ }
76
+
77
+ /** Bare authenticator script: unlock with the (grindable) preimage. */
78
+ function authenticator () {
79
+ return pushTxCore(new Script())
80
+ }
81
+
82
+ /** Extract the committed hashOutputs (item 9, offsetFromEnd 40, len 32) from a preimage on-stack. */
83
+ function extractHashOutputs (script) {
84
+ // last 40 bytes, then the first 32 of those = hashOutputs. (BSV string opcodes.)
85
+ script.add(scriptNum(40)).add(Opcode.OP_RIGHT).add(scriptNum(32)).add(Opcode.OP_LEFT)
86
+ return script
87
+ }
88
+
89
+ /** BIP-143 hashOutputs (SIGHASH_ALL) for a set of Transaction.Output objects. */
90
+ function hashOutputs (outputs) {
91
+ var ser = Buffer.concat(outputs.map(function (o) { return o.toBufferWriter().toBuffer() }))
92
+ return Hash.sha256sha256(ser)
93
+ }
94
+
95
+ /**
96
+ * Value/output covenant: the spend is valid only if its outputs hash to
97
+ * `expectedHashOutputs` — coins can only go where the covenant says.
98
+ */
99
+ function valueCovenant (expectedHashOutputs) {
100
+ var script = new Script().add(Opcode.OP_DUP)
101
+ pushTxCore(script)
102
+ script.add(Opcode.OP_VERIFY)
103
+ extractHashOutputs(script)
104
+ script.add(Buffer.from(expectedHashOutputs)).add(Opcode.OP_EQUAL)
105
+ return script
106
+ }
107
+
108
+ // floor(n/2) — the canonical low-S boundary (matches Signature.hasLowS()).
109
+ var HALF_N = new BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0', 'hex')
110
+
111
+ /**
112
+ * Compute s = (HASH256(preimage)+Gx) mod n; return its 32-byte BE form, or null
113
+ * if the resulting signature would not be a clean, CANONICAL (low-S) DER.
114
+ * Requiring s <= n/2 makes the in-script signature non-malleable and standard,
115
+ * so it passes nodes enforcing SCRIPT_VERIFY_LOW_S — at zero script-size cost
116
+ * (the burden is on the spender's grind, not extra opcodes).
117
+ */
118
+ function sFromPreimage (preimage) {
119
+ var z = Hash.sha256sha256(preimage)
120
+ var s = new BN(z).add(new BN(Gx)).mod(N)
121
+ if (s.gt(HALF_N)) return null // enforce low-S (canonical / non-malleable)
122
+ var sBE = s.toBuffer('be', 32)
123
+ return (sBE[0] >= 0x01) ? sBE : null // s <= n/2 already guarantees sBE[0] <= 0x7f
124
+ }
125
+
126
+ /**
127
+ * Grind the spend tx (vary nLockTime) until the in-script signature is a clean
128
+ * fixed-length DER. Mutates spend.nLockTime; returns { preimage, tries }.
129
+ */
130
+ function grind (spend, inputIndex, lockingScript, satoshis, maxTries) {
131
+ maxTries = maxTries || 5000
132
+ for (var t = 0; t < maxTries; t++) {
133
+ spend.nLockTime = t
134
+ var preimage = H.rawPreimage(spend, inputIndex, lockingScript, satoshis)
135
+ if (sFromPreimage(preimage)) return { preimage: preimage, tries: t + 1 }
136
+ }
137
+ throw new Error('OP_PUSH_TX grind failed after ' + maxTries + ' tries')
138
+ }
139
+
140
+ module.exports = {
141
+ Gx: Gx,
142
+ N: N,
143
+ PUBKEY: PUBKEY,
144
+ gxLe: gxLe,
145
+ N_LE: N_LE,
146
+ DER_PREFIX: DER_PREFIX,
147
+ reverseBytes: reverseBytes,
148
+ pushTxCore: pushTxCore,
149
+ authenticator: authenticator,
150
+ extractHashOutputs: extractHashOutputs,
151
+ hashOutputs: hashOutputs,
152
+ valueCovenant: valueCovenant,
153
+ sFromPreimage: sFromPreimage,
154
+ grind: grind
155
+ }
@@ -0,0 +1,93 @@
1
+ 'use strict'
2
+ /**
3
+ * SmartContract.Token — a stateful ownership token (NFT) covenant.
4
+ *
5
+ * Carries mutable STATE in its own locking script and enforces a STATE-
6
+ * TRANSITION rule on every spend.
7
+ * State : 32-byte owner id = SHA256(ownerSecret).
8
+ * Transition rule: to spend, the owner reveals `ownerSecret`; the spend must
9
+ * recreate the SAME token code with a (possibly new) owner,
10
+ * paying value-fee forward. A transferable, perpetual NFT.
11
+ *
12
+ * Layout: <ownerHash:32> OP_DROP <CODE>. The leading push embeds the state so it
13
+ * rides inside `scriptCode`; OP_DROP discards the runtime copy because CODE reads
14
+ * the authoritative state from the authenticated preimage. Within the scriptCode
15
+ * chunk (varint||script, 3-byte varint for ~480B scripts):
16
+ * [0:4] = varint||0x20 (prefix), [4:36] = ownerHash, [36:] = 0x75||CODE (suffix)
17
+ * A transfer rebuilds the chunk as prefix || newOwnerHash || suffix.
18
+ *
19
+ * Unlocking script: <ownerSecret> <newOwnerHash> <preimage>.
20
+ * Requires post-Genesis limits (SmartContract.enableGenesis()).
21
+ */
22
+
23
+ var bsv = require('../..')
24
+ var Hash = require('../crypto/hash')
25
+ var P = require('./pushtx')
26
+ var H = require('./covenant_helpers')
27
+
28
+ var Script = bsv.Script
29
+ var Opcode = bsv.Opcode
30
+ var n = H.scriptNum
31
+
32
+ /** Build the token locking script for a given owner and per-hop fee. */
33
+ function ownershipToken (fee, ownerHash) {
34
+ if (ownerHash.length !== 32) throw new Error('ownerHash must be 32 bytes (SHA256)')
35
+ var s = new Script()
36
+ s.add(Buffer.from(ownerHash)) // <ownerHash:32>
37
+ s.add(Opcode.OP_DROP)
38
+
39
+ // CODE — stack at entry: [ownerSecret, newOwnerHash, preimage]
40
+ s.add(Opcode.OP_DUP); P.pushTxCore(s); s.add(Opcode.OP_VERIFY) // authenticate
41
+
42
+ // park value (8B @ offsetFromEnd 52) and hashOutputs (32B @ offsetFromEnd 40)
43
+ s.add(Opcode.OP_DUP)
44
+ .add(n(52)).add(Opcode.OP_RIGHT).add(n(8)).add(Opcode.OP_LEFT).add(Opcode.OP_TOALTSTACK) // alt:[value8]
45
+ s.add(Opcode.OP_DUP); P.extractHashOutputs(s); s.add(Opcode.OP_TOALTSTACK) // alt:[value8, hashOutputs]
46
+
47
+ // scriptChunk = preimage[104 : len-52] (consumes preimage)
48
+ s.add(n(104)).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
49
+ .add(Opcode.OP_SIZE).add(n(52)).add(Opcode.OP_SUB).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP)
50
+
51
+ // owner authorization: SHA256(ownerSecret) == ownerHash = chunk[4:36] (OP_SUBSTR)
52
+ s.add(Opcode.OP_DUP).add(n(4)).add(n(32)).add(Opcode.OP_SUBSTR)
53
+ s.add(n(3)).add(Opcode.OP_ROLL)
54
+ s.add(Opcode.OP_SHA256).add(Opcode.OP_EQUALVERIFY) // [newOwnerHash, scriptChunk]
55
+
56
+ // rebuild chunk with new owner: prefix(4) || newOwnerHash || suffix
57
+ s.add(n(4)).add(Opcode.OP_SPLIT).add(n(32)).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
58
+ s.add(Opcode.OP_TOALTSTACK) // park suffix
59
+ s.add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // prefix || newOwnerHash
60
+ s.add(Opcode.OP_FROMALTSTACK).add(Opcode.OP_CAT) // || suffix => nextChunk
61
+
62
+ // newValue = value - fee (8B LE); nextOutput = newValue || nextChunk
63
+ s.add(Opcode.OP_FROMALTSTACK) // hashOutputs
64
+ s.add(Opcode.OP_SWAP)
65
+ s.add(Opcode.OP_FROMALTSTACK) // value8
66
+ s.add(Opcode.OP_BIN2NUM).add(n(fee)).add(Opcode.OP_SUB).add(n(8)).add(Opcode.OP_NUM2BIN)
67
+ s.add(Opcode.OP_SWAP).add(Opcode.OP_CAT)
68
+ s.add(Opcode.OP_HASH256)
69
+ s.add(Opcode.OP_EQUAL)
70
+
71
+ var len = s.toBuffer().length
72
+ if (len <= 252 || len > 0xffff) {
73
+ throw new Error('token script length must use a 3-byte varint (253..65535); got ' + len)
74
+ }
75
+ return s
76
+ }
77
+
78
+ /** Owner identifier from a secret. */
79
+ function ownerId (secret) { return Hash.sha256(secret) }
80
+
81
+ /** Unlocking script for a transfer. */
82
+ function unlockTransfer (ownerSecret, newOwnerHash, preimage) {
83
+ return new Script()
84
+ .add(Buffer.from(ownerSecret))
85
+ .add(Buffer.from(newOwnerHash))
86
+ .add(Buffer.from(preimage))
87
+ }
88
+
89
+ module.exports = {
90
+ ownershipToken: ownershipToken,
91
+ ownerId: ownerId,
92
+ unlockTransfer: unlockTransfer
93
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartledger/bsv",
3
- "version": "4.1.0",
3
+ "version": "4.4.0",
4
4
  "description": "🚀 Complete Bitcoin SV development framework with legally-recognizable DID:web + W3C VC-JWT toolkit, Legal Token Protocol (LTP), Global Digital Attestation Framework (GDAF), StatusList2021 revocation, and 16 flexible loading options. Standards-based credentials with ES256/ES256K support, on-chain BSV anchoring, and comprehensive Bitcoin SV API. Perfect for legal tokens, verifiable credentials, DeFi, smart contracts, and secure Bitcoin applications.",
5
5
  "author": "SmartLedger Technology <hello@smartledger.technology> (https://smartledger.technology)",
6
6
  "homepage": "https://github.com/codenlighten/smartledger-bsv#readme",