@smartledger/bsv 4.2.1 → 4.5.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 +79 -0
- package/bsv-covenant.min.js +7 -7
- package/bsv-gdaf.min.js +8 -8
- package/bsv-ltp.min.js +8 -8
- package/bsv-smartcontract.min.js +8 -8
- package/bsv.bundle.js +8 -8
- package/bsv.d.ts +94 -0
- package/bsv.min.js +7 -7
- package/lib/script/interpreter.js +52 -2
- package/lib/smart_contract/covenant_helpers.js +3 -2
- package/lib/smart_contract/debugger.js +71 -0
- package/lib/smart_contract/dsl.js +129 -0
- package/lib/smart_contract/index.js +68 -55
- package/lib/smart_contract/pels.js +1 -2
- package/lib/smart_contract/pushtx.js +25 -8
- package/lib/smart_contract/token.js +3 -5
- package/package.json +2 -1
|
@@ -701,7 +701,7 @@ Interpreter.prototype.step = function () {
|
|
|
701
701
|
return true
|
|
702
702
|
}
|
|
703
703
|
break
|
|
704
|
-
|
|
704
|
+
|
|
705
705
|
// Monolith opcodes are now enabled by default in SmartLedger BSV
|
|
706
706
|
// These were activated in May 2018 and are part of standard BSV consensus
|
|
707
707
|
case Opcode.OP_DIV:
|
|
@@ -715,7 +715,7 @@ Interpreter.prototype.step = function () {
|
|
|
715
715
|
case Opcode.OP_NUM2BIN:
|
|
716
716
|
// These opcodes are now always enabled - no flag required
|
|
717
717
|
return false
|
|
718
|
-
|
|
718
|
+
|
|
719
719
|
default:
|
|
720
720
|
break
|
|
721
721
|
}
|
|
@@ -1757,6 +1757,56 @@ Interpreter.prototype.step = function () {
|
|
|
1757
1757
|
this.stack[this.stack.length - 1] = n1.slice(position)
|
|
1758
1758
|
break
|
|
1759
1759
|
|
|
1760
|
+
case Opcode.OP_LEFT:
|
|
1761
|
+
case Opcode.OP_RIGHT:
|
|
1762
|
+
// (in size -- out) OP_LEFT keeps the first `size` bytes; OP_RIGHT the last.
|
|
1763
|
+
// Re-enabled string opcodes (BSV Chronicle). Original Satoshi semantics.
|
|
1764
|
+
if (this.stack.length < 2) {
|
|
1765
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'
|
|
1766
|
+
return false
|
|
1767
|
+
}
|
|
1768
|
+
buf1 = stacktop(-2)
|
|
1769
|
+
n = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal).toNumber()
|
|
1770
|
+
if (n < 0) {
|
|
1771
|
+
this.errstr = 'SCRIPT_ERR_INVALID_NUMBER_RANGE'
|
|
1772
|
+
return false
|
|
1773
|
+
}
|
|
1774
|
+
if (n > buf1.length) {
|
|
1775
|
+
n = buf1.length
|
|
1776
|
+
}
|
|
1777
|
+
this.stack.pop()
|
|
1778
|
+
if (opcodenum === Opcode.OP_LEFT) {
|
|
1779
|
+
this.stack[this.stack.length - 1] = Buffer.from(buf1).slice(0, n)
|
|
1780
|
+
} else {
|
|
1781
|
+
this.stack[this.stack.length - 1] = Buffer.from(buf1).slice(buf1.length - n)
|
|
1782
|
+
}
|
|
1783
|
+
break
|
|
1784
|
+
|
|
1785
|
+
case Opcode.OP_SUBSTR:
|
|
1786
|
+
// (in begin size -- out) out = in[begin : begin + size]
|
|
1787
|
+
// Re-enabled string opcode (BSV Chronicle). Original Satoshi semantics.
|
|
1788
|
+
if (this.stack.length < 3) {
|
|
1789
|
+
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'
|
|
1790
|
+
return false
|
|
1791
|
+
}
|
|
1792
|
+
buf1 = stacktop(-3)
|
|
1793
|
+
var subSize = BN.fromScriptNumBuffer(stacktop(-1), fRequireMinimal).toNumber()
|
|
1794
|
+
var subBegin = BN.fromScriptNumBuffer(stacktop(-2), fRequireMinimal).toNumber()
|
|
1795
|
+
if (subBegin < 0 || subSize < 0) {
|
|
1796
|
+
this.errstr = 'SCRIPT_ERR_INVALID_NUMBER_RANGE'
|
|
1797
|
+
return false
|
|
1798
|
+
}
|
|
1799
|
+
if (subBegin > buf1.length) {
|
|
1800
|
+
subBegin = buf1.length
|
|
1801
|
+
}
|
|
1802
|
+
if (subBegin + subSize > buf1.length) {
|
|
1803
|
+
subSize = buf1.length - subBegin
|
|
1804
|
+
}
|
|
1805
|
+
this.stack.pop()
|
|
1806
|
+
this.stack.pop()
|
|
1807
|
+
this.stack[this.stack.length - 1] = Buffer.from(buf1).slice(subBegin, subBegin + subSize)
|
|
1808
|
+
break
|
|
1809
|
+
|
|
1760
1810
|
//
|
|
1761
1811
|
// Conversion operations
|
|
1762
1812
|
//
|
|
@@ -18,13 +18,14 @@ var Signature = require('../crypto/signature')
|
|
|
18
18
|
var SIGHASH = Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID // 0x41
|
|
19
19
|
|
|
20
20
|
// Post-Genesis consensus flags (big numbers, OP_CAT/OP_SPLIT/OP_MUL, FORKID,
|
|
21
|
-
// CLTV/CSV). LOW_S is
|
|
22
|
-
// signature
|
|
21
|
+
// CLTV/CSV). LOW_S is enforced: the OP_PUSH_TX grind produces a canonical
|
|
22
|
+
// (low-S) in-script signature, so these covenants meet mainnet standardness.
|
|
23
23
|
function flags () {
|
|
24
24
|
var I = bsv.Script.Interpreter
|
|
25
25
|
return I.SCRIPT_VERIFY_P2SH |
|
|
26
26
|
I.SCRIPT_VERIFY_STRICTENC |
|
|
27
27
|
I.SCRIPT_VERIFY_DERSIG |
|
|
28
|
+
I.SCRIPT_VERIFY_LOW_S |
|
|
28
29
|
I.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
|
|
29
30
|
I.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
|
|
30
31
|
I.SCRIPT_ENABLE_SIGHASH_FORKID |
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
/**
|
|
3
|
+
* SmartContract.trace — a covenant stack debugger.
|
|
4
|
+
*
|
|
5
|
+
* Step-traces a locking/unlocking pair through Script.Interpreter and records the
|
|
6
|
+
* stack + alt-stack after every opcode, so you can watch an OP_PUSH_TX covenant
|
|
7
|
+
* build its signature and enforce its constraints. Uses the interpreter's
|
|
8
|
+
* stepListener hook. Call SmartContract.enableGenesis() first for OP_PUSH_TX
|
|
9
|
+
* covenants (their preimage element / opcode count exceed pre-Genesis limits).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
var bsv = require('../..')
|
|
13
|
+
var helpers = require('./covenant_helpers')
|
|
14
|
+
|
|
15
|
+
function opLabel (opcodenum) {
|
|
16
|
+
if (opcodenum === 0) return 'OP_0'
|
|
17
|
+
if (opcodenum >= 1 && opcodenum <= 75) return 'PUSH(' + opcodenum + ')'
|
|
18
|
+
if (opcodenum === 76) return 'OP_PUSHDATA1'
|
|
19
|
+
if (opcodenum === 77) return 'OP_PUSHDATA2'
|
|
20
|
+
if (opcodenum === 78) return 'OP_PUSHDATA4'
|
|
21
|
+
try { return bsv.Opcode.fromNumber(opcodenum).toString() } catch (e) { return '0x' + opcodenum.toString(16) }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hex (buf, max) {
|
|
25
|
+
max = max || 16
|
|
26
|
+
var h = Buffer.from(buf).toString('hex')
|
|
27
|
+
return h.length <= max * 2 ? (h || '0') : h.slice(0, max * 2) + '...(' + buf.length + 'B)'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @returns {{ ok:boolean, err:string, steps:Array<{phase,op,stack,altstack}> }}
|
|
32
|
+
*/
|
|
33
|
+
function trace (unlock, lock, opts) {
|
|
34
|
+
opts = opts || {}
|
|
35
|
+
var interp = new bsv.Script.Interpreter()
|
|
36
|
+
var steps = []
|
|
37
|
+
interp.stepListener = function (step, stack, altstack) {
|
|
38
|
+
steps.push({
|
|
39
|
+
phase: interp.script === lock ? 'lock' : 'unlock',
|
|
40
|
+
op: opLabel(step.opcode && typeof step.opcode.toNumber === 'function' ? step.opcode.toNumber() : step.opcode.num),
|
|
41
|
+
stack: stack.map(function (b) { return hex(b) }),
|
|
42
|
+
altstack: altstack.map(function (b) { return hex(b) })
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
var ok = interp.verify(
|
|
46
|
+
unlock, lock,
|
|
47
|
+
opts.tx || new bsv.Transaction(),
|
|
48
|
+
opts.inputIndex || 0,
|
|
49
|
+
typeof opts.flags === 'number' ? opts.flags : helpers.flags(),
|
|
50
|
+
new bsv.crypto.BN(opts.satoshis || 0)
|
|
51
|
+
)
|
|
52
|
+
return { ok: ok, err: interp.errstr || '', steps: steps }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Pretty-print a trace result to a string. */
|
|
56
|
+
function format (result) {
|
|
57
|
+
var lines = []
|
|
58
|
+
var phase = null
|
|
59
|
+
result.steps.forEach(function (s, i) {
|
|
60
|
+
if (s.phase !== phase) { lines.push('-- ' + s.phase + ' script --'); phase = s.phase }
|
|
61
|
+
var stk = s.stack.length ? s.stack.join(' | ') : '(empty)'
|
|
62
|
+
var alt = s.altstack.length ? ' alt:[' + s.altstack.join(' | ') + ']' : ''
|
|
63
|
+
lines.push(String(i).padStart(3) + ' ' + s.op.padEnd(16) + ' -> [' + stk + ']' + alt)
|
|
64
|
+
})
|
|
65
|
+
lines.push((result.ok ? 'VALID' : 'INVALID') + (result.err ? ' (' + result.err + ')' : ''))
|
|
66
|
+
return lines.join('\n')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function print (result) { console.log(format(result)); return result } // eslint-disable-line no-console
|
|
70
|
+
|
|
71
|
+
module.exports = { trace: trace, format: format, print: print, opLabel: opLabel }
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
/**
|
|
3
|
+
* SmartContract.policy — a declarative covenant DSL.
|
|
4
|
+
*
|
|
5
|
+
* Describe a spending POLICY and compile it to a verified OP_PUSH_TX locking
|
|
6
|
+
* script, no hand-written opcodes:
|
|
7
|
+
*
|
|
8
|
+
* var c = bsv.SmartContract.policy()
|
|
9
|
+
* .payTo(aliceAddr, 9500) // the spend MUST create this output...
|
|
10
|
+
* .lockUntil(800000) // ...with nLockTime >= 800000
|
|
11
|
+
* .compile()
|
|
12
|
+
* // c.lock -> locking Script
|
|
13
|
+
* // c.outputs -> the required Transaction.Output[]
|
|
14
|
+
* // c.unlock(spendTx, satoshis) -> grinds + returns the preimage unlock Script
|
|
15
|
+
*
|
|
16
|
+
* Each clause compiles to one preimage-field check on top of a single OP_PUSH_TX
|
|
17
|
+
* authentication; clauses AND together. Requires post-Genesis limits at verify
|
|
18
|
+
* time (SmartContract.enableGenesis()).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
var bsv = require('../..')
|
|
22
|
+
var PushTx = require('./pushtx')
|
|
23
|
+
var PELS = require('./pels')
|
|
24
|
+
var Token = require('./token')
|
|
25
|
+
var helpers = require('./covenant_helpers')
|
|
26
|
+
|
|
27
|
+
var Script = bsv.Script
|
|
28
|
+
var Opcode = bsv.Opcode
|
|
29
|
+
var Transaction = bsv.Transaction
|
|
30
|
+
var n = helpers.scriptNum
|
|
31
|
+
|
|
32
|
+
function toOutput (dest, satoshis) {
|
|
33
|
+
if (dest instanceof Transaction.Output) return dest
|
|
34
|
+
if (dest instanceof Script) return new Transaction.Output({ script: dest, satoshis: satoshis })
|
|
35
|
+
return helpers.p2pkhOutput(dest, satoshis)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Grind the OP_PUSH_TX nonce (nLockTime) from `base` upward until the in-script
|
|
39
|
+
// signature is a clean low-S DER. Grinding from `base` keeps nLockTime within a
|
|
40
|
+
// lockUntil floor instead of fighting it (grind uses nLockTime as its nonce).
|
|
41
|
+
function grindFrom (spendTx, inputIndex, lock, satoshis, base, maxTries) {
|
|
42
|
+
maxTries = maxTries || 50000
|
|
43
|
+
for (var t = 0; t < maxTries; t++) {
|
|
44
|
+
spendTx.nLockTime = base + t
|
|
45
|
+
var pre = helpers.rawPreimage(spendTx, inputIndex, lock, satoshis)
|
|
46
|
+
if (PushTx.sFromPreimage(pre)) return pre
|
|
47
|
+
}
|
|
48
|
+
throw new Error('covenant grind failed after ' + maxTries + ' tries')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function Policy () {
|
|
52
|
+
if (!(this instanceof Policy)) return new Policy()
|
|
53
|
+
this._outputs = []
|
|
54
|
+
this._minLockTime = null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Require the spend to create this output (call once per required output, in order). */
|
|
58
|
+
Policy.prototype.payTo = function (dest, satoshis) {
|
|
59
|
+
this._outputs.push(toOutput(dest, satoshis))
|
|
60
|
+
return this
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Require the spend's nLockTime to be >= height. */
|
|
64
|
+
Policy.prototype.lockUntil = function (height) {
|
|
65
|
+
this._minLockTime = height
|
|
66
|
+
return this
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The exact outputs a valid spend must contain. */
|
|
70
|
+
Policy.prototype.outputs = function () {
|
|
71
|
+
return this._outputs.map(function (o) { return new Transaction.Output(o.toObject()) })
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Policy.prototype.describe = function () {
|
|
75
|
+
var parts = []
|
|
76
|
+
this._outputs.forEach(function (o, i) {
|
|
77
|
+
parts.push('output[' + i + '] = ' + o.satoshis + ' sat to ' + o.script.toASM().slice(0, 24) + '...')
|
|
78
|
+
})
|
|
79
|
+
if (this._minLockTime !== null) parts.push('nLockTime >= ' + this._minLockTime)
|
|
80
|
+
return 'spend allowed iff: ' + parts.join(' AND ')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Compile to a locking script + spend helpers. */
|
|
84
|
+
Policy.prototype.compile = function () {
|
|
85
|
+
if (this._outputs.length === 0 && this._minLockTime === null) {
|
|
86
|
+
throw new Error('empty policy: add at least one constraint (payTo / lockUntil)')
|
|
87
|
+
}
|
|
88
|
+
var policy = this
|
|
89
|
+
var s = new Script().add(Opcode.OP_DUP)
|
|
90
|
+
PushTx.pushTxCore(s) // authenticate the preimage
|
|
91
|
+
s.add(Opcode.OP_VERIFY) // stack: [preimage]
|
|
92
|
+
|
|
93
|
+
if (this._outputs.length) {
|
|
94
|
+
var expected = PushTx.hashOutputs(this._outputs)
|
|
95
|
+
s.add(Opcode.OP_DUP)
|
|
96
|
+
PushTx.extractHashOutputs(s)
|
|
97
|
+
s.add(Buffer.from(expected)).add(Opcode.OP_EQUALVERIFY)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (this._minLockTime !== null) {
|
|
101
|
+
s.add(Opcode.OP_DUP)
|
|
102
|
+
s.add(n(8)).add(Opcode.OP_RIGHT).add(n(4)).add(Opcode.OP_LEFT) // last 8, first 4 = nLockTime
|
|
103
|
+
s.add(Opcode.OP_BIN2NUM).add(n(this._minLockTime)).add(Opcode.OP_GREATERTHANOREQUAL).add(Opcode.OP_VERIFY)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
s.add(Opcode.OP_DROP).add(Opcode.OP_1) // drop preimage; succeed
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
lock: s,
|
|
110
|
+
outputs: policy.outputs(),
|
|
111
|
+
describe: function () { return policy.describe() },
|
|
112
|
+
unlock: function (spendTx, satoshis, inputIndex) {
|
|
113
|
+
inputIndex = inputIndex || 0
|
|
114
|
+
var base = policy._minLockTime === null ? 0 : policy._minLockTime
|
|
115
|
+
if (policy._minLockTime !== null) {
|
|
116
|
+
spendTx.inputs[inputIndex].sequenceNumber = 0xfffffffe // non-final so locktime applies
|
|
117
|
+
}
|
|
118
|
+
return new Script().add(grindFrom(spendTx, inputIndex, s, satoshis, base))
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function policy () { return new Policy() }
|
|
124
|
+
|
|
125
|
+
// Terminal shortcuts for the non-composable whole-covenant patterns.
|
|
126
|
+
policy.perpetual = function (fee) { return PELS.pelsCovenant(fee) }
|
|
127
|
+
policy.token = function (fee, ownerHash) { return Token.ownershipToken(fee, ownerHash) }
|
|
128
|
+
|
|
129
|
+
module.exports = { policy: policy, Policy: Policy }
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SmartContract Module
|
|
3
3
|
* ===================
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* Production-ready covenant functionality for Bitcoin SV (BSV).
|
|
6
6
|
* Implements BIP-143 transaction preimage parsing and covenant operations
|
|
7
7
|
* with comprehensive SIGHASH flag support and error handling.
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* Classes:
|
|
10
10
|
* - Covenant: Core covenant creation and spending logic
|
|
11
11
|
* - Preimage: BIP-143 preimage parsing and validation with field extraction
|
|
@@ -43,7 +43,20 @@ var SmartContract = {
|
|
|
43
43
|
Locks: require('./locks'),
|
|
44
44
|
PushTx: require('./pushtx'),
|
|
45
45
|
PELS: require('./pels'),
|
|
46
|
-
Token: require('./token')
|
|
46
|
+
Token: require('./token'),
|
|
47
|
+
|
|
48
|
+
// Declarative covenant DSL + stack debugger (v4.5.0)
|
|
49
|
+
DSL: require('./dsl'),
|
|
50
|
+
Policy: require('./dsl').Policy,
|
|
51
|
+
Debugger: require('./debugger')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Declarative spending policy -> verified OP_PUSH_TX covenant (v4.5.0).
|
|
55
|
+
// (Exposes the policy() factory with its .perpetual()/.token() shortcuts.)
|
|
56
|
+
SmartContract.policy = SmartContract.DSL.policy
|
|
57
|
+
// Step-trace a locking/unlocking pair (returns { ok, err, steps }).
|
|
58
|
+
SmartContract.trace = function (unlockingScript, lockingScript, opts) {
|
|
59
|
+
return SmartContract.Debugger.trace(unlockingScript, lockingScript, opts)
|
|
47
60
|
}
|
|
48
61
|
|
|
49
62
|
// --- Covenant convenience API (v4.2.0) ---
|
|
@@ -69,161 +82,161 @@ SmartContract.valueCovenant = function (expectedHashOutputs) {
|
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
// Convenience constructor methods
|
|
72
|
-
SmartContract.createCovenant = function(privateKey, options) {
|
|
85
|
+
SmartContract.createCovenant = function (privateKey, options) {
|
|
73
86
|
return new SmartContract.Covenant(privateKey, options)
|
|
74
87
|
}
|
|
75
88
|
|
|
76
|
-
SmartContract.extractPreimage = function(preimage, options) {
|
|
89
|
+
SmartContract.extractPreimage = function (preimage, options) {
|
|
77
90
|
return new SmartContract.Preimage(preimage, options)
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
SmartContract.analyzeSIGHASH = function(sighashType) {
|
|
93
|
+
SmartContract.analyzeSIGHASH = function (sighashType) {
|
|
81
94
|
return new SmartContract.SIGHASH(sighashType)
|
|
82
95
|
}
|
|
83
96
|
|
|
84
|
-
SmartContract.buildCovenant = function(privateKey, options) {
|
|
97
|
+
SmartContract.buildCovenant = function (privateKey, options) {
|
|
85
98
|
return new SmartContract.Builder(privateKey, options)
|
|
86
99
|
}
|
|
87
100
|
|
|
88
|
-
SmartContract.createUTXOGenerator = function(options) {
|
|
101
|
+
SmartContract.createUTXOGenerator = function (options) {
|
|
89
102
|
return new SmartContract.UTXOGenerator(options)
|
|
90
103
|
}
|
|
91
104
|
|
|
92
|
-
SmartContract.createTestEnvironment = function(options) {
|
|
105
|
+
SmartContract.createTestEnvironment = function (options) {
|
|
93
106
|
return SmartContract.UTXOGenerator.createTestEnvironment(options)
|
|
94
107
|
}
|
|
95
108
|
|
|
96
|
-
SmartContract.testScript = function(unlocking, locking, options) {
|
|
109
|
+
SmartContract.testScript = function (unlocking, locking, options) {
|
|
97
110
|
return SmartContract.ScriptTester.test(unlocking, locking, options)
|
|
98
111
|
}
|
|
99
112
|
|
|
100
|
-
SmartContract.testCovenant = function(preimageHex, constraints, options) {
|
|
113
|
+
SmartContract.testCovenant = function (preimageHex, constraints, options) {
|
|
101
114
|
return SmartContract.ScriptTester.testCovenant(preimageHex, constraints, options)
|
|
102
115
|
}
|
|
103
116
|
|
|
104
|
-
SmartContract.testFieldExtraction = function(preimageHex, fieldName, options) {
|
|
117
|
+
SmartContract.testFieldExtraction = function (preimageHex, fieldName, options) {
|
|
105
118
|
return SmartContract.ScriptTester.testFieldExtraction(preimageHex, fieldName, options)
|
|
106
119
|
}
|
|
107
120
|
|
|
108
|
-
SmartContract.debugScript = function(config, options) {
|
|
121
|
+
SmartContract.debugScript = function (config, options) {
|
|
109
122
|
var tester = new SmartContract.ScriptTester(options)
|
|
110
123
|
return tester.debug(config)
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
// Educational and utility functions
|
|
114
|
-
SmartContract.explainZeroHashes = function() {
|
|
127
|
+
SmartContract.explainZeroHashes = function () {
|
|
115
128
|
return SmartContract.SIGHASH.explainZeroMystery()
|
|
116
129
|
}
|
|
117
130
|
|
|
118
|
-
SmartContract.getAllSIGHASHTypes = function() {
|
|
131
|
+
SmartContract.getAllSIGHASHTypes = function () {
|
|
119
132
|
return SmartContract.SIGHASH.getAllTypes()
|
|
120
133
|
}
|
|
121
134
|
|
|
122
|
-
SmartContract.demonstrateAllSIGHASH = function() {
|
|
135
|
+
SmartContract.demonstrateAllSIGHASH = function () {
|
|
123
136
|
return SmartContract.SIGHASH.generateAllDemonstrations()
|
|
124
137
|
}
|
|
125
138
|
|
|
126
139
|
// JavaScript-to-Bitcoin Script covenant generation
|
|
127
|
-
SmartContract.createCovenantBuilder = function() {
|
|
140
|
+
SmartContract.createCovenantBuilder = function () {
|
|
128
141
|
return new SmartContract.CovenantBuilder()
|
|
129
142
|
}
|
|
130
143
|
|
|
131
|
-
SmartContract.createValueLockCovenant = function(expectedValue) {
|
|
144
|
+
SmartContract.createValueLockCovenant = function (expectedValue) {
|
|
132
145
|
return SmartContract.CovenantTemplates.valueLock(expectedValue)
|
|
133
146
|
}
|
|
134
147
|
|
|
135
|
-
SmartContract.createHashLockCovenant = function(expectedHash) {
|
|
148
|
+
SmartContract.createHashLockCovenant = function (expectedHash) {
|
|
136
149
|
return SmartContract.CovenantTemplates.hashLock(expectedHash)
|
|
137
150
|
}
|
|
138
151
|
|
|
139
|
-
SmartContract.createComplexValidationCovenant = function(rules) {
|
|
152
|
+
SmartContract.createComplexValidationCovenant = function (rules) {
|
|
140
153
|
return SmartContract.CovenantTemplates.complexValidation(rules)
|
|
141
154
|
}
|
|
142
155
|
|
|
143
156
|
// Opcode mapping utilities
|
|
144
|
-
SmartContract.getOpcodeMap = function() {
|
|
157
|
+
SmartContract.getOpcodeMap = function () {
|
|
145
158
|
return SmartContract.OpcodeMap.opcodeMap
|
|
146
159
|
}
|
|
147
160
|
|
|
148
|
-
SmartContract.simulateScript = function(operations, initialStack) {
|
|
161
|
+
SmartContract.simulateScript = function (operations, initialStack) {
|
|
149
162
|
return SmartContract.OpcodeMap.utils.simulate(operations, initialStack)
|
|
150
163
|
}
|
|
151
164
|
|
|
152
|
-
SmartContract.createASMFromJS = function(operations) {
|
|
165
|
+
SmartContract.createASMFromJS = function (operations) {
|
|
153
166
|
return SmartContract.OpcodeMap.utils.createASM(operations)
|
|
154
167
|
}
|
|
155
168
|
|
|
156
169
|
// Script analysis and conversion utilities
|
|
157
|
-
SmartContract.scriptToASM = function(scriptBuffer) {
|
|
170
|
+
SmartContract.scriptToASM = function (scriptBuffer) {
|
|
158
171
|
return SmartContract.ScriptUtils.scriptToASM(scriptBuffer)
|
|
159
172
|
}
|
|
160
173
|
|
|
161
|
-
SmartContract.asmToScript = function(asmString) {
|
|
174
|
+
SmartContract.asmToScript = function (asmString) {
|
|
162
175
|
return SmartContract.ScriptUtils.asmToScript(asmString)
|
|
163
176
|
}
|
|
164
177
|
|
|
165
|
-
SmartContract.asmToHex = function(asmString) {
|
|
178
|
+
SmartContract.asmToHex = function (asmString) {
|
|
166
179
|
return SmartContract.ScriptUtils.asmToHex(asmString)
|
|
167
180
|
}
|
|
168
181
|
|
|
169
|
-
SmartContract.hexToASM = function(hexString) {
|
|
182
|
+
SmartContract.hexToASM = function (hexString) {
|
|
170
183
|
return SmartContract.ScriptUtils.hexToASM(hexString)
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
SmartContract.scriptToHex = function(script) {
|
|
186
|
+
SmartContract.scriptToHex = function (script) {
|
|
174
187
|
return SmartContract.ScriptUtils.scriptToHex(script)
|
|
175
188
|
}
|
|
176
189
|
|
|
177
|
-
SmartContract.validateASM = function(asmString) {
|
|
190
|
+
SmartContract.validateASM = function (asmString) {
|
|
178
191
|
return SmartContract.ScriptUtils.validateASM(asmString)
|
|
179
192
|
}
|
|
180
193
|
|
|
181
|
-
SmartContract.validateScript = function(script) {
|
|
194
|
+
SmartContract.validateScript = function (script) {
|
|
182
195
|
return SmartContract.ScriptUtils.validateScript(script)
|
|
183
196
|
}
|
|
184
197
|
|
|
185
|
-
SmartContract.estimateScriptSize = function(script) {
|
|
198
|
+
SmartContract.estimateScriptSize = function (script) {
|
|
186
199
|
return SmartContract.ScriptUtils.estimateScriptSize(script)
|
|
187
200
|
}
|
|
188
201
|
|
|
189
|
-
SmartContract.optimizeScript = function(script) {
|
|
202
|
+
SmartContract.optimizeScript = function (script) {
|
|
190
203
|
return SmartContract.ScriptUtils.optimizeScript(script)
|
|
191
204
|
}
|
|
192
205
|
|
|
193
|
-
SmartContract.compareScripts = function(scriptA, scriptB) {
|
|
206
|
+
SmartContract.compareScripts = function (scriptA, scriptB) {
|
|
194
207
|
return SmartContract.ScriptUtils.compareScripts(scriptA, scriptB)
|
|
195
208
|
}
|
|
196
209
|
|
|
197
|
-
SmartContract.explainScript = function(script) {
|
|
210
|
+
SmartContract.explainScript = function (script) {
|
|
198
211
|
return SmartContract.ScriptUtils.explainScript(script)
|
|
199
212
|
}
|
|
200
213
|
|
|
201
|
-
SmartContract.scriptMetrics = function(script) {
|
|
214
|
+
SmartContract.scriptMetrics = function (script) {
|
|
202
215
|
return SmartContract.ScriptUtils.scriptMetrics(script)
|
|
203
216
|
}
|
|
204
217
|
|
|
205
|
-
SmartContract.analyzeComplexity = function(script) {
|
|
218
|
+
SmartContract.analyzeComplexity = function (script) {
|
|
206
219
|
return SmartContract.ScriptUtils.analyzeComplexity(script)
|
|
207
220
|
}
|
|
208
221
|
|
|
209
|
-
SmartContract.findOptimizations = function(script) {
|
|
222
|
+
SmartContract.findOptimizations = function (script) {
|
|
210
223
|
return SmartContract.ScriptUtils.findOptimizations(script)
|
|
211
224
|
}
|
|
212
225
|
|
|
213
|
-
SmartContract.covenantToEnglish = function(covenant) {
|
|
226
|
+
SmartContract.covenantToEnglish = function (covenant) {
|
|
214
227
|
return SmartContract.ScriptUtils.covenantToEnglish(covenant)
|
|
215
228
|
}
|
|
216
229
|
|
|
217
|
-
SmartContract.batchTestScripts = function(scripts, options) {
|
|
230
|
+
SmartContract.batchTestScripts = function (scripts, options) {
|
|
218
231
|
return SmartContract.ScriptUtils.batchTestScripts(scripts, options)
|
|
219
232
|
}
|
|
220
233
|
|
|
221
234
|
// Debug and analysis tools
|
|
222
|
-
SmartContract.examineStack = function(lockingHex, unlockingHex) {
|
|
235
|
+
SmartContract.examineStack = function (lockingHex, unlockingHex) {
|
|
223
236
|
return SmartContract.StackExaminer.runScript(lockingHex, unlockingHex)
|
|
224
237
|
}
|
|
225
238
|
|
|
226
|
-
SmartContract.debugScriptExecution = function(unlockingScript, lockingScript, options) {
|
|
239
|
+
SmartContract.debugScriptExecution = function (unlockingScript, lockingScript, options) {
|
|
227
240
|
options = options || {}
|
|
228
241
|
if (options.stepMode) {
|
|
229
242
|
return SmartContract.ScriptInterpreter.stepThroughScript(
|
|
@@ -240,18 +253,18 @@ SmartContract.debugScriptExecution = function(unlockingScript, lockingScript, op
|
|
|
240
253
|
}
|
|
241
254
|
}
|
|
242
255
|
|
|
243
|
-
SmartContract.parseScript = function(scriptInput) {
|
|
256
|
+
SmartContract.parseScript = function (scriptInput) {
|
|
244
257
|
return SmartContract.ScriptInterpreter.parseScript(scriptInput)
|
|
245
258
|
}
|
|
246
259
|
|
|
247
|
-
SmartContract.printStack = function(stack, altstack) {
|
|
260
|
+
SmartContract.printStack = function (stack, altstack) {
|
|
248
261
|
return SmartContract.ScriptInterpreter.printStack(stack, altstack)
|
|
249
262
|
}
|
|
250
263
|
|
|
251
264
|
// Quick covenant creation utility
|
|
252
|
-
SmartContract.createQuickCovenant = function(type, params) {
|
|
265
|
+
SmartContract.createQuickCovenant = function (type, params) {
|
|
253
266
|
const builder = SmartContract.createCovenantBuilder()
|
|
254
|
-
|
|
267
|
+
|
|
255
268
|
switch (type) {
|
|
256
269
|
case 'value_lock':
|
|
257
270
|
return builder
|
|
@@ -261,7 +274,7 @@ SmartContract.createQuickCovenant = function(type, params) {
|
|
|
261
274
|
.greaterThanOrEqual()
|
|
262
275
|
.verify()
|
|
263
276
|
.build()
|
|
264
|
-
|
|
277
|
+
|
|
265
278
|
case 'hash_lock':
|
|
266
279
|
return builder
|
|
267
280
|
.comment(`Hash lock: requires preimage of ${params.hash}`)
|
|
@@ -270,7 +283,7 @@ SmartContract.createQuickCovenant = function(type, params) {
|
|
|
270
283
|
.push(params.hash)
|
|
271
284
|
.equalVerify()
|
|
272
285
|
.build()
|
|
273
|
-
|
|
286
|
+
|
|
274
287
|
case 'time_lock':
|
|
275
288
|
return builder
|
|
276
289
|
.comment(`Time lock: requires nLockTime > ${params.timestamp}`)
|
|
@@ -279,7 +292,7 @@ SmartContract.createQuickCovenant = function(type, params) {
|
|
|
279
292
|
.greaterThan()
|
|
280
293
|
.verify()
|
|
281
294
|
.build()
|
|
282
|
-
|
|
295
|
+
|
|
283
296
|
case 'multi_condition':
|
|
284
297
|
let covenant = builder.comment('Multi-condition covenant')
|
|
285
298
|
params.conditions.forEach(condition => {
|
|
@@ -308,20 +321,20 @@ SmartContract.createQuickCovenant = function(type, params) {
|
|
|
308
321
|
}
|
|
309
322
|
})
|
|
310
323
|
return covenant.build()
|
|
311
|
-
|
|
324
|
+
|
|
312
325
|
default:
|
|
313
326
|
throw new Error(`Unknown covenant type: ${type}`)
|
|
314
327
|
}
|
|
315
328
|
}
|
|
316
329
|
|
|
317
330
|
// Complete workflow helpers
|
|
318
|
-
SmartContract.completeCovenantFlow = function(privateKey, p2pkhUtxo, broadcastCallback) {
|
|
331
|
+
SmartContract.completeCovenantFlow = function (privateKey, p2pkhUtxo, broadcastCallback) {
|
|
319
332
|
var covenant = new SmartContract.Covenant(privateKey)
|
|
320
333
|
return covenant.completeFlow(p2pkhUtxo, broadcastCallback)
|
|
321
334
|
}
|
|
322
335
|
|
|
323
336
|
// Educational utilities
|
|
324
|
-
SmartContract.getEducationalResources = function() {
|
|
337
|
+
SmartContract.getEducationalResources = function () {
|
|
325
338
|
return {
|
|
326
339
|
zeroHashMystery: SmartContract.SIGHASH.explainZeroMystery(),
|
|
327
340
|
sighashTypes: SmartContract.SIGHASH.getAllTypes(),
|
|
@@ -331,17 +344,17 @@ SmartContract.getEducationalResources = function() {
|
|
|
331
344
|
|
|
332
345
|
// Utility functions
|
|
333
346
|
SmartContract.utils = {}
|
|
334
|
-
SmartContract.utils.reconstructP2pkhScript = function(address) {
|
|
347
|
+
SmartContract.utils.reconstructP2pkhScript = function (address) {
|
|
335
348
|
return bsv.Script.buildPublicKeyHashOut(address).toHex()
|
|
336
349
|
}
|
|
337
350
|
|
|
338
|
-
SmartContract.utils.createCovenantAddress = function(script) {
|
|
351
|
+
SmartContract.utils.createCovenantAddress = function (script) {
|
|
339
352
|
// Create P2SH address for covenant script
|
|
340
353
|
var scriptHash = bsv.crypto.Hash.sha256ripemd160(script.toBuffer())
|
|
341
354
|
return bsv.Address.fromScriptHash(scriptHash)
|
|
342
355
|
}
|
|
343
356
|
|
|
344
|
-
SmartContract.utils.decodeCompactSize = function(buffer, offset) {
|
|
357
|
+
SmartContract.utils.decodeCompactSize = function (buffer, offset) {
|
|
345
358
|
return SmartContract.Preimage.decodeCompactSize(buffer, offset)
|
|
346
359
|
}
|
|
347
360
|
|
|
@@ -380,4 +393,4 @@ SmartContract.features = {
|
|
|
380
393
|
SmartContract.interpretScript = SmartContract.debugScriptExecution
|
|
381
394
|
SmartContract.getScriptMetrics = SmartContract.scriptMetrics
|
|
382
395
|
|
|
383
|
-
module.exports = SmartContract
|
|
396
|
+
module.exports = SmartContract
|
|
@@ -44,8 +44,7 @@ function pelsCovenant (fee) {
|
|
|
44
44
|
|
|
45
45
|
// 3. value (8B @ offsetFromEnd 52); newValue = value - fee, as 8-byte LE
|
|
46
46
|
s.add(Opcode.OP_OVER)
|
|
47
|
-
s.add(
|
|
48
|
-
s.add(n(8)).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP)
|
|
47
|
+
s.add(n(52)).add(Opcode.OP_RIGHT).add(n(8)).add(Opcode.OP_LEFT) // last 52 bytes, first 8 = value
|
|
49
48
|
s.add(Opcode.OP_BIN2NUM).add(n(fee)).add(Opcode.OP_SUB)
|
|
50
49
|
s.add(n(8)).add(Opcode.OP_NUM2BIN)
|
|
51
50
|
|