@smartledger/bsv 9.3.0 → 9.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.
@@ -28,6 +28,11 @@ var Opcode = require('../opcode')
28
28
  var Transaction = require('../transaction')
29
29
  var n = helpers.scriptNum
30
30
 
31
+ // Consensus constants, named so the compiled script can be read against the node.
32
+ var LOCKTIME_THRESHOLD = 500000000 // below: block height. at or above: unix time.
33
+ var SEQUENCE_FINAL = Buffer.from([0xff, 0xff, 0xff, 0xff])
34
+ var SIGN_PAD = Buffer.from([0x00]) // makes an unsigned 32-bit field read positive
35
+
31
36
  function toOutput (dest, satoshis) {
32
37
  if (dest instanceof Transaction.Output) return dest
33
38
  if (dest instanceof Script) return new Transaction.Output({ script: dest, satoshis: satoshis })
@@ -59,8 +64,22 @@ Policy.prototype.payTo = function (dest, satoshis) {
59
64
  return this
60
65
  }
61
66
 
62
- /** Require the spend's nLockTime to be >= height. */
67
+ /**
68
+ * Require the spend's nLockTime to be >= height.
69
+ *
70
+ * Below LOCKTIME_THRESHOLD (500000000) the value is a BLOCK HEIGHT; at or above
71
+ * it, a UNIX TIMESTAMP. The compiled script enforces the same unit the floor
72
+ * uses, so a height floor cannot be satisfied by a timestamp.
73
+ *
74
+ * 0 is rejected: consensus reads nLockTime 0 as "no lock" and treats the
75
+ * transaction as final regardless of any input's sequence, so a floor of 0
76
+ * would compile to a lock that binds nothing.
77
+ */
63
78
  Policy.prototype.lockUntil = function (height) {
79
+ if (!Number.isInteger(height) || height < 1 || height > 0xffffffff) {
80
+ throw new Error('lockUntil: expected an integer in 1..4294967295, got ' + height +
81
+ ' (nLockTime 0 means "no lock" — consensus treats such a transaction as final)')
82
+ }
64
83
  this._minLockTime = height
65
84
  return this
66
85
  }
@@ -75,7 +94,11 @@ Policy.prototype.describe = function () {
75
94
  this._outputs.forEach(function (o, i) {
76
95
  parts.push('output[' + i + '] = ' + o.satoshis + ' sat to ' + o.script.toASM().slice(0, 24) + '...')
77
96
  })
78
- if (this._minLockTime !== null) parts.push('nLockTime >= ' + this._minLockTime)
97
+ if (this._minLockTime !== null) {
98
+ var unit = this._minLockTime < LOCKTIME_THRESHOLD ? 'block height' : 'unix time'
99
+ parts.push('nLockTime >= ' + this._minLockTime + ' as a ' + unit)
100
+ parts.push('this input is non-final (nSequence != 0xffffffff)')
101
+ }
79
102
  return 'spend allowed iff: ' + parts.join(' AND ')
80
103
  }
81
104
 
@@ -97,9 +120,38 @@ Policy.prototype.compile = function () {
97
120
  }
98
121
 
99
122
  if (this._minLockTime !== null) {
123
+ // A time lock is three consensus rules, not one comparison. All three are in
124
+ // the node's CheckLockTime; checking only the number reproduces none of them.
125
+
126
+ // 1. nSequence of THIS input must be non-final. IsFinalTx() ignores nLockTime
127
+ // outright when every input is 0xffffffff, so without this the spend is
128
+ // minable in the next block no matter how large a locktime it declares.
129
+ // One non-final input is enough to make the whole transaction non-final,
130
+ // and this input is the only one the preimage lets us see.
131
+ s.add(Opcode.OP_DUP)
132
+ s.add(n(44)).add(Opcode.OP_RIGHT).add(n(4)).add(Opcode.OP_LEFT) // last 44, first 4 = nSequence
133
+ s.add(SEQUENCE_FINAL).add(Opcode.OP_EQUAL).add(Opcode.OP_NOT).add(Opcode.OP_VERIFY)
134
+
135
+ // 2. nLockTime is an UNSIGNED 32-bit field, and OP_BIN2NUM reads a SIGNED
136
+ // one. From 19 Jan 2038 the high bit is set, and 0x80000000 minimally
137
+ // encodes to negative zero — the floor would be compared against 0 and no
138
+ // spend would ever satisfy it. Sign-pad to five bytes first. That push is
139
+ // only legal because OP_BIN2NUM now honours the era's script-number width.
100
140
  s.add(Opcode.OP_DUP)
101
141
  s.add(n(8)).add(Opcode.OP_RIGHT).add(n(4)).add(Opcode.OP_LEFT) // last 8, first 4 = nLockTime
102
- s.add(Opcode.OP_BIN2NUM).add(n(this._minLockTime)).add(Opcode.OP_GREATERTHANOREQUAL).add(Opcode.OP_VERIFY)
142
+ s.add(SIGN_PAD).add(Opcode.OP_CAT).add(Opcode.OP_BIN2NUM)
143
+
144
+ // 3. Units must match the floor's. Consensus reads nLockTime below
145
+ // LOCKTIME_THRESHOLD as a height and at or above it as a timestamp, so a
146
+ // bare `>=` lets a long-past timestamp clear a future height floor —
147
+ // 1500000000 >= 900000 is true, and that transaction is minable today.
148
+ // A floor already at or above the threshold needs no guard: `>= floor`
149
+ // implies `>= threshold`.
150
+ if (this._minLockTime < LOCKTIME_THRESHOLD) {
151
+ s.add(Opcode.OP_DUP).add(n(LOCKTIME_THRESHOLD)).add(Opcode.OP_LESSTHAN).add(Opcode.OP_VERIFY)
152
+ }
153
+
154
+ s.add(n(this._minLockTime)).add(Opcode.OP_GREATERTHANOREQUAL).add(Opcode.OP_VERIFY)
103
155
  }
104
156
 
105
157
  s.add(Opcode.OP_DROP).add(Opcode.OP_1) // drop preimage; succeed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartledger/bsv",
3
- "version": "9.3.0",
3
+ "version": "9.5.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",
@@ -118,7 +118,7 @@
118
118
  "test:all": "npm test && npm run test:covenants && npm run test:scripts",
119
119
  "coverage": "nyc --reporter=text npm run test",
120
120
  "demo": "node demos/smart_contract_demo.js",
121
- "demo:web": "echo '\ud83c\udf10 Open demos/smart_contract_demo.html in your browser'",
121
+ "demo:web": "echo '🌐 Open demos/smart_contract_demo.html in your browser'",
122
122
  "demo:basics": "node demos/smart_contract_demo.js --feature basics",
123
123
  "demo:covenant": "node demos/smart_contract_demo.js --feature covenant",
124
124
  "demo:preimage": "node demos/smart_contract_demo.js --feature preimage",
@@ -127,7 +127,7 @@
127
127
  "demo:legacy": "npm run test:covenants",
128
128
  "demo:legacy:basic": "npm run test:basic",
129
129
  "demo:legacy:scripts": "npm run test:scripts",
130
- "demo:legacy:preimage": "echo '\ud83e\udde0 BIP-143 Preimage Extractor Demo' && node examples/preimage/extract_preimage_bidirectional.js",
130
+ "demo:legacy:preimage": "echo '🧠 BIP-143 Preimage Extractor Demo' && node examples/preimage/extract_preimage_bidirectional.js",
131
131
  "smart-contracts": "node examples/smart_contract_templates.js",
132
132
  "smart-contracts:working": "node examples/final_working_contracts.js",
133
133
  "workflow": "node examples/complete_workflow_demo.js",
package/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  'use strict'
2
2
  // GENERATED by scripts/sync-version.js on `npm version` — do not edit.
3
- module.exports = '9.3.0'
3
+ module.exports = '9.5.0'