@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.
- package/README.md +48 -224
- package/SECURITY.md +38 -191
- package/docs/README.md +28 -0
- package/docs/advanced/contract-upgrades.md +537 -0
- package/docs/advanced/plugins.md +90 -25
- package/docs/api-reference/blockchain.md +48 -14
- package/docs/contracts/op-net-base.md +22 -0
- package/docs/contracts/upgradeable.md +396 -0
- package/docs/core-concepts/blockchain-environment.md +0 -2
- package/docs/types/bytes-writer-reader.md +76 -0
- package/package.json +5 -5
- package/runtime/buffer/BytesReader.ts +90 -3
- package/runtime/buffer/BytesWriter.ts +81 -3
- package/runtime/contracts/OP721.ts +40 -4
- package/runtime/contracts/OP_NET.ts +83 -11
- package/runtime/contracts/Upgradeable.ts +242 -0
- package/runtime/env/BlockchainEnvironment.ts +124 -27
- package/runtime/env/global.ts +24 -0
- package/runtime/events/upgradeable/UpgradeableEvents.ts +41 -0
- package/runtime/generic/AddressMap.ts +20 -18
- package/runtime/generic/ExtendedAddressMap.ts +147 -0
- package/runtime/generic/MapUint8Array.ts +20 -18
- package/runtime/index.ts +8 -0
- package/runtime/plugins/Plugin.ts +34 -0
- package/runtime/plugins/UpgradeablePlugin.ts +279 -0
- package/runtime/storage/BaseStoredString.ts +1 -1
- package/runtime/storage/arrays/StoredPackedArray.ts +4 -0
- package/runtime/types/ExtendedAddress.ts +36 -24
- package/runtime/types/ExtendedAddressCache.ts +27 -0
- package/runtime/types/SafeMath.ts +34 -63
- package/runtime/types/SchnorrSignature.ts +44 -0
- 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
|
|
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
|
-
|
|
1047
|
-
|
|
1042
|
+
const bitLen = SafeMath.bitLength256(x);
|
|
1043
|
+
|
|
1044
|
+
if (bitLen === 0) {
|
|
1045
|
+
throw new Revert('SafeMath: log of zero');
|
|
1048
1046
|
}
|
|
1049
1047
|
|
|
1050
|
-
|
|
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
|
-
|
|
1112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
1266
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
+
}
|
package/runtime/utils/lengths.ts
CHANGED