@btc-vision/transaction 1.7.3 → 1.7.5

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.
@@ -1,7 +1,7 @@
1
1
  export class Map {
2
2
  constructor() {
3
3
  this._keys = [];
4
- this._values = [];
4
+ this._values = {};
5
5
  }
6
6
  get size() {
7
7
  return this._keys.length;
@@ -10,57 +10,65 @@ export class Map {
10
10
  yield* this._keys;
11
11
  }
12
12
  *values() {
13
- yield* this._values;
13
+ for (const key of this._keys) {
14
+ yield this._values[this.keyToString(key)];
15
+ }
14
16
  }
15
17
  *entries() {
16
- for (let i = 0; i < this._keys.length; i++) {
17
- yield [this._keys[i], this._values[i]];
18
+ for (const key of this._keys) {
19
+ yield [key, this._values[this.keyToString(key)]];
18
20
  }
19
21
  }
20
22
  set(key, value) {
21
- const index = this.indexOf(key);
22
- if (index == -1) {
23
+ const keyStr = this.keyToString(key);
24
+ if (!this.has(key)) {
23
25
  this._keys.push(key);
24
- this._values.push(value);
25
- }
26
- else {
27
- this._values[index] = value;
28
26
  }
27
+ this._values[keyStr] = value;
29
28
  }
30
29
  indexOf(key) {
31
30
  for (let i = 0; i < this._keys.length; i++) {
32
- if (this._keys[i] == key) {
31
+ if (this._keys[i] === key) {
33
32
  return i;
34
33
  }
35
34
  }
36
35
  return -1;
37
36
  }
38
37
  get(key) {
39
- const index = this.indexOf(key);
40
- if (index == -1) {
41
- return undefined;
42
- }
43
- return this._values[index];
38
+ return this._values[this.keyToString(key)];
44
39
  }
45
40
  has(key) {
46
- return this.indexOf(key) != -1;
41
+ return Object.prototype.hasOwnProperty.call(this._values, this.keyToString(key));
47
42
  }
48
43
  delete(key) {
49
44
  const index = this.indexOf(key);
50
- if (index == -1) {
45
+ if (index === -1) {
51
46
  return false;
52
47
  }
48
+ const keyStr = this.keyToString(key);
53
49
  this._keys.splice(index, 1);
54
- this._values.splice(index, 1);
50
+ delete this._values[keyStr];
55
51
  return true;
56
52
  }
57
53
  clear() {
58
54
  this._keys = [];
59
- this._values = [];
55
+ this._values = {};
60
56
  }
61
57
  *[Symbol.iterator]() {
62
- for (let i = 0; i < this._keys.length; i++) {
63
- yield [this._keys[i], this._values[i]];
58
+ for (const key of this._keys) {
59
+ yield [key, this._values[this.keyToString(key)]];
60
+ }
61
+ }
62
+ keyToString(key) {
63
+ if (typeof key === 'string') {
64
+ return key;
65
+ }
66
+ if (typeof key === 'number' || typeof key === 'boolean') {
67
+ return String(key);
68
+ }
69
+ if (typeof key === 'object' && key !== null) {
70
+ return JSON.stringify(key);
64
71
  }
72
+ return String(key);
65
73
  }
66
74
  }
package/build/opnet.d.ts CHANGED
@@ -76,6 +76,7 @@ export * from './transaction/browser/Web3Provider.js';
76
76
  export * from './keypair/Secp256k1PointDeriver.js';
77
77
  export * from './transaction/ContractAddress.js';
78
78
  export * from './deterministic/Map.js';
79
+ export * from './deterministic/CustomMap.js';
79
80
  declare global {
80
81
  interface Window {
81
82
  unisat?: Unisat;
package/build/opnet.js CHANGED
@@ -75,3 +75,4 @@ export * from './transaction/browser/Web3Provider.js';
75
75
  export * from './keypair/Secp256k1PointDeriver.js';
76
76
  export * from './transaction/ContractAddress.js';
77
77
  export * from './deterministic/Map.js';
78
+ export * from './deterministic/CustomMap.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.7.3",
4
+ "version": "1.7.5",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -3,11 +3,9 @@ import { Map } from './Map.js';
3
3
 
4
4
  export class AddressMap<V> {
5
5
  private items: Map<bigint, V>;
6
- private keyOrder: Address[];
7
6
 
8
7
  constructor(iterable?: ReadonlyArray<readonly [Address, V]> | null) {
9
8
  this.items = new Map();
10
- this.keyOrder = [];
11
9
 
12
10
  if (iterable) {
13
11
  for (const [key, value] of iterable) {
@@ -17,15 +15,14 @@ export class AddressMap<V> {
17
15
  }
18
16
 
19
17
  public get size(): number {
20
- return this.keyOrder.length;
18
+ return this.items.size;
21
19
  }
22
20
 
23
- public set(key: Address, value: V): void {
21
+ public set(key: Address, value: V): this {
24
22
  const keyBigInt = key.toBigInt();
25
- if (!this.items.has(keyBigInt)) {
26
- this.keyOrder.push(key);
27
- }
28
23
  this.items.set(keyBigInt, value);
24
+
25
+ return this;
29
26
  }
30
27
 
31
28
  public get(key: Address): V | undefined {
@@ -38,41 +35,35 @@ export class AddressMap<V> {
38
35
 
39
36
  public delete(key: Address): boolean {
40
37
  const keyBigInt = key.toBigInt();
41
- if (this.items.delete(keyBigInt)) {
42
- this.keyOrder = this.keyOrder.filter((k) => k.toBigInt() !== keyBigInt);
43
- return true;
44
- }
45
- return false;
38
+ return this.items.delete(keyBigInt);
46
39
  }
47
40
 
48
41
  public clear(): void {
49
42
  this.items.clear();
50
- this.keyOrder = [];
51
43
  }
52
44
 
53
45
  public indexOf(address: Address): number {
54
- const addressBigInt = address.toBigInt();
55
- for (let i: number = 0; i < this.keyOrder.length; i++) {
56
- if (this.keyOrder[i].toBigInt() === addressBigInt) {
57
- return i;
58
- }
59
- }
60
- return -1;
46
+ return this.items.indexOf(address.toBigInt());
61
47
  }
62
48
 
49
+ /**
50
+ * WARNING, THIS RETURN NEW COPY OF THE KEYS
51
+ */
63
52
  *entries(): IterableIterator<[Address, V]> {
64
- for (const key of this.keyOrder) {
65
- yield [key, this.items.get(key.toBigInt()) as V];
53
+ for (const [keyBigInt, value] of this.items.entries()) {
54
+ yield [Address.fromBigInt(keyBigInt), value];
66
55
  }
67
56
  }
68
57
 
69
58
  *keys(): IterableIterator<Address> {
70
- yield* this.keyOrder;
59
+ for (const keyBigInt of this.items.keys()) {
60
+ yield Address.fromBigInt(keyBigInt);
61
+ }
71
62
  }
72
63
 
73
64
  *values(): IterableIterator<V> {
74
- for (const key of this.keyOrder) {
75
- yield this.items.get(key.toBigInt()) as V;
65
+ for (const value of this.items.values()) {
66
+ yield value;
76
67
  }
77
68
  }
78
69
 
@@ -80,15 +71,13 @@ export class AddressMap<V> {
80
71
  callback: (value: V, key: Address, map: AddressMap<V>) => void,
81
72
  thisArg?: unknown,
82
73
  ): void {
83
- for (const key of this.keyOrder) {
84
- callback.call(thisArg, this.items.get(key.toBigInt()) as V, key, this);
74
+ for (const [keyBigInt, value] of this.items.entries()) {
75
+ const key = Address.fromBigInt(keyBigInt);
76
+ callback.call(thisArg, value, key, this);
85
77
  }
86
78
  }
87
79
 
88
80
  *[Symbol.iterator](): IterableIterator<[Address, V]> {
89
- for (let i = 0; i < this.keyOrder.length; i++) {
90
- const key = this.keyOrder[i];
91
- yield [key, this.items.get(key.toBigInt()) as V];
92
- }
81
+ yield* this.entries();
93
82
  }
94
83
  }
@@ -0,0 +1,316 @@
1
+ export class CustomMap<K, V> {
2
+ private static readonly INITIAL_CAPACITY = 16;
3
+ private static readonly LOAD_FACTOR = 0.75;
4
+
5
+ #keys: (K | undefined)[];
6
+ #values: (V | undefined)[];
7
+
8
+ private deleted: boolean[];
9
+ private capacity: number;
10
+
11
+ constructor() {
12
+ this.capacity = CustomMap.INITIAL_CAPACITY;
13
+ this.#keys = new Array<K>(this.capacity);
14
+ this.#values = new Array<V>(this.capacity);
15
+ this.deleted = new Array<boolean>(this.capacity).fill(false);
16
+ }
17
+
18
+ private _size: number = 0;
19
+
20
+ public get size(): number {
21
+ return this._size;
22
+ }
23
+
24
+ public set(key: K, value: V): boolean {
25
+ let exist: boolean = true;
26
+
27
+ const index = this.findInsertIndex(key);
28
+ if (this.#keys[index] === undefined || this.deleted[index]) {
29
+ this._size++;
30
+ exist = false;
31
+ }
32
+
33
+ this.#keys[index] = key;
34
+ this.#values[index] = value;
35
+ this.deleted[index] = false;
36
+
37
+ if (this._size > this.capacity * CustomMap.LOAD_FACTOR) {
38
+ this.resize();
39
+ }
40
+
41
+ return exist;
42
+ }
43
+
44
+ public get(key: K): V | undefined {
45
+ const index = this.findIndex(key);
46
+ return index === -1 ? undefined : this.#values[index];
47
+ }
48
+
49
+ public has(key: K): boolean {
50
+ return this.findIndex(key) !== -1;
51
+ }
52
+
53
+ public indexOf(key: K): number {
54
+ return this.findIndex(key);
55
+ }
56
+
57
+ public delete(key: K): boolean {
58
+ const index = this.findIndex(key);
59
+
60
+ if (index === -1) {
61
+ return false;
62
+ }
63
+
64
+ this.#keys[index] = undefined;
65
+ this.#values[index] = undefined;
66
+ this.deleted[index] = true;
67
+ this._size--;
68
+
69
+ return true;
70
+ }
71
+
72
+ public clear(): void {
73
+ this.#keys = new Array<K>(this.capacity);
74
+ this.#values = new Array<V>(this.capacity);
75
+ this.deleted = new Array<boolean>(this.capacity).fill(false);
76
+ this._size = 0;
77
+ }
78
+
79
+ public *entries(): MapIterator<[K, V]> {
80
+ for (let i = 0; i < this.capacity; i++) {
81
+ if (this.#keys[i] !== undefined && !this.deleted[i]) {
82
+ yield [this.#keys[i] as K, this.#values[i] as V];
83
+ }
84
+ }
85
+ }
86
+
87
+ public *keys(): MapIterator<K> {
88
+ for (let i = 0; i < this.capacity; i++) {
89
+ if (this.#keys[i] !== undefined && !this.deleted[i]) {
90
+ yield this.#keys[i] as K;
91
+ }
92
+ }
93
+ }
94
+
95
+ public *values(): MapIterator<V> {
96
+ for (let i = 0; i < this.capacity; i++) {
97
+ if (this.#keys[i] !== undefined && !this.deleted[i]) {
98
+ yield this.#values[i] as V;
99
+ }
100
+ }
101
+ }
102
+
103
+ *[Symbol.iterator](): MapIterator<[K, V]> {
104
+ yield* this.entries();
105
+ }
106
+
107
+ private hashBigInt(key: bigint): number {
108
+ // For small bigints that fit in 32 bits, use direct conversion
109
+ if (key >= -2147483648n && key <= 2147483647n) {
110
+ return Number(key) | 0;
111
+ }
112
+
113
+ // For larger bigints, use bit manipulation
114
+ // Mix high and low 32-bit parts
115
+ let hash = 2166136261; // FNV-1a initial value
116
+
117
+ // Process the bigint in 32-bit chunks
118
+ let n = key < 0n ? -key : key;
119
+
120
+ while (n > 0n) {
121
+ // Extract 32-bit chunk
122
+ const chunk = Number(n & 0xffffffffn);
123
+ hash ^= chunk;
124
+ hash = Math.imul(hash, 16777619);
125
+ n = n >> 32n;
126
+ }
127
+
128
+ // Mix in the sign
129
+ if (key < 0n) {
130
+ hash ^= 0x80000000;
131
+ hash = Math.imul(hash, 16777619);
132
+ }
133
+
134
+ return Math.abs(hash);
135
+ }
136
+
137
+ private hash(key: K): number {
138
+ let hash = 0;
139
+
140
+ switch (typeof key) {
141
+ case 'number':
142
+ // Handle NaN and infinity specially
143
+ if (key !== key) return 0x7ff8000000000000; // NaN
144
+ if (!isFinite(key)) return key > 0 ? 0x7ff0000000000000 : 0xfff0000000000000;
145
+ // Use the number itself as hash
146
+ hash = key | 0; // Convert to 32-bit integer
147
+ break;
148
+
149
+ case 'string':
150
+ // FNV-1a hash for strings
151
+ hash = 2166136261;
152
+ for (let i = 0; i < (key as string).length; i++) {
153
+ hash ^= (key as string).charCodeAt(i);
154
+ hash = Math.imul(hash, 16777619);
155
+ }
156
+ break;
157
+
158
+ case 'boolean':
159
+ hash = key ? 1231 : 1237;
160
+ break;
161
+
162
+ case 'symbol': {
163
+ // Symbols need special handling - use description
164
+ const desc = (key as symbol).description || '';
165
+ hash = this.hash(desc as K); // Recursive call with string
166
+ break;
167
+ }
168
+
169
+ case 'bigint':
170
+ // Convert bigint to string for hashing
171
+ hash = this.hashBigInt(key);
172
+ break;
173
+
174
+ case 'undefined':
175
+ hash = 0;
176
+ break;
177
+
178
+ case 'object':
179
+ if (key === null) {
180
+ hash = 0;
181
+ } else if (key instanceof Date) {
182
+ hash = key.getTime() | 0;
183
+ } else if (ArrayBuffer.isView(key) || key instanceof ArrayBuffer) {
184
+ // Handle Buffer, TypedArrays, ArrayBuffer
185
+ hash = this.hashBuffer(key);
186
+ } else if (Array.isArray(key)) {
187
+ // Hash arrays by combining element hashes
188
+ hash = 1;
189
+ for (const item of key) {
190
+ hash = Math.imul(hash, 31) + this.hash(item as K);
191
+ }
192
+ } else {
193
+ throw new Error('Raw object not supported.');
194
+ // For objects, we need reference equality
195
+ // So we'll use linear probing with === comparison
196
+ // Start with a random-ish position
197
+ //hash = 0x42424242;
198
+ }
199
+ break;
200
+
201
+ case 'function':
202
+ // Hash function by its string representation
203
+ hash = this.hash(key.toString() as K);
204
+ break;
205
+ }
206
+
207
+ // Ensure positive index
208
+ return Math.abs(hash) % this.capacity;
209
+ }
210
+
211
+ private hashBuffer(buffer: ArrayBuffer | object): number {
212
+ let bytes: Uint8Array;
213
+
214
+ if (buffer instanceof ArrayBuffer) {
215
+ bytes = new Uint8Array(buffer);
216
+ } else if (ArrayBuffer.isView(buffer)) {
217
+ bytes = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
218
+ } else {
219
+ return 0;
220
+ }
221
+
222
+ // FNV-1a hash for bytes
223
+ let hash = 2166136261;
224
+ for (let i = 0; i < Math.min(bytes.length, 100); i++) {
225
+ // Cap at 100 bytes for performance
226
+ hash ^= bytes[i];
227
+ hash = Math.imul(hash, 16777619);
228
+ }
229
+ return hash;
230
+ }
231
+
232
+ private equals(a: K, b: K): boolean {
233
+ // Handle special cases
234
+ if (a === b) return true;
235
+
236
+ // NaN === NaN should be true for map #keys
237
+ if (typeof a === 'number' && typeof b === 'number' && a !== a && b !== b) {
238
+ return true;
239
+ }
240
+
241
+ // For buffers, do deep comparison
242
+ if (
243
+ (ArrayBuffer.isView(a) || a instanceof ArrayBuffer) &&
244
+ (ArrayBuffer.isView(b) || b instanceof ArrayBuffer)
245
+ ) {
246
+ return this.buffersEqual(a, b);
247
+ }
248
+
249
+ return false;
250
+ }
251
+
252
+ private buffersEqual(a: ArrayBuffer | object, b: ArrayBuffer | object): boolean {
253
+ const bytesA = this.getBytes(a);
254
+ const bytesB = this.getBytes(b);
255
+
256
+ if (bytesA.length !== bytesB.length) return false;
257
+
258
+ for (let i = 0; i < bytesA.length; i++) {
259
+ if (bytesA[i] !== bytesB[i]) return false;
260
+ }
261
+
262
+ return true;
263
+ }
264
+
265
+ private getBytes(buffer: ArrayBuffer | object): Uint8Array {
266
+ if (buffer instanceof ArrayBuffer) {
267
+ return new Uint8Array(buffer);
268
+ } else if (ArrayBuffer.isView(buffer)) {
269
+ return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
270
+ }
271
+ return new Uint8Array(0);
272
+ }
273
+
274
+ private findIndex(key: K): number {
275
+ let index = this.hash(key);
276
+
277
+ while (this.#keys[index] !== undefined || this.deleted[index]) {
278
+ if (this.#keys[index] !== undefined && this.equals(this.#keys[index] as K, key)) {
279
+ return index;
280
+ }
281
+ index = (index + 1) % this.capacity;
282
+ }
283
+
284
+ return -1;
285
+ }
286
+
287
+ private findInsertIndex(key: K): number {
288
+ let index = this.hash(key);
289
+
290
+ while (this.#keys[index] !== undefined && !this.deleted[index]) {
291
+ if (this.equals(this.#keys[index] as K, key)) {
292
+ return index; // Key already exists
293
+ }
294
+ index = (index + 1) % this.capacity;
295
+ }
296
+
297
+ return index;
298
+ }
299
+
300
+ private resize(): void {
301
+ const oldKeys = this.#keys;
302
+ const oldValues = this.#values;
303
+
304
+ this.capacity *= 2;
305
+ this.#keys = new Array<K>(this.capacity);
306
+ this.#values = new Array<V>(this.capacity);
307
+ this.deleted = new Array<boolean>(this.capacity).fill(false);
308
+ this._size = 0;
309
+
310
+ for (let i = 0; i < oldKeys.length; i++) {
311
+ if (oldKeys[i] !== undefined && !this.deleted[i]) {
312
+ this.set(oldKeys[i] as K, oldValues[i] as V);
313
+ }
314
+ }
315
+ }
316
+ }
@@ -26,8 +26,18 @@ export class DeterministicMap<K, V> {
26
26
 
27
27
  public set(key: K, value: V): void {
28
28
  if (!this.map.has(key)) {
29
- this.#keys.push(key);
30
- this.#keys.sort(this.compareFn);
29
+ // Binary search for insertion position
30
+ let left = 0,
31
+ right = this.#keys.length;
32
+ while (left < right) {
33
+ const mid = Math.floor((left + right) / 2);
34
+ if (this.compareFn(this.#keys[mid], key) < 0) {
35
+ left = mid + 1;
36
+ } else {
37
+ right = mid;
38
+ }
39
+ }
40
+ this.#keys.splice(left, 0, key);
31
41
  }
32
42
  this.map.set(key, value);
33
43
  }
@@ -64,8 +74,24 @@ export class DeterministicMap<K, V> {
64
74
  public delete(key: K): boolean {
65
75
  if (this.map.has(key)) {
66
76
  this.map.delete(key);
67
- this.#keys = this.#keys.filter((k) => k !== key);
68
- return true;
77
+
78
+ // Binary search to find the key's index
79
+ let left = 0,
80
+ right = this.#keys.length - 1;
81
+ while (left <= right) {
82
+ const mid = Math.floor((left + right) / 2);
83
+ const cmp = this.compareFn(this.#keys[mid], key);
84
+
85
+ if (cmp === 0) {
86
+ // Found it, remove at this index
87
+ this.#keys.splice(mid, 1);
88
+ return true;
89
+ } else if (cmp < 0) {
90
+ left = mid + 1;
91
+ } else {
92
+ right = mid - 1;
93
+ }
94
+ }
69
95
  }
70
96
  return false;
71
97
  }
@@ -5,25 +5,39 @@ export class DeterministicSet<T> {
5
5
  this.elements = [];
6
6
  }
7
7
 
8
+ public get size(): number {
9
+ return this.elements.length;
10
+ }
11
+
12
+ public static fromSet<T>(set: Set<T>, compareFn: (a: T, b: T) => number): DeterministicSet<T> {
13
+ const deterministicSet = new DeterministicSet<T>(compareFn);
14
+ for (const value of set) {
15
+ deterministicSet.add(value);
16
+ }
17
+ return deterministicSet;
18
+ }
19
+
8
20
  public add(value: T): void {
9
- if (!this.elements.includes(value)) {
10
- this.elements.push(value);
11
- this.elements.sort(this.compareFn);
21
+ const { found, index } = this.binarySearch(value);
22
+
23
+ if (!found) {
24
+ this.elements.splice(index, 0, value);
12
25
  }
13
26
  }
14
27
 
15
28
  public delete(value: T): boolean {
16
- const index = this.elements.indexOf(value);
17
- if (index === -1) {
18
- return false;
29
+ const { found, index } = this.binarySearch(value);
30
+
31
+ if (found) {
32
+ this.elements.splice(index, 1);
33
+ return true;
19
34
  }
20
35
 
21
- this.elements.splice(index, 1);
22
- return true;
36
+ return false;
23
37
  }
24
38
 
25
39
  public has(value: T): boolean {
26
- return this.elements.includes(value);
40
+ return this.binarySearch(value).found;
27
41
  }
28
42
 
29
43
  public clear(): void {
@@ -36,21 +50,30 @@ export class DeterministicSet<T> {
36
50
  }
37
51
  }
38
52
 
39
- public static fromSet<T>(set: Set<T>, compareFn: (a: T, b: T) => number): DeterministicSet<T> {
40
- const deterministicSet = new DeterministicSet<T>(compareFn);
41
- for (const value of set) {
42
- deterministicSet.add(value);
43
- }
44
- return deterministicSet;
53
+ *values(): IterableIterator<T> {
54
+ yield* this.elements;
45
55
  }
46
56
 
47
- public get size(): number {
48
- return this.elements.length;
57
+ *[Symbol.iterator](): IterableIterator<T> {
58
+ yield* this.elements;
49
59
  }
50
60
 
51
- *[Symbol.iterator](): IterableIterator<T> {
52
- for (const value of this.elements) {
53
- yield value;
61
+ private binarySearch(value: T): { found: boolean; index: number } {
62
+ let left = 0, right = this.elements.length;
63
+
64
+ while (left < right) {
65
+ const mid = Math.floor((left + right) / 2);
66
+ const cmp = this.compareFn(this.elements[mid], value);
67
+
68
+ if (cmp === 0) {
69
+ return { found: true, index: mid };
70
+ } else if (cmp < 0) {
71
+ left = mid + 1;
72
+ } else {
73
+ right = mid;
74
+ }
54
75
  }
76
+
77
+ return { found: false, index: left };
55
78
  }
56
79
  }