@btc-vision/btc-runtime 1.10.3 → 1.10.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.
- package/package.json +2 -2
- package/runtime/contracts/OP20.ts +19 -8
- package/runtime/contracts/OP721.ts +19 -8
- package/runtime/env/BlockchainEnvironment.ts +90 -65
- package/runtime/env/global.ts +24 -14
- package/runtime/generic/AddressMap.ts +113 -24
- package/runtime/generic/Map.ts +78 -22
- package/runtime/generic/MapUint8Array.ts +115 -27
- package/runtime/storage/arrays/StoredPackedArray.ts +117 -60
- package/runtime/types/ExtendedAddress.ts +4 -4
- package/runtime/types/SafeMath.ts +240 -337
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/btc-runtime",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.5",
|
|
4
4
|
"description": "Bitcoin Smart Contract Runtime",
|
|
5
5
|
"main": "btc/index.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@assemblyscript/loader": "^0.28.9",
|
|
47
|
-
"@btc-vision/as-bignum": "^0.0.
|
|
47
|
+
"@btc-vision/as-bignum": "^0.0.6",
|
|
48
48
|
"@btc-vision/opnet-transform": "^0.1.12",
|
|
49
49
|
"@eslint/js": "9.39.1",
|
|
50
50
|
"gulplog": "^2.2.0",
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
import { IOP20 } from './interfaces/IOP20';
|
|
44
44
|
import { OP20InitParameters } from './interfaces/OP20InitParameters';
|
|
45
45
|
import { ReentrancyGuard, ReentrancyLevel } from './ReentrancyGuard';
|
|
46
|
+
import { ExtendedAddress } from '../types/ExtendedAddress';
|
|
46
47
|
|
|
47
48
|
const nonceMapPointer: u16 = Blockchain.nextPointer;
|
|
48
49
|
const maxSupplyPointer: u16 = Blockchain.nextPointer;
|
|
@@ -486,7 +487,8 @@ export abstract class OP20 extends ReentrancyGuard implements IOP20 {
|
|
|
486
487
|
* Uses Schnorr signatures now, will support ML-DSA after quantum transition.
|
|
487
488
|
*/
|
|
488
489
|
@method(
|
|
489
|
-
{ name: 'owner', type: ABIDataTypes.
|
|
490
|
+
{ name: 'owner', type: ABIDataTypes.BYTES32 },
|
|
491
|
+
{ name: 'ownerTweakedPublicKey', type: ABIDataTypes.BYTES32 },
|
|
490
492
|
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
491
493
|
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
492
494
|
{ name: 'deadline', type: ABIDataTypes.UINT64 },
|
|
@@ -494,7 +496,11 @@ export abstract class OP20 extends ReentrancyGuard implements IOP20 {
|
|
|
494
496
|
)
|
|
495
497
|
@emit('Approved')
|
|
496
498
|
public increaseAllowanceBySignature(calldata: Calldata): BytesWriter {
|
|
497
|
-
const
|
|
499
|
+
const ownerAddress = calldata.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
500
|
+
const ownerTweakedPublicKey = calldata.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
501
|
+
|
|
502
|
+
const owner = new ExtendedAddress(ownerTweakedPublicKey, ownerAddress);
|
|
503
|
+
|
|
498
504
|
const spender: Address = calldata.readAddress();
|
|
499
505
|
const amount: u256 = calldata.readU256();
|
|
500
506
|
const deadline: u64 = calldata.readU64();
|
|
@@ -513,7 +519,8 @@ export abstract class OP20 extends ReentrancyGuard implements IOP20 {
|
|
|
513
519
|
* @throws {Revert} If signature is invalid or expired
|
|
514
520
|
*/
|
|
515
521
|
@method(
|
|
516
|
-
{ name: 'owner', type: ABIDataTypes.
|
|
522
|
+
{ name: 'owner', type: ABIDataTypes.BYTES32 },
|
|
523
|
+
{ name: 'ownerTweakedPublicKey', type: ABIDataTypes.BYTES32 },
|
|
517
524
|
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
518
525
|
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
519
526
|
{ name: 'deadline', type: ABIDataTypes.UINT64 },
|
|
@@ -521,7 +528,11 @@ export abstract class OP20 extends ReentrancyGuard implements IOP20 {
|
|
|
521
528
|
)
|
|
522
529
|
@emit('Approved')
|
|
523
530
|
public decreaseAllowanceBySignature(calldata: Calldata): BytesWriter {
|
|
524
|
-
const
|
|
531
|
+
const ownerAddress = calldata.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
532
|
+
const ownerTweakedPublicKey = calldata.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
533
|
+
|
|
534
|
+
const owner = new ExtendedAddress(ownerTweakedPublicKey, ownerAddress);
|
|
535
|
+
|
|
525
536
|
const spender: Address = calldata.readAddress();
|
|
526
537
|
const amount: u256 = calldata.readU256();
|
|
527
538
|
const deadline: u64 = calldata.readU64();
|
|
@@ -714,7 +725,7 @@ export abstract class OP20 extends ReentrancyGuard implements IOP20 {
|
|
|
714
725
|
* @protected
|
|
715
726
|
*/
|
|
716
727
|
protected _increaseAllowanceBySignature(
|
|
717
|
-
owner:
|
|
728
|
+
owner: ExtendedAddress,
|
|
718
729
|
spender: Address,
|
|
719
730
|
amount: u256,
|
|
720
731
|
deadline: u64,
|
|
@@ -760,7 +771,7 @@ export abstract class OP20 extends ReentrancyGuard implements IOP20 {
|
|
|
760
771
|
* @protected
|
|
761
772
|
*/
|
|
762
773
|
protected _decreaseAllowanceBySignature(
|
|
763
|
-
owner:
|
|
774
|
+
owner: ExtendedAddress,
|
|
764
775
|
spender: Address,
|
|
765
776
|
amount: u256,
|
|
766
777
|
deadline: u64,
|
|
@@ -783,7 +794,7 @@ export abstract class OP20 extends ReentrancyGuard implements IOP20 {
|
|
|
783
794
|
*/
|
|
784
795
|
protected _verifySignature(
|
|
785
796
|
typeHash: u8[],
|
|
786
|
-
owner:
|
|
797
|
+
owner: ExtendedAddress,
|
|
787
798
|
spender: Address,
|
|
788
799
|
amount: u256,
|
|
789
800
|
deadline: u64,
|
|
@@ -817,7 +828,7 @@ export abstract class OP20 extends ReentrancyGuard implements IOP20 {
|
|
|
817
828
|
|
|
818
829
|
const hash = sha256(messageWriter.getBuffer());
|
|
819
830
|
|
|
820
|
-
if (!Blockchain.
|
|
831
|
+
if (!Blockchain.verifySignature(owner, signature, hash)) {
|
|
821
832
|
throw new Revert('Invalid signature');
|
|
822
833
|
}
|
|
823
834
|
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
OP721_APPROVAL_FOR_ALL_TYPE_HASH,
|
|
40
40
|
OP721_APPROVAL_TYPE_HASH,
|
|
41
41
|
} from '../constants/Exports';
|
|
42
|
+
import { ExtendedAddress } from '../types/ExtendedAddress';
|
|
42
43
|
|
|
43
44
|
const stringPointer: u16 = Blockchain.nextPointer;
|
|
44
45
|
const totalSupplyPointer: u16 = Blockchain.nextPointer;
|
|
@@ -395,7 +396,8 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
395
396
|
}
|
|
396
397
|
|
|
397
398
|
@method(
|
|
398
|
-
{ name: 'owner', type: ABIDataTypes.
|
|
399
|
+
{ name: 'owner', type: ABIDataTypes.BYTES32 },
|
|
400
|
+
{ name: 'ownerTweakedPublicKey', type: ABIDataTypes.BYTES32 },
|
|
399
401
|
{ name: 'operator', type: ABIDataTypes.ADDRESS },
|
|
400
402
|
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
|
|
401
403
|
{ name: 'deadline', type: ABIDataTypes.UINT64 },
|
|
@@ -403,7 +405,11 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
403
405
|
)
|
|
404
406
|
@emit('Approved')
|
|
405
407
|
public approveBySignature(calldata: Calldata): BytesWriter {
|
|
406
|
-
const
|
|
408
|
+
const ownerAddress = calldata.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
409
|
+
const ownerTweakedPublicKey = calldata.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
410
|
+
|
|
411
|
+
const owner = new ExtendedAddress(ownerTweakedPublicKey, ownerAddress);
|
|
412
|
+
|
|
407
413
|
const operator = calldata.readAddress();
|
|
408
414
|
const tokenId = calldata.readU256();
|
|
409
415
|
const deadline = calldata.readU64();
|
|
@@ -421,7 +427,8 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
421
427
|
}
|
|
422
428
|
|
|
423
429
|
@method(
|
|
424
|
-
{ name: 'owner', type: ABIDataTypes.
|
|
430
|
+
{ name: 'owner', type: ABIDataTypes.BYTES32 },
|
|
431
|
+
{ name: 'ownerTweakedPublicKey', type: ABIDataTypes.BYTES32 },
|
|
425
432
|
{ name: 'operator', type: ABIDataTypes.ADDRESS },
|
|
426
433
|
{ name: 'approved', type: ABIDataTypes.BOOL },
|
|
427
434
|
{ name: 'deadline', type: ABIDataTypes.UINT64 },
|
|
@@ -429,7 +436,11 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
429
436
|
)
|
|
430
437
|
@emit('Approved')
|
|
431
438
|
public setApprovalForAllBySignature(calldata: Calldata): BytesWriter {
|
|
432
|
-
const
|
|
439
|
+
const ownerAddress = calldata.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
440
|
+
const ownerTweakedPublicKey = calldata.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
441
|
+
|
|
442
|
+
const owner = new ExtendedAddress(ownerTweakedPublicKey, ownerAddress);
|
|
443
|
+
|
|
433
444
|
const operator = calldata.readAddress();
|
|
434
445
|
const approved = calldata.readBoolean();
|
|
435
446
|
const deadline = calldata.readU64();
|
|
@@ -784,7 +795,7 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
784
795
|
}
|
|
785
796
|
|
|
786
797
|
protected _verifyApproveSignature(
|
|
787
|
-
owner:
|
|
798
|
+
owner: ExtendedAddress,
|
|
788
799
|
spender: Address,
|
|
789
800
|
tokenId: u256,
|
|
790
801
|
deadline: u64,
|
|
@@ -814,7 +825,7 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
814
825
|
}
|
|
815
826
|
|
|
816
827
|
protected _verifySetApprovalForAllSignature(
|
|
817
|
-
owner:
|
|
828
|
+
owner: ExtendedAddress,
|
|
818
829
|
spender: Address,
|
|
819
830
|
approved: boolean,
|
|
820
831
|
deadline: u64,
|
|
@@ -845,7 +856,7 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
845
856
|
|
|
846
857
|
protected _verifySignature(
|
|
847
858
|
structHash: Uint8Array,
|
|
848
|
-
owner:
|
|
859
|
+
owner: ExtendedAddress,
|
|
849
860
|
signature: Uint8Array,
|
|
850
861
|
nonce: u256,
|
|
851
862
|
): void {
|
|
@@ -856,7 +867,7 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
856
867
|
|
|
857
868
|
const hash = sha256(messageWriter.getBuffer());
|
|
858
869
|
|
|
859
|
-
if (!Blockchain.
|
|
870
|
+
if (!Blockchain.verifySignature(owner, signature, hash)) {
|
|
860
871
|
throw new Revert('Invalid signature');
|
|
861
872
|
}
|
|
862
873
|
|
|
@@ -50,6 +50,12 @@ export class CallResult {
|
|
|
50
50
|
) {}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
const SCRATCH_SIZE: i32 = 256;
|
|
54
|
+
const SCRATCH_BUF: ArrayBuffer = new ArrayBuffer(SCRATCH_SIZE);
|
|
55
|
+
const SCRATCH_VIEW: Uint8Array = Uint8Array.wrap(SCRATCH_BUF);
|
|
56
|
+
|
|
57
|
+
const FOUR_BYTES_UINT8ARRAY_MEMORY_CACHE = new Uint8Array(4);
|
|
58
|
+
|
|
53
59
|
/**
|
|
54
60
|
* BlockchainEnvironment - Core Runtime Environment for OP_NET Smart Contracts
|
|
55
61
|
*
|
|
@@ -282,9 +288,10 @@ export class BlockchainEnvironment {
|
|
|
282
288
|
* Called once during deployment. State changes here are permanent.
|
|
283
289
|
*/
|
|
284
290
|
public onDeployment(calldata: Calldata): void {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
291
|
+
const len = this._plugins.length;
|
|
292
|
+
for (let i: i32 = 0; i < len; i++) {
|
|
293
|
+
// Unchecked access for speed
|
|
294
|
+
unchecked(this._plugins[i].onDeployment(calldata));
|
|
288
295
|
}
|
|
289
296
|
this.contract.onDeployment(calldata);
|
|
290
297
|
}
|
|
@@ -299,9 +306,9 @@ export class BlockchainEnvironment {
|
|
|
299
306
|
* Used for access control, reentrancy guards, and validation.
|
|
300
307
|
*/
|
|
301
308
|
public onExecutionStarted(selector: Selector, calldata: Calldata): void {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
309
|
+
const len = this._plugins.length;
|
|
310
|
+
for (let i: i32 = 0; i < len; i++) {
|
|
311
|
+
unchecked(this._plugins[i].onExecutionStarted(selector, calldata));
|
|
305
312
|
}
|
|
306
313
|
this.contract.onExecutionStarted(selector, calldata);
|
|
307
314
|
}
|
|
@@ -316,9 +323,9 @@ export class BlockchainEnvironment {
|
|
|
316
323
|
* Only called on successful execution. Used for cleanup and events.
|
|
317
324
|
*/
|
|
318
325
|
public onExecutionCompleted(selector: Selector, calldata: Calldata): void {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
326
|
+
const len = this._plugins.length;
|
|
327
|
+
for (let i: i32 = 0; i < len; i++) {
|
|
328
|
+
unchecked(this._plugins[i].onExecutionCompleted(selector, calldata));
|
|
322
329
|
}
|
|
323
330
|
this.contract.onExecutionCompleted(selector, calldata);
|
|
324
331
|
}
|
|
@@ -332,6 +339,7 @@ export class BlockchainEnvironment {
|
|
|
332
339
|
* Called automatically by the runtime to set up execution context.
|
|
333
340
|
*/
|
|
334
341
|
public setEnvironmentVariables(data: Uint8Array): void {
|
|
342
|
+
// BytesReader is unavoidable for parsing complex external struct
|
|
335
343
|
const reader: BytesReader = new BytesReader(data);
|
|
336
344
|
|
|
337
345
|
const blockHash = reader.readBytes(32);
|
|
@@ -404,16 +412,18 @@ export class BlockchainEnvironment {
|
|
|
404
412
|
throw new Revert('Destination contract is required');
|
|
405
413
|
}
|
|
406
414
|
|
|
407
|
-
|
|
415
|
+
// This creates the underlying ArrayBuffer AND gives us a 'dataStart' pointer for free.
|
|
408
416
|
const status = callContract(
|
|
409
417
|
destinationContract.buffer,
|
|
410
418
|
calldata.getBuffer().buffer,
|
|
411
419
|
calldata.bufferLength(),
|
|
412
|
-
|
|
420
|
+
FOUR_BYTES_UINT8ARRAY_MEMORY_CACHE.buffer, // Pass the underlying ArrayBuffer to the host
|
|
413
421
|
);
|
|
414
422
|
|
|
415
|
-
|
|
416
|
-
|
|
423
|
+
// OPTIMIZATION: Read raw memory directly using load<u32>
|
|
424
|
+
// We use .dataStart to get the raw pointer to the payload.
|
|
425
|
+
const resultLength = bswap<u32>(load<u32>(FOUR_BYTES_UINT8ARRAY_MEMORY_CACHE.dataStart));
|
|
426
|
+
|
|
417
427
|
const resultBuffer = new ArrayBuffer(resultLength);
|
|
418
428
|
getCallResult(0, resultLength, resultBuffer);
|
|
419
429
|
|
|
@@ -474,14 +484,29 @@ export class BlockchainEnvironment {
|
|
|
474
484
|
*/
|
|
475
485
|
public emit(event: NetEvent): void {
|
|
476
486
|
const data = event.getEventData();
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
487
|
+
const eventType = event.eventType;
|
|
488
|
+
const typeLen = String.UTF8.byteLength(eventType);
|
|
489
|
+
|
|
490
|
+
// Structure: [4 bytes type len] + [type bytes] + [4 bytes data len] + [data bytes]
|
|
491
|
+
const totalLen = 8 + typeLen + data.length;
|
|
492
|
+
|
|
493
|
+
const writer = new Uint8Array(totalLen);
|
|
494
|
+
const ptr = writer.dataStart;
|
|
480
495
|
|
|
481
|
-
|
|
482
|
-
|
|
496
|
+
// Write type length (BE)
|
|
497
|
+
store<u32>(ptr, bswap<u32>(typeLen));
|
|
483
498
|
|
|
484
|
-
|
|
499
|
+
// Write type string
|
|
500
|
+
String.UTF8.encodeUnsafe(changetype<usize>(eventType), eventType.length, ptr + 4);
|
|
501
|
+
|
|
502
|
+
// Write data length (BE)
|
|
503
|
+
const offset = 4 + typeLen;
|
|
504
|
+
store<u32>(ptr + offset, bswap<u32>(data.length));
|
|
505
|
+
|
|
506
|
+
// Write data bytes (Safe memory copy)
|
|
507
|
+
memory.copy(ptr + offset + 4, data.dataStart, data.length);
|
|
508
|
+
|
|
509
|
+
emit(writer.buffer, totalLen);
|
|
485
510
|
}
|
|
486
511
|
|
|
487
512
|
/**
|
|
@@ -502,11 +527,21 @@ export class BlockchainEnvironment {
|
|
|
502
527
|
* ```
|
|
503
528
|
*/
|
|
504
529
|
public validateBitcoinAddress(address: string): bool {
|
|
505
|
-
const
|
|
506
|
-
writer.writeString(address);
|
|
530
|
+
const len = String.UTF8.byteLength(address);
|
|
507
531
|
|
|
508
|
-
|
|
509
|
-
|
|
532
|
+
if (len <= SCRATCH_SIZE) {
|
|
533
|
+
String.UTF8.encodeUnsafe(
|
|
534
|
+
changetype<usize>(address),
|
|
535
|
+
address.length,
|
|
536
|
+
SCRATCH_VIEW.dataStart,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
return validateBitcoinAddress(SCRATCH_BUF, len) === 1;
|
|
540
|
+
} else {
|
|
541
|
+
const writer = new BytesWriter(len);
|
|
542
|
+
writer.writeString(address);
|
|
543
|
+
return validateBitcoinAddress(writer.getBuffer().buffer, len) === 1;
|
|
544
|
+
}
|
|
510
545
|
}
|
|
511
546
|
|
|
512
547
|
/**
|
|
@@ -672,7 +707,7 @@ export class BlockchainEnvironment {
|
|
|
672
707
|
* ```
|
|
673
708
|
*/
|
|
674
709
|
public verifySchnorrSignature(
|
|
675
|
-
publicKey:
|
|
710
|
+
publicKey: ExtendedAddress,
|
|
676
711
|
signature: Uint8Array,
|
|
677
712
|
hash: Uint8Array,
|
|
678
713
|
): boolean {
|
|
@@ -732,32 +767,29 @@ export class BlockchainEnvironment {
|
|
|
732
767
|
): boolean {
|
|
733
768
|
const publicKeyLength = MLDSAMetadata.fromLevel(level);
|
|
734
769
|
if (publicKey.length !== (publicKeyLength as i32)) {
|
|
735
|
-
throw new Revert(
|
|
736
|
-
`Invalid ML-DSA public key length. Expected ${publicKeyLength}, got ${publicKey.length}`,
|
|
737
|
-
);
|
|
770
|
+
throw new Revert(`Invalid ML-DSA public key length.`);
|
|
738
771
|
}
|
|
739
772
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
throw new Revert(
|
|
743
|
-
`Invalid ML-DSA signature length. Expected ${signatureLength}, got ${signature.length}`,
|
|
744
|
-
);
|
|
773
|
+
if (signature.length !== MLDSAMetadata.signatureLen(publicKeyLength)) {
|
|
774
|
+
throw new Revert(`Invalid ML-DSA signature length.`);
|
|
745
775
|
}
|
|
746
776
|
|
|
747
777
|
if (hash.length !== 32) {
|
|
748
|
-
throw new Revert(`Invalid hash length
|
|
778
|
+
throw new Revert(`Invalid hash length.`);
|
|
749
779
|
}
|
|
750
780
|
|
|
751
|
-
const
|
|
752
|
-
writer
|
|
753
|
-
writer.
|
|
754
|
-
writer.writeBytes(publicKey);
|
|
781
|
+
const bufferLen = 2 + publicKey.length;
|
|
782
|
+
const writer = new Uint8Array(bufferLen);
|
|
783
|
+
const ptr = writer.dataStart;
|
|
755
784
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
785
|
+
// Single bytes - Endianness irrelevant
|
|
786
|
+
store<u8>(ptr, <u8>SignaturesMethods.MLDSA);
|
|
787
|
+
store<u8>(ptr + 1, <u8>level);
|
|
788
|
+
|
|
789
|
+
// Byte array copy
|
|
790
|
+
memory.copy(ptr + 2, publicKey.dataStart, publicKey.length);
|
|
791
|
+
|
|
792
|
+
const result: u32 = verifySignature(writer.buffer, signature.buffer, hash.buffer);
|
|
761
793
|
|
|
762
794
|
return result === 1;
|
|
763
795
|
}
|
|
@@ -822,7 +854,7 @@ export class BlockchainEnvironment {
|
|
|
822
854
|
* ```
|
|
823
855
|
*/
|
|
824
856
|
public verifySignature(
|
|
825
|
-
address:
|
|
857
|
+
address: ExtendedAddress,
|
|
826
858
|
signature: Uint8Array,
|
|
827
859
|
hash: Uint8Array,
|
|
828
860
|
forceMLDSA: boolean = false,
|
|
@@ -981,29 +1013,23 @@ export class BlockchainEnvironment {
|
|
|
981
1013
|
}
|
|
982
1014
|
|
|
983
1015
|
private internalVerifySchnorr(
|
|
984
|
-
publicKey:
|
|
1016
|
+
publicKey: ExtendedAddress,
|
|
985
1017
|
signature: Uint8Array,
|
|
986
1018
|
hash: Uint8Array,
|
|
987
1019
|
): boolean {
|
|
988
|
-
if (signature.
|
|
989
|
-
|
|
990
|
-
}
|
|
1020
|
+
if (signature.length !== 64) throw new Revert(`Invalid signature length.`);
|
|
1021
|
+
if (hash.length !== 32) throw new Revert(`Invalid hash length.`);
|
|
991
1022
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1023
|
+
// 1 byte prefix + 32 bytes address
|
|
1024
|
+
const totalLen = 1 + ADDRESS_BYTE_LENGTH;
|
|
1025
|
+
const buffer = new Uint8Array(totalLen);
|
|
1026
|
+
const ptr = buffer.dataStart;
|
|
995
1027
|
|
|
996
|
-
|
|
997
|
-
writer.writeU8(<u8>SignaturesMethods.Schnorr);
|
|
998
|
-
writer.writeAddress(publicKey);
|
|
1028
|
+
store<u8>(ptr, <u8>SignaturesMethods.Schnorr);
|
|
999
1029
|
|
|
1000
|
-
|
|
1001
|
-
writer.getBuffer().buffer,
|
|
1002
|
-
signature.buffer,
|
|
1003
|
-
hash.buffer,
|
|
1004
|
-
);
|
|
1030
|
+
memory.copy(ptr + 1, publicKey.tweakedPublicKey.dataStart, ADDRESS_BYTE_LENGTH);
|
|
1005
1031
|
|
|
1006
|
-
return
|
|
1032
|
+
return verifySignature(buffer.buffer, signature.buffer, hash.buffer) === 1;
|
|
1007
1033
|
}
|
|
1008
1034
|
|
|
1009
1035
|
private createContractIfNotExists(): void {
|
|
@@ -1017,17 +1043,16 @@ export class BlockchainEnvironment {
|
|
|
1017
1043
|
}
|
|
1018
1044
|
|
|
1019
1045
|
private _internalSetStorageAt(pointerHash: Uint8Array, value: Uint8Array): void {
|
|
1020
|
-
if (pointerHash.
|
|
1046
|
+
if (pointerHash.length !== 32) {
|
|
1021
1047
|
throw new Revert('Pointer must be 32 bytes long');
|
|
1022
1048
|
}
|
|
1023
1049
|
|
|
1024
1050
|
let finalValue: Uint8Array = value;
|
|
1025
|
-
if (value.
|
|
1026
|
-
//
|
|
1051
|
+
if (value.length !== 32) {
|
|
1052
|
+
// Optimization: Pad manually using memory.copy to avoid loop
|
|
1027
1053
|
finalValue = new Uint8Array(32);
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
}
|
|
1054
|
+
const len = value.length < 32 ? value.length : 32;
|
|
1055
|
+
memory.copy(finalValue.dataStart, value.dataStart, len);
|
|
1031
1056
|
}
|
|
1032
1057
|
|
|
1033
1058
|
this.storage.set(pointerHash, finalValue);
|
package/runtime/env/global.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
|
|
3
3
|
import { MLDSAMetadata, MLDSASecurityLevel } from './consensus/MLDSAMetadata';
|
|
4
|
-
import { BytesReader } from '../buffer/BytesReader';
|
|
5
|
-
import { BytesWriter } from '../buffer/BytesWriter';
|
|
6
4
|
import { ADDRESS_BYTE_LENGTH } from '../utils';
|
|
7
5
|
|
|
8
6
|
/**
|
|
@@ -331,24 +329,36 @@ declare function loadMLDSA(key: ArrayBuffer, result: ArrayBuffer): void;
|
|
|
331
329
|
* ```
|
|
332
330
|
*/
|
|
333
331
|
export function loadMLDSAPublicKey(address: Uint8Array, level: MLDSASecurityLevel): Uint8Array {
|
|
334
|
-
const length = MLDSAMetadata.fromLevel(level);
|
|
335
|
-
const resultBuffer = new Uint8Array(
|
|
336
|
-
1 + length,
|
|
337
|
-
);
|
|
332
|
+
const length = MLDSAMetadata.fromLevel(level) as i32;
|
|
338
333
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
334
|
+
// Prepare Input: [Level (1 byte) + Address (32 bytes)]
|
|
335
|
+
// Allocation is cheap for small fixed sizes.
|
|
336
|
+
const inputBuffer = new Uint8Array(1 + ADDRESS_BYTE_LENGTH);
|
|
337
|
+
const inputPtr = inputBuffer.dataStart;
|
|
342
338
|
|
|
343
|
-
|
|
339
|
+
store<u8>(inputPtr, level as u8);
|
|
344
340
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
341
|
+
// Copy address bytes directly
|
|
342
|
+
memory.copy(inputPtr + 1, address.dataStart, ADDRESS_BYTE_LENGTH);
|
|
343
|
+
|
|
344
|
+
// Prepare Output: [Exists (1 byte) + Key (length bytes)]
|
|
345
|
+
const resultBuffer = new Uint8Array(1 + length);
|
|
346
|
+
|
|
347
|
+
// Host Call
|
|
348
|
+
loadMLDSA(inputBuffer.buffer, resultBuffer.buffer);
|
|
349
|
+
|
|
350
|
+
// Check Exists (Byte 0) via direct load
|
|
351
|
+
if (load<u8>(resultBuffer.dataStart) === 0) {
|
|
348
352
|
throw new Error('ML-DSA public key not found');
|
|
349
353
|
}
|
|
350
354
|
|
|
351
|
-
|
|
355
|
+
// Return Key
|
|
356
|
+
// slice(1) creates a new buffer with just the key.
|
|
357
|
+
|
|
358
|
+
// Note: using .subarray(1) would be O(1) (view) vs .slice(1) which is O(N) (copy).
|
|
359
|
+
// slice is safer if you need a standalone buffer, but subarray is faster for gas.
|
|
360
|
+
// Sticking to slice to match original behavior (fresh buffer).
|
|
361
|
+
return resultBuffer.slice(1);
|
|
352
362
|
}
|
|
353
363
|
|
|
354
364
|
export * from './Atomic';
|