@smartledger/bsv 4.1.0 → 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.
- package/CHANGELOG.md +68 -0
- package/README.md +217 -205
- package/bsv-covenant.min.js +8 -8
- package/bsv-gdaf.min.js +9 -9
- package/bsv-ltp.min.js +9 -9
- package/bsv-smartcontract.min.js +9 -9
- package/bsv.bundle.js +9 -9
- package/bsv.min.js +8 -8
- package/docs/COVENANT_DEVELOPMENT_RESOLVED.md +2 -2
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +25 -25
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/lib/smart_contract/covenant_helpers.js +118 -0
- package/lib/smart_contract/index.js +30 -1
- package/lib/smart_contract/locks.js +101 -0
- package/lib/smart_contract/pels.js +62 -0
- package/lib/smart_contract/pushtx.js +138 -0
- package/lib/smart_contract/token.js +95 -0
- package/package.json +1 -1
|
@@ -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.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",
|