@btc-vision/btc-runtime 1.8.1 → 1.9.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.
- package/README.md +57 -79
- package/package.json +8 -8
- package/runtime/buffer/BytesReader.ts +36 -45
- package/runtime/buffer/BytesWriter.ts +60 -20
- package/runtime/contracts/OP20.ts +636 -0
- package/runtime/contracts/OP_NET.ts +3 -3
- package/runtime/contracts/interfaces/IOP20.ts +15 -0
- package/runtime/contracts/interfaces/OP20InitParameters.ts +3 -1
- package/runtime/env/BlockchainEnvironment.ts +28 -7
- package/runtime/env/classes/Transaction.ts +1 -2
- package/runtime/env/global.ts +171 -23
- package/runtime/events/NetEvent.ts +1 -1
- package/runtime/events/predefined/{ApproveEvent.ts → ApprovedEvent.ts} +3 -3
- package/runtime/events/predefined/{MintEvent.ts → BurnedEvent.ts} +5 -6
- package/runtime/events/predefined/{TransferEvent.ts → MintedEvent.ts} +5 -6
- package/runtime/events/predefined/TransferredEvent.ts +18 -0
- package/runtime/events/predefined/index.ts +4 -4
- package/runtime/exports/index.ts +4 -4
- package/runtime/index.ts +13 -3
- package/runtime/math/abi.ts +10 -6
- package/runtime/math/bytes.ts +5 -17
- package/runtime/memory/AddressMemoryMap.ts +4 -5
- package/runtime/memory/KeyMerger.ts +7 -7
- package/runtime/memory/MapOfMap.ts +2 -7
- package/runtime/memory/Nested.ts +6 -8
- package/runtime/nested/PointerManager.ts +1 -1
- package/runtime/nested/codecs/AddressCodec.ts +1 -1
- package/runtime/nested/codecs/BooleanCodec.ts +1 -1
- package/runtime/nested/codecs/Ids.ts +1 -1
- package/runtime/nested/codecs/NumericCodec.ts +1 -1
- package/runtime/nested/codecs/StringCodec.ts +1 -1
- package/runtime/nested/codecs/VariableBytesCodec.ts +3 -3
- package/runtime/nested/storage/StorageMap.ts +3 -3
- package/runtime/nested/storage/StorageSet.ts +2 -2
- package/runtime/plugins/Plugin.ts +5 -7
- package/runtime/script/Bech32.ts +369 -0
- package/runtime/script/BitcoinAddresses.ts +208 -0
- package/runtime/script/BitcoinCodec.ts +395 -0
- package/runtime/script/Networks.ts +94 -0
- package/runtime/script/Opcodes.ts +155 -0
- package/runtime/script/Script.ts +463 -0
- package/runtime/script/ScriptUtils.ts +101 -0
- package/runtime/script/Segwit.ts +185 -0
- package/runtime/script/reader/ScriptReader.ts +247 -0
- package/runtime/secp256k1/ECPoint.ts +6 -12
- package/runtime/shared-libraries/TransferHelper.ts +72 -31
- package/runtime/storage/AdvancedStoredString.ts +1 -1
- package/runtime/storage/StoredAddress.ts +1 -1
- package/runtime/storage/StoredBoolean.ts +2 -4
- package/runtime/storage/StoredString.ts +6 -3
- package/runtime/storage/StoredU256.ts +1 -3
- package/runtime/storage/StoredU32.ts +1 -6
- package/runtime/storage/StoredU64.ts +1 -4
- package/runtime/storage/arrays/StoredBooleanArray.ts +7 -12
- package/runtime/storage/arrays/StoredPackedArray.ts +3 -13
- package/runtime/storage/maps/StoredMapU256.ts +5 -5
- package/runtime/types/Address.ts +49 -39
- package/runtime/types/SafeMath.ts +19 -18
- package/runtime/types/SafeMathI128.ts +14 -13
- package/runtime/utils/hex.ts +41 -19
- package/runtime/utils/index.ts +0 -2
- package/runtime/contracts/DeployableOP_20.ts +0 -415
- package/runtime/contracts/OP_20.ts +0 -9
- package/runtime/contracts/interfaces/IOP_20.ts +0 -19
- package/runtime/events/predefined/BurnEvent.ts +0 -14
- package/runtime/utils/b32.ts +0 -243
- package/runtime/utils/box.ts +0 -134
- package/runtime/utils/encodings.ts +0 -45
- /package/{LICENSE → LICENSE.md} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BytesWriter } from '../buffer/BytesWriter';
|
|
2
2
|
import { Blockchain } from '../env';
|
|
3
3
|
import { encodePointerUnknownLength } from '../math/abi';
|
|
4
|
-
|
|
4
|
+
import { Revert } from '../types/Revert';
|
|
5
5
|
|
|
6
6
|
@final
|
|
7
7
|
export class KeyMerger<K extends string, K2 extends string, V extends Uint8Array> {
|
|
@@ -22,24 +22,24 @@ export class KeyMerger<K extends string, K2 extends string, V extends Uint8Array
|
|
|
22
22
|
return this;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
public get(key:
|
|
25
|
+
public get(key: K2): Uint8Array {
|
|
26
26
|
const mergedKey: string = this.mergeKey(key);
|
|
27
27
|
return Blockchain.getStorageAt(this.encodePointer(mergedKey));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
public has(key:
|
|
30
|
+
public has(key: K2): bool {
|
|
31
31
|
const mergedKey: string = this.mergeKey(key);
|
|
32
32
|
return Blockchain.hasStorageAt(this.encodePointer(mergedKey));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
@unsafe
|
|
36
|
-
public delete(_key:
|
|
37
|
-
throw new
|
|
36
|
+
public delete(_key: K2): bool {
|
|
37
|
+
throw new Revert('Method not implemented.');
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
@unsafe
|
|
41
41
|
public clear(): void {
|
|
42
|
-
throw new
|
|
42
|
+
throw new Revert('Clear method not implemented.');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -49,7 +49,7 @@ export class KeyMerger<K extends string, K2 extends string, V extends Uint8Array
|
|
|
49
49
|
* parentKey = "ab", key = "cdef" => "2:ab4:cdef"
|
|
50
50
|
*/
|
|
51
51
|
@inline
|
|
52
|
-
private mergeKey(key:
|
|
52
|
+
private mergeKey(key: K2): string {
|
|
53
53
|
return `${this.parentKey.length}:${this.parentKey}${key.length}:${key}`;
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -2,15 +2,10 @@ import { Address } from '../types/Address';
|
|
|
2
2
|
import { Nested } from './Nested';
|
|
3
3
|
|
|
4
4
|
@final
|
|
5
|
-
export class MapOfMap<T> extends Map<
|
|
6
|
-
Address,
|
|
7
|
-
Nested<T>
|
|
8
|
-
> {
|
|
5
|
+
export class MapOfMap<T> extends Map<Address, Nested<T>> {
|
|
9
6
|
public pointer: u16;
|
|
10
7
|
|
|
11
|
-
constructor(
|
|
12
|
-
pointer: u16,
|
|
13
|
-
) {
|
|
8
|
+
constructor(pointer: u16) {
|
|
14
9
|
super();
|
|
15
10
|
|
|
16
11
|
this.pointer = pointer;
|
package/runtime/memory/Nested.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { encodePointerUnknownLength } from '../math/abi';
|
|
|
3
3
|
import { BytesWriter } from '../buffer/BytesWriter';
|
|
4
4
|
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
5
5
|
import { Address } from '../types/Address';
|
|
6
|
+
import { Revert } from '../types/Revert';
|
|
6
7
|
|
|
7
8
|
@final
|
|
8
9
|
export class Nested<T> {
|
|
@@ -10,10 +11,7 @@ export class Nested<T> {
|
|
|
10
11
|
|
|
11
12
|
public pointer: u16;
|
|
12
13
|
|
|
13
|
-
constructor(
|
|
14
|
-
parent: Uint8Array,
|
|
15
|
-
pointer: u16,
|
|
16
|
-
) {
|
|
14
|
+
constructor(parent: Uint8Array, pointer: u16) {
|
|
17
15
|
this.pointer = pointer;
|
|
18
16
|
|
|
19
17
|
this.parentKey = parent;
|
|
@@ -40,12 +38,12 @@ export class Nested<T> {
|
|
|
40
38
|
|
|
41
39
|
@unsafe
|
|
42
40
|
public delete(_key: Uint8Array): bool {
|
|
43
|
-
throw new
|
|
41
|
+
throw new Revert('Method not implemented.');
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
@unsafe
|
|
47
45
|
public clear(): void {
|
|
48
|
-
throw new
|
|
46
|
+
throw new Revert('Clear method not implemented.');
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
/**
|
|
@@ -70,7 +68,7 @@ export class Nested<T> {
|
|
|
70
68
|
return changetype<T>(String.UTF8.decode(value.buffer));
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
throw new
|
|
71
|
+
throw new Revert('Unsupported type');
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
/**
|
|
@@ -95,7 +93,7 @@ export class Nested<T> {
|
|
|
95
93
|
return Uint8Array.wrap(String.UTF8.encode(str));
|
|
96
94
|
}
|
|
97
95
|
|
|
98
|
-
throw new
|
|
96
|
+
throw new Revert('Unsupported type');
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
private getKeyHash(key: Uint8Array): Uint8Array {
|
|
@@ -21,7 +21,7 @@ export class IVariableBytesCodec implements ICodec<Uint8Array> {
|
|
|
21
21
|
|
|
22
22
|
// If remaining > 0, each chunk is 32 bytes.
|
|
23
23
|
// So total chunks needed = 1 (for first chunk) + ceil(remaining / 32).
|
|
24
|
-
const additionalChunks: u32 = remaining == 0 ? 0 : (
|
|
24
|
+
const additionalChunks: u32 = remaining == 0 ? 0 : (remaining + 32 - 1) / 32;
|
|
25
25
|
const totalChunks: u32 = 1 + additionalChunks;
|
|
26
26
|
|
|
27
27
|
// 1) Allocate `totalChunks` from PointerManager
|
|
@@ -52,7 +52,7 @@ export class IVariableBytesCodec implements ICodec<Uint8Array> {
|
|
|
52
52
|
for (let j: u32 = 0; j < chunkSize; j++) {
|
|
53
53
|
chunk[j] = value[offset + j];
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
offset += chunkSize;
|
|
57
57
|
remaining -= chunkSize;
|
|
58
58
|
|
|
@@ -131,4 +131,4 @@ function isAllZero(arr: Uint8Array): bool {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export const idOfVariableBytes = idof<IVariableBytesCodec>();
|
|
134
|
-
export const VariableBytesCodec = new IVariableBytesCodec();
|
|
134
|
+
export const VariableBytesCodec = new IVariableBytesCodec();
|
|
@@ -4,7 +4,7 @@ import { BytesReader } from '../../buffer/BytesReader';
|
|
|
4
4
|
import { encodePointerUnknownLength } from '../../math/abi';
|
|
5
5
|
|
|
6
6
|
import { i128, u128, u256 } from '@btc-vision/as-bignum/assembly';
|
|
7
|
-
import { idOfAddress, idOfI128, idOfString, idOfU128, idOfU256, idOfUint8Array } from '../codecs/Ids';
|
|
7
|
+
import { idOfAddress, idOfI128, idOfString, idOfU128, idOfU256, idOfUint8Array, } from '../codecs/Ids';
|
|
8
8
|
|
|
9
9
|
import { AddressCodec } from '../codecs/AddressCodec';
|
|
10
10
|
import { BooleanCodec } from '../codecs/BooleanCodec';
|
|
@@ -66,7 +66,7 @@ export class StorageMap<K, V> {
|
|
|
66
66
|
@unsafe
|
|
67
67
|
public clear(): void {
|
|
68
68
|
// Not implemented: we'd need key-tracking to remove them all
|
|
69
|
-
throw new
|
|
69
|
+
throw new Revert('clear() not implemented; no key-tracking logic here.');
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// ----------------------------------------------------------
|
|
@@ -224,4 +224,4 @@ export class StorageMap<K, V> {
|
|
|
224
224
|
|
|
225
225
|
throw new Revert('encodeValue: Unsupported type');
|
|
226
226
|
}
|
|
227
|
-
}
|
|
227
|
+
}
|
|
@@ -2,7 +2,7 @@ import { ICodec } from '../interfaces/ICodec';
|
|
|
2
2
|
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
3
3
|
import { encodePointerUnknownLength } from '../../math/abi';
|
|
4
4
|
import { Blockchain } from '../../env';
|
|
5
|
-
|
|
5
|
+
import { Revert } from '../../types/Revert';
|
|
6
6
|
|
|
7
7
|
export class StorageSet<T> {
|
|
8
8
|
private readonly pointer: u16;
|
|
@@ -43,7 +43,7 @@ export class StorageSet<T> {
|
|
|
43
43
|
|
|
44
44
|
@unsafe
|
|
45
45
|
public clear(): void {
|
|
46
|
-
throw new
|
|
46
|
+
throw new Revert('clear() not implemented.');
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
private getStorageKey(value: T): Uint8Array {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { Calldata } from '../types';
|
|
2
|
+
import { Selector } from '../math/abi';
|
|
2
3
|
|
|
3
4
|
export class Plugin {
|
|
4
|
-
public onDeployment(_calldata: Calldata): void {
|
|
5
|
-
}
|
|
5
|
+
public onDeployment(_calldata: Calldata): void {}
|
|
6
6
|
|
|
7
|
-
public onExecutionStarted(): void {
|
|
8
|
-
}
|
|
7
|
+
public onExecutionStarted(_selector: Selector, _calldata: Calldata): void {}
|
|
9
8
|
|
|
10
|
-
public onExecutionCompleted(): void {
|
|
11
|
-
|
|
12
|
-
}
|
|
9
|
+
public onExecutionCompleted(_selector: Selector, _calldata: Calldata): void {}
|
|
10
|
+
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
2
|
+
import { SegwitDecoded } from './ScriptUtils';
|
|
3
|
+
import { Revert } from '../types/Revert';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Result type for Bech32 operations that can fail
|
|
7
|
+
*/
|
|
8
|
+
@final
|
|
9
|
+
export class Bech32Result<T> {
|
|
10
|
+
public readonly success: bool;
|
|
11
|
+
public readonly value: T | null;
|
|
12
|
+
public readonly error: string | null;
|
|
13
|
+
|
|
14
|
+
public constructor(success: bool, value: T | null, error: string | null) {
|
|
15
|
+
this.success = success;
|
|
16
|
+
this.value = value;
|
|
17
|
+
this.error = error;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public static ok<T>(value: T): Bech32Result<T> {
|
|
21
|
+
return new Bech32Result<T>(true, value, null);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public static err<T>(error: string): Bech32Result<T> {
|
|
25
|
+
return new Bech32Result<T>(false, null, error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@final
|
|
30
|
+
export class Bech32 {
|
|
31
|
+
private static readonly CHARSET: string = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
|
|
32
|
+
private static decodeTbl: Int8Array | null = null;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Consider using encodeOrNull instead, this method will throw on error
|
|
36
|
+
* @param hrp
|
|
37
|
+
* @param witver
|
|
38
|
+
* @param program
|
|
39
|
+
*/
|
|
40
|
+
public static encode(hrp: string, witver: i32, program: Uint8Array): string {
|
|
41
|
+
const result = Bech32.encodeOrNull(hrp, witver, program);
|
|
42
|
+
if (!result) {
|
|
43
|
+
throw new Revert('Bech32 encoding failed');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return result as string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Safe encode that returns null on error instead of throwing
|
|
51
|
+
*/
|
|
52
|
+
public static encodeOrNull(hrp: string, witver: i32, program: Uint8Array): string | null {
|
|
53
|
+
// Validate inputs
|
|
54
|
+
if (witver < 0 || witver > 16) return null;
|
|
55
|
+
if (witver == 0 && program.length != 20 && program.length != 32) return null;
|
|
56
|
+
if (witver != 0 && (program.length < 2 || program.length > 40)) return null;
|
|
57
|
+
|
|
58
|
+
// Build the 5-bit words array
|
|
59
|
+
const wordsWB = new BytesWriter(1 + (program.length * 8 + 4) / 5);
|
|
60
|
+
wordsWB.writeU8(<u8>witver);
|
|
61
|
+
|
|
62
|
+
const convertResult = Bech32.convertBitsSafe(program, 8, 5, true);
|
|
63
|
+
if (!convertResult.success) return null;
|
|
64
|
+
|
|
65
|
+
if (convertResult.value === null) {
|
|
66
|
+
throw new Revert('Bech32 convertBits failed with unknown error');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
wordsWB.writeBytes(convertResult.value as Uint8Array);
|
|
70
|
+
|
|
71
|
+
const data = wordsWB.getBuffer().subarray(0, <i32>wordsWB.getOffset());
|
|
72
|
+
const chk = Bech32.createChecksum(hrp, data, /*bech32m*/ witver != 0);
|
|
73
|
+
|
|
74
|
+
// Build final output
|
|
75
|
+
const totalLen = hrp.length + 1 + data.length + chk.length;
|
|
76
|
+
const outWB = new BytesWriter(totalLen);
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < hrp.length; i++) {
|
|
79
|
+
outWB.writeU8(<u8>hrp.charCodeAt(i));
|
|
80
|
+
}
|
|
81
|
+
outWB.writeU8(49); // '1' separator
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < data.length; i++) {
|
|
84
|
+
outWB.writeU8(<u8>Bech32.CHARSET.charCodeAt(data[i]));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < chk.length; i++) {
|
|
88
|
+
outWB.writeU8(<u8>Bech32.CHARSET.charCodeAt(chk[i]));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return String.UTF8.decode(outWB.getBuffer().buffer);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Consider using decodeOrNull instead, this method will throw on error
|
|
96
|
+
* @param addr
|
|
97
|
+
*/
|
|
98
|
+
public static decode(addr: string): SegwitDecoded {
|
|
99
|
+
const result = Bech32.decodeOrNull(addr);
|
|
100
|
+
if (!result) {
|
|
101
|
+
throw new Revert('Bech32 decode failed');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return result as SegwitDecoded;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Safe decode that returns null on error
|
|
109
|
+
*/
|
|
110
|
+
public static decodeOrNull(addr: string): SegwitDecoded | null {
|
|
111
|
+
const L = addr.length;
|
|
112
|
+
|
|
113
|
+
// Validate basic length constraints
|
|
114
|
+
if (L < 8 || L > 90) return null;
|
|
115
|
+
|
|
116
|
+
let lower = false;
|
|
117
|
+
let upper = false;
|
|
118
|
+
let pos = -1;
|
|
119
|
+
|
|
120
|
+
// Scan for separator and validate characters
|
|
121
|
+
for (let i = 0; i < L; i++) {
|
|
122
|
+
const c = addr.charCodeAt(i);
|
|
123
|
+
if (c < 33 || c > 126) return null;
|
|
124
|
+
|
|
125
|
+
if (c >= 97 && c <= 122) lower = true;
|
|
126
|
+
else if (c >= 65 && c <= 90) upper = true;
|
|
127
|
+
|
|
128
|
+
if (c == 49) pos = i; // '1' separator
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check case consistency
|
|
132
|
+
if (lower && upper) return null;
|
|
133
|
+
|
|
134
|
+
// Validate separator position
|
|
135
|
+
if (pos < 1 || pos + 7 > L) return null;
|
|
136
|
+
|
|
137
|
+
// Extract HRP and normalize to lowercase
|
|
138
|
+
const hrp = lower ? addr.substring(0, pos) : addr.substring(0, pos).toLowerCase();
|
|
139
|
+
|
|
140
|
+
// Decode the data part
|
|
141
|
+
const dpLen = L - pos - 1;
|
|
142
|
+
const data = new Uint8Array(dpLen);
|
|
143
|
+
|
|
144
|
+
Bech32.ensureTable();
|
|
145
|
+
const tbl = changetype<Int8Array>(Bech32.decodeTbl);
|
|
146
|
+
|
|
147
|
+
for (let i = 0; i < dpLen; i++) {
|
|
148
|
+
let c = addr.charCodeAt(pos + 1 + i);
|
|
149
|
+
// Normalize to lowercase if needed
|
|
150
|
+
if (!lower && c >= 65 && c <= 90) c += 32;
|
|
151
|
+
|
|
152
|
+
const v = c < 128 ? unchecked(tbl[c]) : -1;
|
|
153
|
+
if (v < 0) return null;
|
|
154
|
+
|
|
155
|
+
data[i] = <u8>v;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Verify checksum
|
|
159
|
+
const w = new BytesWriter(hrp.length * 2 + 1 + dpLen);
|
|
160
|
+
w.writeBytes(Bech32.hrpExpand(hrp));
|
|
161
|
+
w.writeBytes(data);
|
|
162
|
+
|
|
163
|
+
const pm = Bech32.polymod(w.getBuffer().subarray(0, <i32>w.getOffset()));
|
|
164
|
+
const witver = <i32>data[0];
|
|
165
|
+
|
|
166
|
+
if (witver < 0 || witver > 16) return null;
|
|
167
|
+
|
|
168
|
+
// Check if checksum matches for either bech32 or bech32m
|
|
169
|
+
let constOK = false;
|
|
170
|
+
if ((pm ^ 1) == 0) constOK = witver == 0; // bech32 for v0
|
|
171
|
+
if ((pm ^ 0x2bc830a3) == 0) constOK = witver != 0; // bech32m for v1+
|
|
172
|
+
|
|
173
|
+
if (!constOK) return null;
|
|
174
|
+
|
|
175
|
+
// Extract witness program
|
|
176
|
+
const words = data.subarray(0, dpLen - 6);
|
|
177
|
+
const progWords = words.subarray(1);
|
|
178
|
+
|
|
179
|
+
const convertResult = Bech32.convertBitsSafe(progWords, 5, 8, false);
|
|
180
|
+
if (!convertResult.success) return null;
|
|
181
|
+
|
|
182
|
+
const program = convertResult.value!;
|
|
183
|
+
|
|
184
|
+
// Validate program length based on witness version
|
|
185
|
+
if (witver == 0) {
|
|
186
|
+
const Lp = program.length;
|
|
187
|
+
if (Lp != 20 && Lp != 32) return null;
|
|
188
|
+
} else {
|
|
189
|
+
const Lp = program.length;
|
|
190
|
+
if (Lp < 2 || Lp > 40) return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return new SegwitDecoded(hrp, witver, program);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Ensure the decode table is initialized
|
|
198
|
+
* This is safe to call multiple times
|
|
199
|
+
*/
|
|
200
|
+
private static ensureTable(): void {
|
|
201
|
+
if (Bech32.decodeTbl) return;
|
|
202
|
+
|
|
203
|
+
const t = new Int8Array(128);
|
|
204
|
+
for (let i = 0; i < 128; i++) {
|
|
205
|
+
unchecked((t[i] = -1));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for (let i = 0; i < 32; i++) {
|
|
209
|
+
const c = Bech32.CHARSET.charCodeAt(i);
|
|
210
|
+
if (c < 128) {
|
|
211
|
+
unchecked((t[c] = <i8>i));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
Bech32.decodeTbl = t;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Expand human-readable part for checksum computation
|
|
220
|
+
*/
|
|
221
|
+
private static hrpExpand(hrp: string): Uint8Array {
|
|
222
|
+
const L = hrp.length;
|
|
223
|
+
const w = new BytesWriter(L * 2 + 1);
|
|
224
|
+
|
|
225
|
+
// High bits of each character
|
|
226
|
+
for (let i = 0; i < L; i++) {
|
|
227
|
+
w.writeU8(<u8>(hrp.charCodeAt(i) >>> 5));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
w.writeU8(0); // Separator
|
|
231
|
+
|
|
232
|
+
// Low bits of each character
|
|
233
|
+
for (let i = 0; i < L; i++) {
|
|
234
|
+
w.writeU8(<u8>(hrp.charCodeAt(i) & 31));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return w.getBuffer();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Compute Bech32 checksum polymod
|
|
242
|
+
*/
|
|
243
|
+
private static polymod(values: Uint8Array): u32 {
|
|
244
|
+
let chk: u32 = 1;
|
|
245
|
+
|
|
246
|
+
// Generator polynomial coefficients
|
|
247
|
+
const G0: u32 = 0x3b6a57b2;
|
|
248
|
+
const G1: u32 = 0x26508e6d;
|
|
249
|
+
const G2: u32 = 0x1ea119fa;
|
|
250
|
+
const G3: u32 = 0x3d4233dd;
|
|
251
|
+
const G4: u32 = 0x2a1462b3;
|
|
252
|
+
|
|
253
|
+
const L = values.length;
|
|
254
|
+
for (let i = 0; i < L; i++) {
|
|
255
|
+
const top = chk >>> 25;
|
|
256
|
+
chk = ((chk & 0x1ffffff) << 5) ^ (<u32>unchecked(values[i]));
|
|
257
|
+
|
|
258
|
+
if ((top & 1) != 0) chk ^= G0;
|
|
259
|
+
if ((top & 2) != 0) chk ^= G1;
|
|
260
|
+
if ((top & 4) != 0) chk ^= G2;
|
|
261
|
+
if ((top & 8) != 0) chk ^= G3;
|
|
262
|
+
if ((top & 16) != 0) chk ^= G4;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return chk;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Consider using convertBitsSafe instead, this method will throw on error
|
|
270
|
+
*/
|
|
271
|
+
private static convertBits(
|
|
272
|
+
data: Uint8Array,
|
|
273
|
+
fromBits: i32,
|
|
274
|
+
toBits: i32,
|
|
275
|
+
pad: bool,
|
|
276
|
+
): Uint8Array {
|
|
277
|
+
const result = Bech32.convertBitsSafe(data, fromBits, toBits, pad);
|
|
278
|
+
if (!result.success) {
|
|
279
|
+
if (!result.error) {
|
|
280
|
+
throw new Revert('Bech32 convertBits failed with unknown error');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
throw new Revert(result.error);
|
|
284
|
+
}
|
|
285
|
+
return result.value!;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Safe version of convertBits that returns a result object
|
|
290
|
+
*/
|
|
291
|
+
private static convertBitsSafe(
|
|
292
|
+
data: Uint8Array,
|
|
293
|
+
fromBits: i32,
|
|
294
|
+
toBits: i32,
|
|
295
|
+
pad: bool,
|
|
296
|
+
): Bech32Result<Uint8Array> {
|
|
297
|
+
let acc: i32 = 0;
|
|
298
|
+
let bits: i32 = 0;
|
|
299
|
+
const maxv = (1 << toBits) - 1;
|
|
300
|
+
|
|
301
|
+
// Calculate output length
|
|
302
|
+
const outLen64: i64 = pad
|
|
303
|
+
? (<i64>data.length * fromBits + (toBits - 1)) / toBits
|
|
304
|
+
: (<i64>data.length * fromBits) / toBits;
|
|
305
|
+
|
|
306
|
+
if (outLen64 > <i64>0x7fffffff) {
|
|
307
|
+
return Bech32Result.err<Uint8Array>('convertBits outLen overflow');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const wb = new BytesWriter(<i32>outLen64);
|
|
311
|
+
const L = data.length;
|
|
312
|
+
|
|
313
|
+
for (let i = 0; i < L; i++) {
|
|
314
|
+
const v = <i32>unchecked(data[i]);
|
|
315
|
+
|
|
316
|
+
// Validate input value
|
|
317
|
+
if (v < 0 || v >> fromBits != 0) {
|
|
318
|
+
return Bech32Result.err<Uint8Array>('invalid value');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
acc = (acc << fromBits) | v;
|
|
322
|
+
bits += fromBits;
|
|
323
|
+
|
|
324
|
+
while (bits >= toBits) {
|
|
325
|
+
bits -= toBits;
|
|
326
|
+
wb.writeU8(<u8>((acc >> bits) & maxv));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (pad) {
|
|
331
|
+
if (bits > 0) {
|
|
332
|
+
wb.writeU8(<u8>((acc << (toBits - bits)) & maxv));
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
if (bits >= fromBits) {
|
|
336
|
+
return Bech32Result.err<Uint8Array>('excess padding');
|
|
337
|
+
}
|
|
338
|
+
if (((acc << (toBits - bits)) & maxv) != 0) {
|
|
339
|
+
return Bech32Result.err<Uint8Array>('non-zero padding');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return Bech32Result.ok<Uint8Array>(wb.getBuffer().subarray(0, <i32>wb.getOffset()));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private static createChecksum(hrp: string, data: Uint8Array, bech32m: bool): Uint8Array {
|
|
347
|
+
const hrpEx = Bech32.hrpExpand(hrp);
|
|
348
|
+
const w = new BytesWriter(hrpEx.length + data.length + 6);
|
|
349
|
+
|
|
350
|
+
w.writeBytes(hrpEx);
|
|
351
|
+
w.writeBytes(data);
|
|
352
|
+
|
|
353
|
+
// Add 6 zero bytes for checksum calculation
|
|
354
|
+
for (let i = 0; i < 6; i++) {
|
|
355
|
+
w.writeU8(0);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const pm =
|
|
359
|
+
Bech32.polymod(w.getBuffer().subarray(0, <i32>w.getOffset())) ^
|
|
360
|
+
(bech32m ? 0x2bc830a3 : 1);
|
|
361
|
+
|
|
362
|
+
const out = new Uint8Array(6);
|
|
363
|
+
for (let p = 0; p < 6; p++) {
|
|
364
|
+
out[p] = <u8>((pm >>> (5 * (5 - p))) & 31);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return out;
|
|
368
|
+
}
|
|
369
|
+
}
|