@btc-vision/btc-runtime 1.10.12 → 1.11.0-alpha

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,27 @@
1
+ let _cachedDeadAddress: usize = 0;
2
+ let _cachedZeroAddress: usize = 0;
3
+
4
+ export function getCachedDeadAddress(): usize {
5
+ return _cachedDeadAddress;
6
+ }
7
+
8
+ export function setCachedDeadAddress(addr: usize): void {
9
+ _cachedDeadAddress = addr;
10
+ }
11
+
12
+ export function getCachedZeroAddress(): usize {
13
+ return _cachedZeroAddress;
14
+ }
15
+
16
+ export function setCachedZeroAddress(addr: usize): void {
17
+ _cachedZeroAddress = addr;
18
+ }
19
+
20
+ export const DEAD_ARRAY: u8[] = [
21
+ 40, 74, 228, 172, 219, 50, 169, 155, 163, 235, 250, 102, 169, 29, 219, 65, 167, 183, 161, 210,
22
+ 254, 244, 21, 57, 153, 34, 205, 138, 4, 72, 92, 2,
23
+ ];
24
+
25
+ export const ZERO_ARRAY: u8[] = [
26
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27
+ ];
@@ -967,6 +967,7 @@ export class SafeMath {
967
967
  *
968
968
  * @param x - The input value
969
969
  * @returns floor(log2(x)) as u256
970
+ * @throws {Revert} When x is zero (log of zero)
970
971
  *
971
972
  * @example
972
973
  * ```typescript
@@ -975,11 +976,6 @@ export class SafeMath {
975
976
  * const log_1000 = SafeMath.approximateLog2(u256.fromU32(1000)); // Returns 9 (floor of 9.97...)
976
977
  * ```
977
978
  *
978
- * @warning Returns 0 for both input 0 and input 1. While log2(0) is mathematically
979
- * undefined and log2(1) = 0, this implementation returns 0 for both cases
980
- * to avoid reverts and maintain gas efficiency in smart contracts. Callers
981
- * requiring mathematical precision should handle these edge cases explicitly.
982
- *
983
979
  * @security Extensively tested for monotonicity and consistency. Critical for:
984
980
  * - Binary search algorithms in sorted data structures
985
981
  * - Bit manipulation operations requiring position of highest bit
@@ -996,7 +992,7 @@ export class SafeMath {
996
992
  */
997
993
  public static approximateLog2(x: u256): u256 {
998
994
  const bitLen = SafeMath.bitLength256(x);
999
- if (bitLen == 0) return u256.Zero;
995
+ if (bitLen === 0) throw new Revert('SafeMath: log of zero');
1000
996
  return u256.fromU32(bitLen - 1);
1001
997
  }
1002
998
 
@@ -1005,6 +1001,7 @@ export class SafeMath {
1005
1001
  *
1006
1002
  * @param x - The input value (must be ≥ 1)
1007
1003
  * @returns ln(x) scaled by 10^6 for fixed-point precision
1004
+ * @throws {Revert} When x is zero (log of zero)
1008
1005
  *
1009
1006
  * @example
1010
1007
  * ```typescript
@@ -1036,19 +1033,19 @@ export class SafeMath {
1036
1033
  * - Algorithm: Decomposes x = 2^k * (1 + r) where 0 ≤ r < 1
1037
1034
  * - Then: ln(x) = k*ln(2) + ln(1+r)
1038
1035
  * - Uses polyLn1p3 for accurate ln(1+r) approximation
1039
- * - Returns 0 for inputs 0 or 1 (mathematically ln(1) = 0)
1040
1036
  * - Result scaled by 10^6 to maintain 6 decimal places of precision
1041
1037
  * - Gas cost increases logarithmically with input magnitude
1042
1038
  * - Maximum theoretical input: u256.Max (though precision may degrade)
1043
1039
  * - Monotonicity guaranteed across entire input range
1044
1040
  */
1045
1041
  public static preciseLog(x: u256): u256 {
1046
- if (x.isZero() || u256.eq(x, SafeMath.ONE)) {
1047
- return u256.Zero;
1042
+ const bitLen = SafeMath.bitLength256(x);
1043
+
1044
+ if (bitLen === 0) {
1045
+ throw new Revert('SafeMath: log of zero');
1048
1046
  }
1049
1047
 
1050
- const bitLen = SafeMath.bitLength256(x);
1051
- if (bitLen <= 1) {
1048
+ if (bitLen === 1) {
1052
1049
  return u256.Zero;
1053
1050
  }
1054
1051
 
@@ -1079,6 +1076,7 @@ export class SafeMath {
1079
1076
  *
1080
1077
  * @param x - The input value
1081
1078
  * @returns ln(x) scaled by 10^6 for fixed-point precision
1079
+ * @throws {Revert} When x is zero (log of zero)
1082
1080
  *
1083
1081
  * @example
1084
1082
  * ```typescript
@@ -1102,18 +1100,18 @@ export class SafeMath {
1102
1100
  * @remarks
1103
1101
  * - Algorithm: ln(x) ≈ (bitLength(x) - 1) * ln(2)
1104
1102
  * - Exact for all powers of 2
1105
- * - Returns 0 for inputs 0 and 1
1106
1103
  * - Result scaled by 10^6 for 6 decimal places of precision
1107
1104
  * - O(1) complexity, extremely gas efficient
1108
1105
  * - Monotonically non-decreasing (required for security)
1109
1106
  */
1110
1107
  public static approxLog(x: u256): u256 {
1111
- if (x.isZero() || u256.eq(x, SafeMath.ONE)) {
1112
- return u256.Zero;
1108
+ const bitLen: u32 = SafeMath.bitLength256(x);
1109
+
1110
+ if (bitLen === 0) {
1111
+ throw new Revert('SafeMath: log of zero');
1113
1112
  }
1114
1113
 
1115
- const bitLen: u32 = SafeMath.bitLength256(x);
1116
- if (bitLen <= 1) {
1114
+ if (bitLen === 1) {
1117
1115
  return u256.Zero;
1118
1116
  }
1119
1117
 
@@ -1246,7 +1244,10 @@ export class SafeMath {
1246
1244
 
1247
1245
  // Sum and apply final scaling
1248
1246
  const atanhSum: u64 = wScaled + t3 + t5 + t7 + t9;
1249
- return atanhSum << 1; // Multiply by 2 using bit shift
1247
+ const result: u64 = atanhSum << 1; // Multiply by 2 using bit shift
1248
+
1249
+ // Preserve monotonicity for tiny positive inputs that would round to zero
1250
+ return result == 0 ? 1 : result;
1250
1251
  }
1251
1252
 
1252
1253
  /**
@@ -1260,10 +1261,18 @@ export class SafeMath {
1260
1261
  * @param a - Numerator (must be > 0)
1261
1262
  * @param b - Denominator (must be > 0)
1262
1263
  * @returns ln(a/b) * 1,000,000
1264
+ * @throws {Revert} When:
1265
+ * - a is zero (log of zero)
1266
+ * - b is zero (division by zero)
1267
+ * - result is negative (return type is unsigned)
1263
1268
  */
1264
1269
  public static preciseLogRatio(a: u256, b: u256): u256 {
1265
- if (a.isZero() || b.isZero()) {
1266
- return u256.Zero;
1270
+ if (b.isZero()) {
1271
+ throw new Revert('SafeMath: division by zero');
1272
+ }
1273
+
1274
+ if (a.isZero()) {
1275
+ throw new Revert('SafeMath: log of zero');
1267
1276
  }
1268
1277
 
1269
1278
  // If a == b, ln(1) = 0
@@ -1280,8 +1289,7 @@ export class SafeMath {
1280
1289
 
1281
1290
  if (scaledRatio.isZero()) {
1282
1291
  // a/b is very small, return negative (but we only handle positive ln)
1283
- // For a < b, ln(a/b) < 0. Return 0 or handle separately if needed.
1284
- return u256.Zero;
1292
+ throw new Revert('SafeMath: negative log result');
1285
1293
  }
1286
1294
 
1287
1295
  // If scaledRatio == SCALE, then a/b == 1, ln = 0
@@ -1290,9 +1298,8 @@ export class SafeMath {
1290
1298
  }
1291
1299
 
1292
1300
  // If scaledRatio < SCALE (i.e., a < b), ln is negative
1293
- // We only return positive values, so return 0 for this case
1294
1301
  if (u256.lt(scaledRatio, SCALE)) {
1295
- return u256.Zero;
1302
+ throw new Revert('SafeMath: negative log result');
1296
1303
  }
1297
1304
 
1298
1305
  // Now scaledRatio > SCALE, meaning a/b > 1, so ln(a/b) > 0
@@ -1303,11 +1310,11 @@ export class SafeMath {
1303
1310
  // fractionScaled = scaledRatio - SCALE represents fraction * SCALE
1304
1311
  const fractionScaled = SafeMath.sub(scaledRatio, SCALE);
1305
1312
 
1306
- // For small fractions (a/b < 2, i.e., fractionScaled < SCALE), use Taylor series
1313
+ // For small fractions (a/b < 2, i.e., fractionScaled < SCALE), use the stable polyLn1p3 approximation
1307
1314
  // Note: Use strict less-than to ensure ratio = 2 uses the k*ln(2) decomposition
1308
1315
  // This ensures continuity at the boundary - both paths give ln(2) for ratio = 2
1309
1316
  if (u256.lt(fractionScaled, SCALE)) {
1310
- return this.calculateLnOnePlusFraction(fractionScaled, SCALE);
1317
+ return u256.fromU64(SafeMath.polyLn1p3(fractionScaled.toU64()));
1311
1318
  }
1312
1319
 
1313
1320
  // For ratios >= 2, use the decomposition:
@@ -1329,10 +1336,10 @@ export class SafeMath {
1329
1336
  // temp/SCALE is in [1, 2), so (temp - SCALE)/SCALE is in [0, 1)
1330
1337
 
1331
1338
  const normalizedFraction = SafeMath.sub(temp, SCALE);
1332
- const lnNormalized = this.calculateLnOnePlusFraction(normalizedFraction, SCALE);
1339
+ const lnNormalized = SafeMath.polyLn1p3(normalizedFraction.toU64());
1333
1340
  const base = SafeMath.mul(u256.fromU32(k), LN2_SCALED);
1334
1341
 
1335
- return SafeMath.add(base, lnNormalized);
1342
+ return SafeMath.add(base, u256.fromU64(lnNormalized));
1336
1343
  }
1337
1344
 
1338
1345
  /**
@@ -1354,40 +1361,4 @@ export class SafeMath {
1354
1361
  const mMinusX = u256.sub(m, x);
1355
1362
  return u256.ge(x, mMinusX) ? u256.sub(x, mMinusX) : u256.add(x, x);
1356
1363
  }
1357
-
1358
- // ==================== Internal Helper Functions ====================
1359
-
1360
- /**
1361
- * Helper function: Calculate ln(1 + x) where x is provided as xScaled = x * scale
1362
- * Returns the result scaled by scale (i.e., ln(1+x) * scale)
1363
- *
1364
- * Uses Taylor series: ln(1+x) ≈ x - x²/2 + x³/3 - x⁴/4 + x⁵/5
1365
- * Valid for 0 <= x <= 1 (i.e., xScaled <= scale)
1366
- */
1367
- private static calculateLnOnePlusFraction(xScaled: u256, scale: u256): u256 {
1368
- if (xScaled.isZero()) {
1369
- return u256.Zero;
1370
- }
1371
-
1372
- // x² scaled
1373
- const x2 = SafeMath.div(SafeMath.mul(xScaled, xScaled), scale);
1374
-
1375
- // x³ scaled
1376
- const x3 = SafeMath.div(SafeMath.mul(x2, xScaled), scale);
1377
-
1378
- // x⁴ scaled
1379
- const x4 = SafeMath.div(SafeMath.mul(x3, xScaled), scale);
1380
-
1381
- // x⁵ scaled
1382
- const x5 = SafeMath.div(SafeMath.mul(x4, xScaled), scale);
1383
-
1384
- // ln(1+x) ≈ x - x²/2 + x³/3 - x⁴/4 + x⁵/5
1385
- let result = xScaled;
1386
- result = SafeMath.sub(result, SafeMath.div(x2, u256.fromU32(2)));
1387
- result = SafeMath.add(result, SafeMath.div(x3, u256.fromU32(3)));
1388
- result = SafeMath.sub(result, SafeMath.div(x4, u256.fromU32(4)));
1389
- result = SafeMath.add(result, SafeMath.div(x5, u256.fromU32(5)));
1390
-
1391
- return result;
1392
- }
1393
1364
  }
@@ -0,0 +1,44 @@
1
+ import { ExtendedAddress } from './ExtendedAddress';
2
+
3
+ /**
4
+ * Represents a Schnorr signature paired with the signer's ExtendedAddress.
5
+ *
6
+ * This class bundles a 64-byte Schnorr signature with its associated
7
+ * ExtendedAddress (64 bytes), providing a complete signed data structure
8
+ * for Bitcoin Taproot-compatible signatures.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Reading a Schnorr signature from calldata
13
+ * const sig = calldata.readSchnorrSignature();
14
+ * const signer = sig.address;
15
+ * const signature = sig.signature;
16
+ *
17
+ * // Verify the signature
18
+ * const isValid = Blockchain.verifySignature(signer, signature, messageHash);
19
+ * ```
20
+ */
21
+ @final
22
+ export class SchnorrSignature {
23
+ /**
24
+ * The signer's ExtendedAddress (64 bytes).
25
+ * Contains both the tweaked Schnorr public key and ML-DSA key hash.
26
+ */
27
+ public readonly address: ExtendedAddress;
28
+
29
+ /**
30
+ * The 64-byte Schnorr signature.
31
+ */
32
+ public readonly signature: Uint8Array;
33
+
34
+ /**
35
+ * Creates a new SchnorrSignature instance.
36
+ *
37
+ * @param address - The signer's ExtendedAddress
38
+ * @param signature - The 64-byte Schnorr signature
39
+ */
40
+ constructor(address: ExtendedAddress, signature: Uint8Array) {
41
+ this.address = address;
42
+ this.signature = signature;
43
+ }
44
+ }
@@ -1,4 +1,6 @@
1
1
  export const ADDRESS_BYTE_LENGTH: i32 = 32;
2
+ export const EXTENDED_ADDRESS_BYTE_LENGTH: i32 = 64;
3
+ export const SCHNORR_SIGNATURE_BYTE_LENGTH: i32 = 64;
2
4
  export const SELECTOR_BYTE_LENGTH: i32 = 4;
3
5
 
4
6
  export const U256_BYTE_LENGTH: i32 = 32;