@btc-vision/btc-runtime 1.10.11 → 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.
Files changed (41) hide show
  1. package/README.md +48 -224
  2. package/SECURITY.md +38 -191
  3. package/docs/README.md +28 -0
  4. package/docs/advanced/contract-upgrades.md +537 -0
  5. package/docs/advanced/plugins.md +90 -25
  6. package/docs/api-reference/blockchain.md +48 -14
  7. package/docs/api-reference/storage.md +2 -111
  8. package/docs/contracts/op-net-base.md +22 -0
  9. package/docs/contracts/upgradeable.md +396 -0
  10. package/docs/core-concepts/blockchain-environment.md +0 -2
  11. package/docs/core-concepts/security.md +8 -111
  12. package/docs/core-concepts/storage-system.md +1 -32
  13. package/docs/examples/nft-with-reservations.md +8 -238
  14. package/docs/storage/memory-maps.md +1 -44
  15. package/docs/storage/stored-arrays.md +1 -65
  16. package/docs/storage/stored-maps.md +1 -73
  17. package/docs/storage/stored-primitives.md +2 -49
  18. package/docs/types/bytes-writer-reader.md +76 -0
  19. package/docs/types/safe-math.md +2 -45
  20. package/package.json +5 -5
  21. package/runtime/buffer/BytesReader.ts +90 -3
  22. package/runtime/buffer/BytesWriter.ts +81 -3
  23. package/runtime/contracts/OP721.ts +40 -4
  24. package/runtime/contracts/OP_NET.ts +83 -11
  25. package/runtime/contracts/Upgradeable.ts +242 -0
  26. package/runtime/env/BlockchainEnvironment.ts +124 -27
  27. package/runtime/env/global.ts +24 -0
  28. package/runtime/events/upgradeable/UpgradeableEvents.ts +41 -0
  29. package/runtime/generic/AddressMap.ts +20 -18
  30. package/runtime/generic/ExtendedAddressMap.ts +147 -0
  31. package/runtime/generic/MapUint8Array.ts +20 -18
  32. package/runtime/index.ts +8 -0
  33. package/runtime/plugins/Plugin.ts +34 -0
  34. package/runtime/plugins/UpgradeablePlugin.ts +279 -0
  35. package/runtime/storage/BaseStoredString.ts +1 -1
  36. package/runtime/storage/arrays/StoredPackedArray.ts +4 -0
  37. package/runtime/types/ExtendedAddress.ts +36 -24
  38. package/runtime/types/ExtendedAddressCache.ts +27 -0
  39. package/runtime/types/SafeMath.ts +109 -18
  40. package/runtime/types/SchnorrSignature.ts +44 -0
  41. package/runtime/utils/lengths.ts +2 -0
@@ -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,10 +1244,103 @@ 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
- // ==================== Internal Helper Functions ====================
1253
+ /**
1254
+ * Calculate ln(a/b) with precision, avoiding bit-length mismatch issues.
1255
+ * Returns the result scaled by 1e6 (i.e., ln(a/b) * 1,000,000)
1256
+ *
1257
+ * This function correctly handles the case where a and b have different
1258
+ * bit lengths, which would cause incorrect results if computing
1259
+ * preciseLog(a) - preciseLog(b) directly.
1260
+ *
1261
+ * @param a - Numerator (must be > 0)
1262
+ * @param b - Denominator (must be > 0)
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)
1268
+ */
1269
+ public static preciseLogRatio(a: u256, b: u256): u256 {
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');
1276
+ }
1277
+
1278
+ // If a == b, ln(1) = 0
1279
+ if (u256.eq(a, b)) {
1280
+ return u256.Zero;
1281
+ }
1282
+
1283
+ const SCALE = u256.fromU64(1_000_000);
1284
+ const LN2_SCALED = u256.fromU64(693147); // ln(2) * 1e6
1285
+
1286
+ // Compute ratio = a / b with scaling to preserve precision
1287
+ // scaledRatio = (a * SCALE) / b represents (a/b) * SCALE
1288
+ const scaledRatio = SafeMath.div(SafeMath.mul(a, SCALE), b);
1289
+
1290
+ if (scaledRatio.isZero()) {
1291
+ // a/b is very small, return negative (but we only handle positive ln)
1292
+ throw new Revert('SafeMath: negative log result');
1293
+ }
1294
+
1295
+ // If scaledRatio == SCALE, then a/b == 1, ln = 0
1296
+ if (u256.eq(scaledRatio, SCALE)) {
1297
+ return u256.Zero;
1298
+ }
1299
+
1300
+ // If scaledRatio < SCALE (i.e., a < b), ln is negative
1301
+ if (u256.lt(scaledRatio, SCALE)) {
1302
+ throw new Revert('SafeMath: negative log result');
1303
+ }
1304
+
1305
+ // Now scaledRatio > SCALE, meaning a/b > 1, so ln(a/b) > 0
1306
+ // We want to compute ln(scaledRatio / SCALE) = ln(scaledRatio) - ln(SCALE)
1307
+ // But we do this correctly by computing ln(1 + (scaledRatio - SCALE) / SCALE)
1308
+
1309
+ // fraction = (scaledRatio - SCALE) / SCALE = (a/b - 1)
1310
+ // fractionScaled = scaledRatio - SCALE represents fraction * SCALE
1311
+ const fractionScaled = SafeMath.sub(scaledRatio, SCALE);
1312
+
1313
+ // For small fractions (a/b < 2, i.e., fractionScaled < SCALE), use the stable polyLn1p3 approximation
1314
+ // Note: Use strict less-than to ensure ratio = 2 uses the k*ln(2) decomposition
1315
+ // This ensures continuity at the boundary - both paths give ln(2) for ratio = 2
1316
+ if (u256.lt(fractionScaled, SCALE)) {
1317
+ return u256.fromU64(SafeMath.polyLn1p3(fractionScaled.toU64()));
1318
+ }
1319
+
1320
+ // For ratios >= 2, use the decomposition:
1321
+ // ln(a/b) = k * ln(2) + ln(normalized)
1322
+ // where normalized = (a/b) / 2^k is in range [1, 2)
1323
+
1324
+ // Find k such that scaledRatio / 2^k is in [SCALE, 2*SCALE)
1325
+ let temp = scaledRatio;
1326
+ let k: u32 = 0;
1327
+ const twoScale = SafeMath.mul(SCALE, u256.fromU32(2));
1328
+
1329
+ while (u256.ge(temp, twoScale)) {
1330
+ temp = SafeMath.shr(temp, 1);
1331
+ k++;
1332
+ }
1333
+
1334
+ // Now temp is in [SCALE, 2*SCALE), representing a value in [1, 2)
1335
+ // ln(a/b) = k * ln(2) + ln(temp/SCALE)
1336
+ // temp/SCALE is in [1, 2), so (temp - SCALE)/SCALE is in [0, 1)
1337
+
1338
+ const normalizedFraction = SafeMath.sub(temp, SCALE);
1339
+ const lnNormalized = SafeMath.polyLn1p3(normalizedFraction.toU64());
1340
+ const base = SafeMath.mul(u256.fromU32(k), LN2_SCALED);
1341
+
1342
+ return SafeMath.add(base, u256.fromU64(lnNormalized));
1343
+ }
1253
1344
 
1254
1345
  /**
1255
1346
  * @internal
@@ -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;