smartledger-bsv 4.2.1 → 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.
@@ -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 intentionally omitted so the in-script OP_PUSH_TX
22
- // signature (which may be high-S) verifies.
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 |
@@ -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(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)
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
 
@@ -59,10 +59,18 @@ function pushTxCore (script) {
59
59
  script.add(gxLe).add(Opcode.OP_ADD) // e + Gx
60
60
  script.add(N_LE).add(Opcode.OP_MOD) // s = (e + Gx) mod n
61
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
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
64
72
  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
73
+ script.add(Opcode.OP_FROMALTSTACK).add(Opcode.OP_CHECKSIG) // pubkey ; verify against P = G
66
74
  return script
67
75
  }
68
76
 
@@ -73,9 +81,8 @@ function authenticator () {
73
81
 
74
82
  /** Extract the committed hashOutputs (item 9, offsetFromEnd 40, len 32) from a preimage on-stack. */
75
83
  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)
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)
79
86
  return script
80
87
  }
81
88
 
@@ -98,12 +105,22 @@ function valueCovenant (expectedHashOutputs) {
98
105
  return script
99
106
  }
100
107
 
101
- /** Compute s = (HASH256(preimage)+Gx) mod n; return its 32-byte BE form, or null if not a clean DER template. */
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
+ */
102
118
  function sFromPreimage (preimage) {
103
119
  var z = Hash.sha256sha256(preimage)
104
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)
105
122
  var sBE = s.toBuffer('be', 32)
106
- return (sBE[0] >= 0x01 && sBE[0] <= 0x7f) ? sBE : null
123
+ return (sBE[0] >= 0x01) ? sBE : null // s <= n/2 already guarantees sBE[0] <= 0x7f
107
124
  }
108
125
 
109
126
  /**
@@ -41,17 +41,15 @@ function ownershipToken (fee, ownerHash) {
41
41
 
42
42
  // park value (8B @ offsetFromEnd 52) and hashOutputs (32B @ offsetFromEnd 40)
43
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]
44
+ .add(n(52)).add(Opcode.OP_RIGHT).add(n(8)).add(Opcode.OP_LEFT).add(Opcode.OP_TOALTSTACK) // alt:[value8]
46
45
  s.add(Opcode.OP_DUP); P.extractHashOutputs(s); s.add(Opcode.OP_TOALTSTACK) // alt:[value8, hashOutputs]
47
46
 
48
47
  // scriptChunk = preimage[104 : len-52] (consumes preimage)
49
48
  s.add(n(104)).add(Opcode.OP_SPLIT).add(Opcode.OP_NIP)
50
49
  .add(Opcode.OP_SIZE).add(n(52)).add(Opcode.OP_SUB).add(Opcode.OP_SPLIT).add(Opcode.OP_DROP)
51
50
 
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)
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)
55
53
  s.add(n(3)).add(Opcode.OP_ROLL)
56
54
  s.add(Opcode.OP_SHA256).add(Opcode.OP_EQUALVERIFY) // [newOwnerHash, scriptChunk]
57
55
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartledger-bsv",
3
- "version": "4.2.1",
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",