ecash-lib 4.8.0 → 4.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 +1 -0
- package/dist/consts.d.ts +5 -0
- package/dist/consts.d.ts.map +1 -1
- package/dist/consts.js +6 -1
- package/dist/consts.js.map +1 -1
- package/dist/ffi/ecash_lib_wasm_bg_browser.js +20534 -20534
- package/dist/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
- package/dist/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
- package/dist/op.d.ts +8 -0
- package/dist/op.d.ts.map +1 -1
- package/dist/op.js +97 -0
- package/dist/op.js.map +1 -1
- package/package.json +1 -1
- package/src/consts.ts +6 -0
- package/src/ffi/ecash_lib_wasm_bg_browser.js +20534 -20534
- package/src/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
- package/src/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
- package/src/op.ts +103 -0
|
Binary file
|
|
Binary file
|
package/src/op.ts
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// Distributed under the MIT software license, see the accompanying
|
|
3
3
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
4
|
|
|
5
|
+
import { MAX_SCRIPTNUM_BYTE_SIZE } from './consts.js';
|
|
5
6
|
import { Bytes } from './io/bytes.js';
|
|
6
7
|
import { Writer } from './io/writer.js';
|
|
7
8
|
import {
|
|
8
9
|
OP_0,
|
|
10
|
+
OP_1,
|
|
9
11
|
OP_1NEGATE,
|
|
12
|
+
OP_16,
|
|
10
13
|
OP_PUSHDATA1,
|
|
11
14
|
OP_PUSHDATA2,
|
|
12
15
|
OP_PUSHDATA4,
|
|
@@ -28,6 +31,106 @@ export interface PushOp {
|
|
|
28
31
|
data: Uint8Array;
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Check if bytes are a minimally encoded script number (not arbitrary data).
|
|
36
|
+
* See CScriptNum::IsMinimallyEncoded in script/script.cpp.
|
|
37
|
+
*/
|
|
38
|
+
function isMinimallyEncoded(data: Uint8Array): boolean {
|
|
39
|
+
if (data.length === 0) return true;
|
|
40
|
+
// Check that the number is encoded with the minimum possible number of bytes.
|
|
41
|
+
// If the most-significant-byte (excluding the sign bit) is zero then we're not minimal.
|
|
42
|
+
// This also rejects the negative-zero encoding, 0x80.
|
|
43
|
+
const last = data[data.length - 1]!;
|
|
44
|
+
if ((last & 0x7f) === 0) {
|
|
45
|
+
// One exception: if there's more than one byte and the most significant bit of the
|
|
46
|
+
// second-to-last byte is set, it would conflict with the sign bit. E.g. +-255
|
|
47
|
+
// encode to 0xff00 and 0xff80 respectively.
|
|
48
|
+
if (data.length <= 1 || (data[data.length - 2]! & 0x80) === 0) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Decode a minimally-encoded script number from bytes (little-endian, sign-magnitude).
|
|
57
|
+
* Validates size and minimal encoding; callers can rely on returned value being valid.
|
|
58
|
+
* Always returns bigint for type safety and full 64-bit range (matches CScriptNum::getint).
|
|
59
|
+
* Empty byte array decodes to 0.
|
|
60
|
+
* @throws Error if data exceeds max size or is not minimally encoded
|
|
61
|
+
*/
|
|
62
|
+
function decodeScriptNum(data: Uint8Array): bigint {
|
|
63
|
+
if (data.length === 0) {
|
|
64
|
+
return 0n;
|
|
65
|
+
}
|
|
66
|
+
if (data.length > MAX_SCRIPTNUM_BYTE_SIZE) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Script number exceeds maximum size (${data.length} > ${MAX_SCRIPTNUM_BYTE_SIZE} bytes)`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (!isMinimallyEncoded(data)) {
|
|
72
|
+
throw new Error('Script number is not minimally encoded');
|
|
73
|
+
}
|
|
74
|
+
let result = 0n;
|
|
75
|
+
for (let i = 0; i < data.length; i++) {
|
|
76
|
+
result |= BigInt(data[i]!) << BigInt(8 * i);
|
|
77
|
+
}
|
|
78
|
+
if (data[data.length - 1]! & 0x80) {
|
|
79
|
+
const mask = ~(0x80n << BigInt(8 * (data.length - 1)));
|
|
80
|
+
result &= mask;
|
|
81
|
+
result = -result;
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if the PushOp uses minimal push encoding for its data length.
|
|
88
|
+
* pushNumberOp always produces minimal pushes.
|
|
89
|
+
*/
|
|
90
|
+
function isMinimalPushOp(pushOp: PushOp): boolean {
|
|
91
|
+
const len = pushOp.data.length;
|
|
92
|
+
if (len === 0) return pushOp.opcode === OP_0;
|
|
93
|
+
if (len >= 1 && len <= 0x4b) return pushOp.opcode === len;
|
|
94
|
+
if (len >= 0x4c && len <= 0xff) return pushOp.opcode === OP_PUSHDATA1;
|
|
95
|
+
if (len >= 0x100 && len <= 0xffff) return pushOp.opcode === OP_PUSHDATA2;
|
|
96
|
+
if (len >= 0x10000 && len <= 0xffffffff)
|
|
97
|
+
return pushOp.opcode === OP_PUSHDATA4;
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Parse a number from a script op.
|
|
103
|
+
* Inverse of pushNumberOp: handles OP_0 (0), OP_1NEGATE (-1), OP_1 through OP_16,
|
|
104
|
+
* single-byte push data, and multi-byte minimal script number encoding (up to 64-bit).
|
|
105
|
+
* Always returns bigint for type safety (matches CScriptNum::getint).
|
|
106
|
+
* @throws Error with descriptive message if the op does not encode a number
|
|
107
|
+
*/
|
|
108
|
+
export function parseNumberFromOp(op: Op): bigint {
|
|
109
|
+
if (typeof op === 'number') {
|
|
110
|
+
if (op === OP_0) {
|
|
111
|
+
return 0n;
|
|
112
|
+
}
|
|
113
|
+
if (op === OP_1NEGATE) {
|
|
114
|
+
return -1n;
|
|
115
|
+
}
|
|
116
|
+
if (op >= OP_1 && op <= OP_16) {
|
|
117
|
+
return BigInt(op - 0x50);
|
|
118
|
+
}
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Opcode 0x${op.toString(16)} does not encode a number (expected OP_0, OP_1NEGATE, or OP_1-OP_16)`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (!isPushOp(op)) {
|
|
124
|
+
throw new Error('Op is not a push op');
|
|
125
|
+
}
|
|
126
|
+
if (!isMinimalPushOp(op)) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Push uses non-minimal encoding (opcode 0x${op.opcode.toString(16)} for ${op.data.length} bytes)`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return decodeScriptNum(op.data);
|
|
132
|
+
}
|
|
133
|
+
|
|
31
134
|
/** Returns true if the given object is a `PushOp` */
|
|
32
135
|
export function isPushOp(op: any): op is PushOp {
|
|
33
136
|
if (!op || typeof op !== 'object') {
|