@smartledger/bsv 4.0.1 → 4.2.1

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,62 @@
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(Opcode.OP_SIZE).add(n(52)).add(Opcode.OP_SUB).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
48
+ s.add(n(8)).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP)
49
+ s.add(Opcode.OP_BIN2NUM).add(n(fee)).add(Opcode.OP_SUB)
50
+ s.add(n(8)).add(Opcode.OP_NUM2BIN)
51
+
52
+ // 4. nextOutput = newValue8 || scriptChunk ; require HASH256(it) == hashOutputs
53
+ s.add(Opcode.OP_SWAP).add(Opcode.OP_CAT)
54
+ s.add(Opcode.OP_HASH256)
55
+ s.add(Opcode.OP_SWAP)
56
+ P.extractHashOutputs(s)
57
+ s.add(Opcode.OP_EQUAL)
58
+
59
+ return s
60
+ }
61
+
62
+ module.exports = { pelsCovenant: pelsCovenant }
@@ -0,0 +1,138 @@
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)
63
+ script.add(DER_PREFIX).add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // PREFIX || s
64
+ script.add(SIGHASH_BYTE).add(Opcode.OP_CAT) // || sighash flag => full DER sig
65
+ script.add(PUBKEY).add(Opcode.OP_CHECKSIG) // verify against P = G
66
+ return script
67
+ }
68
+
69
+ /** Bare authenticator script: unlock with the (grindable) preimage. */
70
+ function authenticator () {
71
+ return pushTxCore(new Script())
72
+ }
73
+
74
+ /** Extract the committed hashOutputs (item 9, offsetFromEnd 40, len 32) from a preimage on-stack. */
75
+ function extractHashOutputs (script) {
76
+ script.add(Opcode.OP_SIZE).add(scriptNum(40)).add(Opcode.OP_SUB)
77
+ .add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
78
+ .add(scriptNum(32)).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP)
79
+ return script
80
+ }
81
+
82
+ /** BIP-143 hashOutputs (SIGHASH_ALL) for a set of Transaction.Output objects. */
83
+ function hashOutputs (outputs) {
84
+ var ser = Buffer.concat(outputs.map(function (o) { return o.toBufferWriter().toBuffer() }))
85
+ return Hash.sha256sha256(ser)
86
+ }
87
+
88
+ /**
89
+ * Value/output covenant: the spend is valid only if its outputs hash to
90
+ * `expectedHashOutputs` — coins can only go where the covenant says.
91
+ */
92
+ function valueCovenant (expectedHashOutputs) {
93
+ var script = new Script().add(Opcode.OP_DUP)
94
+ pushTxCore(script)
95
+ script.add(Opcode.OP_VERIFY)
96
+ extractHashOutputs(script)
97
+ script.add(Buffer.from(expectedHashOutputs)).add(Opcode.OP_EQUAL)
98
+ return script
99
+ }
100
+
101
+ /** Compute s = (HASH256(preimage)+Gx) mod n; return its 32-byte BE form, or null if not a clean DER template. */
102
+ function sFromPreimage (preimage) {
103
+ var z = Hash.sha256sha256(preimage)
104
+ var s = new BN(z).add(new BN(Gx)).mod(N)
105
+ var sBE = s.toBuffer('be', 32)
106
+ return (sBE[0] >= 0x01 && sBE[0] <= 0x7f) ? sBE : null
107
+ }
108
+
109
+ /**
110
+ * Grind the spend tx (vary nLockTime) until the in-script signature is a clean
111
+ * fixed-length DER. Mutates spend.nLockTime; returns { preimage, tries }.
112
+ */
113
+ function grind (spend, inputIndex, lockingScript, satoshis, maxTries) {
114
+ maxTries = maxTries || 5000
115
+ for (var t = 0; t < maxTries; t++) {
116
+ spend.nLockTime = t
117
+ var preimage = H.rawPreimage(spend, inputIndex, lockingScript, satoshis)
118
+ if (sFromPreimage(preimage)) return { preimage: preimage, tries: t + 1 }
119
+ }
120
+ throw new Error('OP_PUSH_TX grind failed after ' + maxTries + ' tries')
121
+ }
122
+
123
+ module.exports = {
124
+ Gx: Gx,
125
+ N: N,
126
+ PUBKEY: PUBKEY,
127
+ gxLe: gxLe,
128
+ N_LE: N_LE,
129
+ DER_PREFIX: DER_PREFIX,
130
+ reverseBytes: reverseBytes,
131
+ pushTxCore: pushTxCore,
132
+ authenticator: authenticator,
133
+ extractHashOutputs: extractHashOutputs,
134
+ hashOutputs: hashOutputs,
135
+ valueCovenant: valueCovenant,
136
+ sFromPreimage: sFromPreimage,
137
+ grind: grind
138
+ }
@@ -0,0 +1,95 @@
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(Opcode.OP_SIZE).add(n(52)).add(Opcode.OP_SUB).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
45
+ .add(n(8)).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP).add(Opcode.OP_TOALTSTACK) // alt:[value8]
46
+ s.add(Opcode.OP_DUP); P.extractHashOutputs(s); s.add(Opcode.OP_TOALTSTACK) // alt:[value8, hashOutputs]
47
+
48
+ // scriptChunk = preimage[104 : len-52] (consumes preimage)
49
+ s.add(n(104)).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
50
+ .add(Opcode.OP_SIZE).add(n(52)).add(Opcode.OP_SUB).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP)
51
+
52
+ // owner authorization: SHA256(ownerSecret) == ownerHash (chunk[4:36])
53
+ s.add(Opcode.OP_DUP).add(n(4)).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
54
+ .add(n(32)).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP)
55
+ s.add(n(3)).add(Opcode.OP_ROLL)
56
+ s.add(Opcode.OP_SHA256).add(Opcode.OP_EQUALVERIFY) // [newOwnerHash, scriptChunk]
57
+
58
+ // rebuild chunk with new owner: prefix(4) || newOwnerHash || suffix
59
+ s.add(n(4)).add(Opcode.OP_SPLIT).add(n(32)).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
60
+ s.add(Opcode.OP_TOALTSTACK) // park suffix
61
+ s.add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // prefix || newOwnerHash
62
+ s.add(Opcode.OP_FROMALTSTACK).add(Opcode.OP_CAT) // || suffix => nextChunk
63
+
64
+ // newValue = value - fee (8B LE); nextOutput = newValue || nextChunk
65
+ s.add(Opcode.OP_FROMALTSTACK) // hashOutputs
66
+ s.add(Opcode.OP_SWAP)
67
+ s.add(Opcode.OP_FROMALTSTACK) // value8
68
+ s.add(Opcode.OP_BIN2NUM).add(n(fee)).add(Opcode.OP_SUB).add(n(8)).add(Opcode.OP_NUM2BIN)
69
+ s.add(Opcode.OP_SWAP).add(Opcode.OP_CAT)
70
+ s.add(Opcode.OP_HASH256)
71
+ s.add(Opcode.OP_EQUAL)
72
+
73
+ var len = s.toBuffer().length
74
+ if (len <= 252 || len > 0xffff) {
75
+ throw new Error('token script length must use a 3-byte varint (253..65535); got ' + len)
76
+ }
77
+ return s
78
+ }
79
+
80
+ /** Owner identifier from a secret. */
81
+ function ownerId (secret) { return Hash.sha256(secret) }
82
+
83
+ /** Unlocking script for a transfer. */
84
+ function unlockTransfer (ownerSecret, newOwnerHash, preimage) {
85
+ return new Script()
86
+ .add(Buffer.from(ownerSecret))
87
+ .add(Buffer.from(newOwnerHash))
88
+ .add(Buffer.from(preimage))
89
+ }
90
+
91
+ module.exports = {
92
+ ownershipToken: ownershipToken,
93
+ ownerId: ownerId,
94
+ unlockTransfer: unlockTransfer
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartledger/bsv",
3
- "version": "4.0.1",
3
+ "version": "4.2.1",
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",