@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.
- 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/api-reference/storage.md +2 -111
- 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/core-concepts/security.md +8 -111
- package/docs/core-concepts/storage-system.md +1 -32
- package/docs/examples/nft-with-reservations.md +8 -238
- package/docs/storage/memory-maps.md +1 -44
- package/docs/storage/stored-arrays.md +1 -65
- package/docs/storage/stored-maps.md +1 -73
- package/docs/storage/stored-primitives.md +2 -49
- package/docs/types/bytes-writer-reader.md +76 -0
- package/docs/types/safe-math.md +2 -45
- 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 +109 -18
- 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,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
|
-
|
|
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
|
+
/**
|
|
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
|
+
}
|
package/runtime/utils/lengths.ts
CHANGED