@btc-vision/btc-runtime 1.9.16 → 1.10.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/package.json +2 -2
- package/runtime/buffer/BytesReader.ts +1 -1
- package/runtime/constants/Exports.ts +11 -11
- package/runtime/contracts/OP20.ts +312 -12
- package/runtime/contracts/OP721.ts +15 -9
- package/runtime/env/BlockchainEnvironment.ts +202 -5
- package/runtime/env/classes/Transaction.ts +36 -3
- package/runtime/env/consensus/ConsensusRules.ts +228 -0
- package/runtime/env/consensus/MLDSAMetadata.ts +181 -0
- package/runtime/env/consensus/Signatures.ts +7 -0
- package/runtime/env/global.ts +111 -7
- package/runtime/exports/index.ts +2 -1
- package/runtime/index.ts +7 -11
- package/runtime/interfaces/as.ts +5 -0
- package/runtime/shared-libraries/TransferHelper.ts +4 -17
- package/runtime/types/Address.ts +230 -46
- package/runtime/types/ExtendedAddress.ts +426 -0
- package/runtime/nested/PointerManager.ts +0 -55
- package/runtime/nested/codecs/AddressCodec.ts +0 -19
- package/runtime/nested/codecs/BooleanCodec.ts +0 -17
- package/runtime/nested/codecs/Ids.ts +0 -10
- package/runtime/nested/codecs/NumericCodec.ts +0 -58
- package/runtime/nested/codecs/StringCodec.ts +0 -24
- package/runtime/nested/codecs/U256Codec.ts +0 -20
- package/runtime/nested/codecs/VariableBytesCodec.ts +0 -134
- package/runtime/nested/interfaces/ICodec.ts +0 -12
- package/runtime/nested/storage/StorageMap.ts +0 -207
- package/runtime/nested/storage/StorageSet.ts +0 -60
|
@@ -25,7 +25,13 @@ import { IOP721 } from './interfaces/IOP721';
|
|
|
25
25
|
import { OP721InitParameters } from './interfaces/OP721InitParameters';
|
|
26
26
|
import { ReentrancyGuard } from './ReentrancyGuard';
|
|
27
27
|
import { StoredMapU256 } from '../storage/maps/StoredMapU256';
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
ApprovedEvent,
|
|
30
|
+
ApprovedForAllEvent,
|
|
31
|
+
MAX_URI_LENGTH,
|
|
32
|
+
TransferredEvent,
|
|
33
|
+
URIEvent,
|
|
34
|
+
} from '../events/predefined';
|
|
29
35
|
import {
|
|
30
36
|
ON_OP721_RECEIVED_SELECTOR,
|
|
31
37
|
OP712_DOMAIN_TYPE_HASH,
|
|
@@ -555,7 +561,7 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
555
561
|
}
|
|
556
562
|
|
|
557
563
|
protected _mint(to: Address, tokenId: u256): void {
|
|
558
|
-
if (to === Address.zero()
|
|
564
|
+
if (to === Address.zero()) {
|
|
559
565
|
throw new Revert('Cannot mint to zero address');
|
|
560
566
|
}
|
|
561
567
|
if (this._exists(tokenId)) {
|
|
@@ -630,7 +636,7 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
630
636
|
throw new Revert('Transfer from incorrect owner');
|
|
631
637
|
}
|
|
632
638
|
|
|
633
|
-
if (to === Address.zero()
|
|
639
|
+
if (to === Address.zero()) {
|
|
634
640
|
throw new Revert('Transfer to zero address');
|
|
635
641
|
}
|
|
636
642
|
|
|
@@ -717,7 +723,7 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
717
723
|
}
|
|
718
724
|
|
|
719
725
|
protected _balanceOf(owner: Address): u256 {
|
|
720
|
-
if (owner === Address.zero()
|
|
726
|
+
if (owner === Address.zero()) {
|
|
721
727
|
throw new Revert('Invalid address');
|
|
722
728
|
}
|
|
723
729
|
return this.balanceOfMap.get(owner);
|
|
@@ -755,10 +761,10 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
755
761
|
): void {
|
|
756
762
|
const calldata = new BytesWriter(
|
|
757
763
|
SELECTOR_BYTE_LENGTH +
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
764
|
+
ADDRESS_BYTE_LENGTH * 2 +
|
|
765
|
+
U256_BYTE_LENGTH +
|
|
766
|
+
U32_BYTE_LENGTH +
|
|
767
|
+
data.length,
|
|
762
768
|
);
|
|
763
769
|
calldata.writeSelector(ON_OP721_RECEIVED_SELECTOR);
|
|
764
770
|
calldata.writeAddress(Blockchain.tx.sender);
|
|
@@ -955,7 +961,7 @@ export abstract class OP721 extends ReentrancyGuard implements IOP721 {
|
|
|
955
961
|
protected _addressFromU256(value: u256): Address {
|
|
956
962
|
// Convert u256 back to 32-byte address
|
|
957
963
|
const bytes = value.toUint8Array(true); // Returns 32 bytes in BE
|
|
958
|
-
const addr = new Address();
|
|
964
|
+
const addr = new Address([]);
|
|
959
965
|
|
|
960
966
|
// Direct copy since both are 32 bytes
|
|
961
967
|
for (let i: i32 = 0; i < 32; i++) {
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
tLoadPointer,
|
|
24
24
|
tStorePointer,
|
|
25
25
|
validateBitcoinAddress,
|
|
26
|
-
|
|
26
|
+
verifySignature,
|
|
27
27
|
} from './global';
|
|
28
28
|
import { eqUint, MapUint8Array } from '../generic/MapUint8Array';
|
|
29
29
|
import { EMPTY_BUFFER } from '../math/bytes';
|
|
@@ -32,6 +32,9 @@ import { Calldata } from '../types';
|
|
|
32
32
|
import { Revert } from '../types/Revert';
|
|
33
33
|
import { Selector } from '../math/abi';
|
|
34
34
|
import { Network, Networks } from '../script/Networks';
|
|
35
|
+
import { ExtendedAddress } from '../types/ExtendedAddress';
|
|
36
|
+
import { SignaturesMethods } from './consensus/Signatures';
|
|
37
|
+
import { MLDSAMetadata, MLDSASecurityLevel } from './consensus/MLDSAMetadata';
|
|
35
38
|
|
|
36
39
|
export * from '../env/global';
|
|
37
40
|
|
|
@@ -61,7 +64,7 @@ export class BlockchainEnvironment {
|
|
|
61
64
|
* Standard dead address for burn operations.
|
|
62
65
|
* Assets sent here are permanently unrecoverable.
|
|
63
66
|
*/
|
|
64
|
-
public readonly DEAD_ADDRESS:
|
|
67
|
+
public readonly DEAD_ADDRESS: ExtendedAddress = ExtendedAddress.dead();
|
|
65
68
|
|
|
66
69
|
private storage: MapUint8Array = new MapUint8Array();
|
|
67
70
|
private transientStorage: MapUint8Array = new MapUint8Array();
|
|
@@ -339,11 +342,15 @@ export class BlockchainEnvironment {
|
|
|
339
342
|
const contractAddress = reader.readAddress();
|
|
340
343
|
const contractDeployer = reader.readAddress();
|
|
341
344
|
const caller = reader.readAddress();
|
|
342
|
-
const origin = reader.
|
|
345
|
+
const origin = reader.readBytesArray(32);
|
|
343
346
|
const chainId = reader.readBytes(32);
|
|
344
347
|
const protocolId = reader.readBytes(32);
|
|
348
|
+
const tweakedPublicKey = reader.readBytesArray(ADDRESS_BYTE_LENGTH);
|
|
349
|
+
const consensusFlags = reader.readU64();
|
|
345
350
|
|
|
346
|
-
|
|
351
|
+
const originAddress = new ExtendedAddress(tweakedPublicKey, origin);
|
|
352
|
+
|
|
353
|
+
this._tx = new Transaction(caller, originAddress, txId, txHash, consensusFlags);
|
|
347
354
|
this._contractDeployer = contractDeployer;
|
|
348
355
|
this._contractAddress = contractAddress;
|
|
349
356
|
this._chainId = chainId;
|
|
@@ -650,6 +657,10 @@ export class BlockchainEnvironment {
|
|
|
650
657
|
* - Linear aggregation properties
|
|
651
658
|
* - Used in Taproot (post-2021)
|
|
652
659
|
*
|
|
660
|
+
* @deprecated Use Blockchain.verifySignature() instead for automatic consensus migration.
|
|
661
|
+
* verifySignature() supports both Schnorr and ML-DSA signatures with proper
|
|
662
|
+
* consensus flag handling for quantum resistance transitions.
|
|
663
|
+
*
|
|
653
664
|
* @example
|
|
654
665
|
* ```typescript
|
|
655
666
|
* const isValid = Blockchain.verifySchnorrSignature(
|
|
@@ -665,10 +676,170 @@ export class BlockchainEnvironment {
|
|
|
665
676
|
signature: Uint8Array,
|
|
666
677
|
hash: Uint8Array,
|
|
667
678
|
): boolean {
|
|
668
|
-
|
|
679
|
+
WARNING(
|
|
680
|
+
'verifySchnorrSignature is deprecated. Use verifySignature() for consensus-aware signature verification and quantum resistance support.',
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
return this.internalVerifySchnorr(publicKey, signature, hash);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Verifies an ML-DSA signature (quantum-resistant).
|
|
688
|
+
*
|
|
689
|
+
* @param level - Security level (MLDSASecurityLevel.Level2: ML-DSA-44, MLDSASecurityLevel.Level3: ML-DSA-65, MLDSASecurityLevel.Level5: ML-DSA-87)
|
|
690
|
+
* @param publicKey - ML-DSA public key (1312/1952/2592 bytes based on level)
|
|
691
|
+
* @param signature - ML-DSA signature (2420/3309/4627 bytes based on level)
|
|
692
|
+
* @param hash - 32-byte message hash
|
|
693
|
+
* @returns true if signature is valid
|
|
694
|
+
*
|
|
695
|
+
* @warning ML-DSA provides quantum resistance:
|
|
696
|
+
* - NIST standardized lattice-based signatures
|
|
697
|
+
* - Larger keys/signatures than classical algorithms
|
|
698
|
+
* - Security levels: 2 (ML-DSA-44), 3 (ML-DSA-65), 5 (ML-DSA-87)
|
|
699
|
+
*
|
|
700
|
+
* @throws {Revert} If public key length doesn't match level
|
|
701
|
+
* @throws {Revert} If signature length doesn't match level
|
|
702
|
+
* @throws {Revert} If hash is not 32 bytes
|
|
703
|
+
*
|
|
704
|
+
* @example
|
|
705
|
+
* ```typescript
|
|
706
|
+
* // ML-DSA-44 (security level 2)
|
|
707
|
+
* const isValid = Blockchain.verifyMLDSASignature(
|
|
708
|
+
* MLDSASecurityLevel.Level2, // level 0 = ML-DSA-44
|
|
709
|
+
* mldsaPublicKey, // 1312 bytes
|
|
710
|
+
* mldsaSignature, // 2420 bytes
|
|
711
|
+
* messageHash // 32 bytes
|
|
712
|
+
* );
|
|
713
|
+
* if (!isValid) throw new Revert("Invalid ML-DSA signature");
|
|
714
|
+
* ```
|
|
715
|
+
*
|
|
716
|
+
* @example
|
|
717
|
+
* ```typescript
|
|
718
|
+
* // ML-DSA-87 (highest security level 5)
|
|
719
|
+
* const isValid = Blockchain.verifyMLDSASignature(
|
|
720
|
+
* MLDSASecurityLevel.Level5, // level 2 = ML-DSA-87
|
|
721
|
+
* mldsaPublicKey, // 2592 bytes
|
|
722
|
+
* mldsaSignature, // 4627 bytes
|
|
723
|
+
* messageHash // 32 bytes
|
|
724
|
+
* );
|
|
725
|
+
* ```
|
|
726
|
+
*/
|
|
727
|
+
public verifyMLDSASignature(
|
|
728
|
+
level: MLDSASecurityLevel,
|
|
729
|
+
publicKey: Uint8Array,
|
|
730
|
+
signature: Uint8Array,
|
|
731
|
+
hash: Uint8Array,
|
|
732
|
+
): boolean {
|
|
733
|
+
const publicKeyLength = MLDSAMetadata.fromLevel(level);
|
|
734
|
+
if (publicKey.length !== (publicKeyLength as i32)) {
|
|
735
|
+
throw new Revert(
|
|
736
|
+
`Invalid ML-DSA public key length. Expected ${publicKeyLength}, got ${publicKey.length}`,
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const signatureLength = MLDSAMetadata.signatureLen(publicKeyLength);
|
|
741
|
+
if (signature.length !== signatureLength) {
|
|
742
|
+
throw new Revert(
|
|
743
|
+
`Invalid ML-DSA signature length. Expected ${signatureLength}, got ${signature.length}`,
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (hash.length !== 32) {
|
|
748
|
+
throw new Revert(`Invalid hash length. Expected 32, got ${hash.length}`);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const writer = new BytesWriter(2 + publicKey.length);
|
|
752
|
+
writer.writeU8(<u8>SignaturesMethods.MLDSA);
|
|
753
|
+
writer.writeU8(<u8>level);
|
|
754
|
+
writer.writeBytes(publicKey);
|
|
755
|
+
|
|
756
|
+
const result: u32 = verifySignature(
|
|
757
|
+
writer.getBuffer().buffer,
|
|
758
|
+
signature.buffer,
|
|
759
|
+
hash.buffer,
|
|
760
|
+
);
|
|
761
|
+
|
|
669
762
|
return result === 1;
|
|
670
763
|
}
|
|
671
764
|
|
|
765
|
+
/**
|
|
766
|
+
* Verifies a signature based on current consensus rules.
|
|
767
|
+
*
|
|
768
|
+
* This method automatically selects the appropriate signature verification algorithm
|
|
769
|
+
* based on the current consensus state:
|
|
770
|
+
*
|
|
771
|
+
* - When `unsafeSignaturesAllowed()` returns `true`: Uses Schnorr signatures (quantum-vulnerable)
|
|
772
|
+
* - When `unsafeSignaturesAllowed()` returns `false`: Uses ML-DSA signatures (quantum-resistant)
|
|
773
|
+
*
|
|
774
|
+
* The `unsafeSignaturesAllowed()` flag indicates whether the network is still accepting
|
|
775
|
+
* legacy Schnorr signatures. This flag will be `true` during the transition period to
|
|
776
|
+
* maintain backwards compatibility with existing infrastructure. Once the network completes
|
|
777
|
+
* its quantum-resistant upgrade, this flag will permanently become `false`, enforcing
|
|
778
|
+
* ML-DSA signatures exclusively.
|
|
779
|
+
*
|
|
780
|
+
* @param address - The address containing the public key(s) to verify against.
|
|
781
|
+
* For Schnorr, uses the taproot tweaked public key.
|
|
782
|
+
* For ML-DSA, uses the ML-DSA public key component.
|
|
783
|
+
* @param signature - The signature bytes to verify. Format depends on algorithm:
|
|
784
|
+
* - Schnorr: 64-byte signature
|
|
785
|
+
* - ML-DSA-44 (Level 2): 2420 bytes
|
|
786
|
+
* - ML-DSA-65 (Level 3): 3309 bytes
|
|
787
|
+
* - ML-DSA-87 (Level 5): 4627 bytes
|
|
788
|
+
* @param hash - The 32-byte message hash that was signed. Usually a SHA256 hash
|
|
789
|
+
* of the transaction data or message being verified.
|
|
790
|
+
*
|
|
791
|
+
* @param forceMLDSA - Optional flag to force ML-DSA verification even if Schnorr is allowed.
|
|
792
|
+
*
|
|
793
|
+
* @returns `true` if the signature is valid for the given address and hash,
|
|
794
|
+
* `false` if verification fails or if the signature format is invalid
|
|
795
|
+
*
|
|
796
|
+
* @throws May throw if the signature or hash have invalid lengths for the selected
|
|
797
|
+
* algorithm, though implementations should generally return false instead
|
|
798
|
+
*
|
|
799
|
+
* @remarks
|
|
800
|
+
* The consensus rules determine which signature scheme is active:
|
|
801
|
+
* - Pre-quantum era: Schnorr signatures (Bitcoin taproot compatible)
|
|
802
|
+
* - Post-quantum era: ML-DSA Level 2 (NIST standardized, 128-bit quantum security)
|
|
803
|
+
*
|
|
804
|
+
* ML-DSA Level 2 (ML-DSA-44) corresponds to NIST security category 2, providing security
|
|
805
|
+
* equivalent to AES-128 against both classical and quantum attacks. Its security is based on
|
|
806
|
+
* the hardness of underlying lattice problems and is designed to resist attacks from quantum
|
|
807
|
+
* computers, including both Shor's algorithm (which breaks RSA/ECC) and Grover's algorithm
|
|
808
|
+
* (which reduces symmetric key security). The security levels (2, 3, 5) correspond to the
|
|
809
|
+
* number of quantum gates required for Grover's algorithm to break them, equivalent to
|
|
810
|
+
* AES-128, AES-192, and AES-256 respectively.
|
|
811
|
+
*
|
|
812
|
+
* @example
|
|
813
|
+
* ```typescript
|
|
814
|
+
* const isValid = contract.verifySignature(
|
|
815
|
+
* senderAddress,
|
|
816
|
+
* signatureBytes,
|
|
817
|
+
* transactionHash
|
|
818
|
+
* );
|
|
819
|
+
* if (!isValid) {
|
|
820
|
+
* throw new Error("Invalid signature");
|
|
821
|
+
* }
|
|
822
|
+
* ```
|
|
823
|
+
*/
|
|
824
|
+
public verifySignature(
|
|
825
|
+
address: Address,
|
|
826
|
+
signature: Uint8Array,
|
|
827
|
+
hash: Uint8Array,
|
|
828
|
+
forceMLDSA: boolean = false,
|
|
829
|
+
): boolean {
|
|
830
|
+
if (this.tx.consensus.unsafeSignaturesAllowed() && !forceMLDSA) {
|
|
831
|
+
return this.internalVerifySchnorr(address, signature, hash);
|
|
832
|
+
} else {
|
|
833
|
+
// Default to ML-DSA Level 2 (ML-DSA-44) for quantum resistance
|
|
834
|
+
return this.verifyMLDSASignature(
|
|
835
|
+
MLDSASecurityLevel.Level2,
|
|
836
|
+
address.mldsaPublicKey,
|
|
837
|
+
signature,
|
|
838
|
+
hash,
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
672
843
|
/**
|
|
673
844
|
* Checks if an address is a contract (not an EOA).
|
|
674
845
|
*
|
|
@@ -809,6 +980,32 @@ export class BlockchainEnvironment {
|
|
|
809
980
|
return Uint8Array.wrap(hash);
|
|
810
981
|
}
|
|
811
982
|
|
|
983
|
+
private internalVerifySchnorr(
|
|
984
|
+
publicKey: Address,
|
|
985
|
+
signature: Uint8Array,
|
|
986
|
+
hash: Uint8Array,
|
|
987
|
+
): boolean {
|
|
988
|
+
if (signature.byteLength !== 64) {
|
|
989
|
+
throw new Revert(`Invalid signature length. Expected 64, got ${signature.length}`);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (hash.byteLength !== 32) {
|
|
993
|
+
throw new Revert(`Invalid hash length. Expected 32, got ${hash.length}`);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
const writer = new BytesWriter(1 + ADDRESS_BYTE_LENGTH);
|
|
997
|
+
writer.writeU8(<u8>SignaturesMethods.Schnorr);
|
|
998
|
+
writer.writeAddress(publicKey);
|
|
999
|
+
|
|
1000
|
+
const result: u32 = verifySignature(
|
|
1001
|
+
writer.getBuffer().buffer,
|
|
1002
|
+
signature.buffer,
|
|
1003
|
+
hash.buffer,
|
|
1004
|
+
);
|
|
1005
|
+
|
|
1006
|
+
return result === 1;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
812
1009
|
private createContractIfNotExists(): void {
|
|
813
1010
|
if (!this._contract) {
|
|
814
1011
|
throw new Revert('Contract is required');
|
|
@@ -4,17 +4,50 @@ import { Potential } from '../../lang/Definitions';
|
|
|
4
4
|
import { BytesReader } from '../../buffer/BytesReader';
|
|
5
5
|
import { getInputsSize, getOutputsSize, inputs, outputs } from '../global';
|
|
6
6
|
import { TransactionDecoder } from '../decoders/TransactionDecoder';
|
|
7
|
+
import { ConsensusRules } from '../consensus/ConsensusRules';
|
|
8
|
+
import { ExtendedAddress } from '../../types/ExtendedAddress';
|
|
7
9
|
|
|
8
10
|
@final
|
|
9
11
|
export class Transaction {
|
|
12
|
+
public readonly consensus: ConsensusRules;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The `sender` is the immediate caller of this transaction, which may be a contract or a user.
|
|
16
|
+
* It is always typed as `Address`, which may not be quantum-resistant.
|
|
17
|
+
*
|
|
18
|
+
* The `origin` is the original transaction signer (the "leftmost" entity in the call chain).
|
|
19
|
+
* It is typed as `ExtendedAddress`, allowing for both Schnorr and ML-DSA keys (including quantum-resistant keys).
|
|
20
|
+
*
|
|
21
|
+
* This distinction is intentional: only the transaction originator may use quantum-resistant keys,
|
|
22
|
+
* while intermediate contract callers are always standard addresses. This should be considered when
|
|
23
|
+
* performing address verification or signature checks.
|
|
24
|
+
*/
|
|
25
|
+
public readonly sender: Address;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The `origin` is the original transaction signer (the "leftmost" entity in the call chain).
|
|
29
|
+
* It is typed as `ExtendedAddress`, allowing for both Schnorr and ML-DSA keys (including quantum-resistant keys).
|
|
30
|
+
*
|
|
31
|
+
* This distinction is intentional: only the transaction originator may use quantum-resistant keys,
|
|
32
|
+
* while intermediate contract callers are always standard addresses. This should be considered when
|
|
33
|
+
* performing address verification or signature checks.
|
|
34
|
+
*
|
|
35
|
+
*/
|
|
36
|
+
public readonly origin: ExtendedAddress;
|
|
37
|
+
|
|
10
38
|
private readonly transactionDecoder: TransactionDecoder = new TransactionDecoder();
|
|
11
39
|
|
|
12
40
|
public constructor(
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
sender: Address, // "immediate caller"
|
|
42
|
+
origin: ExtendedAddress, // "leftmost thing in the call chain"
|
|
15
43
|
public readonly txId: Uint8Array,
|
|
16
44
|
public readonly hash: Uint8Array,
|
|
17
|
-
|
|
45
|
+
consensusFlags: u64,
|
|
46
|
+
) {
|
|
47
|
+
this.sender = sender;
|
|
48
|
+
this.origin = origin;
|
|
49
|
+
this.consensus = new ConsensusRules(consensusFlags);
|
|
50
|
+
}
|
|
18
51
|
|
|
19
52
|
private _inputs: Potential<TransactionInput[]> = null;
|
|
20
53
|
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consensus flags for protocol behavior control
|
|
3
|
+
*/
|
|
4
|
+
@final
|
|
5
|
+
export class ConsensusRules {
|
|
6
|
+
// Flag constants
|
|
7
|
+
public static readonly NONE: u64 = 0b00000000;
|
|
8
|
+
public static readonly UNSAFE_QUANTUM_SIGNATURES_ALLOWED: u64 = 0b00000001;
|
|
9
|
+
public static readonly RESERVED_FLAG_1: u64 = 0b00000010;
|
|
10
|
+
public static readonly RESERVED_FLAG_2: u64 = 0b00000100;
|
|
11
|
+
|
|
12
|
+
private value: u64;
|
|
13
|
+
|
|
14
|
+
constructor(value: u64 = 0) {
|
|
15
|
+
this.value = value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new empty ConsensusRules
|
|
20
|
+
*/
|
|
21
|
+
public static new(): ConsensusRules {
|
|
22
|
+
return new ConsensusRules(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates ConsensusRules from u64 value
|
|
27
|
+
*/
|
|
28
|
+
public static fromU64(value: u64): ConsensusRules {
|
|
29
|
+
return new ConsensusRules(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Helper to create flags from multiple flag values
|
|
34
|
+
*/
|
|
35
|
+
public static combine(flags: u64[]): ConsensusRules {
|
|
36
|
+
let result: u64 = 0;
|
|
37
|
+
for (let i = 0; i < flags.length; i++) {
|
|
38
|
+
result |= flags[i];
|
|
39
|
+
}
|
|
40
|
+
return new ConsensusRules(result);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Gets the underlying u64 value
|
|
45
|
+
*/
|
|
46
|
+
public asU64(): u64 {
|
|
47
|
+
return this.value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Converts to big-endian byte array
|
|
52
|
+
*/
|
|
53
|
+
public toBeBytes(): Uint8Array {
|
|
54
|
+
const bytes = new Uint8Array(8);
|
|
55
|
+
const view = new DataView(bytes.buffer);
|
|
56
|
+
view.setUint64(0, this.value, false); // false = big-endian
|
|
57
|
+
return bytes;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Checks if all flags in 'other' are set
|
|
62
|
+
*/
|
|
63
|
+
public contains(other: ConsensusRules): boolean {
|
|
64
|
+
return (this.value & other.value) == other.value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Checks if flag value is set
|
|
69
|
+
*/
|
|
70
|
+
public containsFlag(flag: u64): boolean {
|
|
71
|
+
return (this.value & flag) == flag;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Checks if any flags in 'other' are set
|
|
76
|
+
*/
|
|
77
|
+
public intersects(other: ConsensusRules): boolean {
|
|
78
|
+
return (this.value & other.value) != 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Checks if any flag value is set
|
|
83
|
+
*/
|
|
84
|
+
public intersectsFlag(flag: u64): boolean {
|
|
85
|
+
return (this.value & flag) != 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Checks if no flags are set
|
|
90
|
+
*/
|
|
91
|
+
public isEmpty(): boolean {
|
|
92
|
+
return this.value == 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Returns union of flags
|
|
97
|
+
*/
|
|
98
|
+
public union(other: ConsensusRules): ConsensusRules {
|
|
99
|
+
return new ConsensusRules(this.value | other.value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Returns intersection of flags
|
|
104
|
+
*/
|
|
105
|
+
public intersection(other: ConsensusRules): ConsensusRules {
|
|
106
|
+
return new ConsensusRules(this.value & other.value);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns difference of flags (flags in this but not in other)
|
|
111
|
+
*/
|
|
112
|
+
public difference(other: ConsensusRules): ConsensusRules {
|
|
113
|
+
return new ConsensusRules(this.value & ~other.value);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Returns symmetric difference of flags
|
|
118
|
+
*/
|
|
119
|
+
public symmetricDifference(other: ConsensusRules): ConsensusRules {
|
|
120
|
+
return new ConsensusRules(this.value ^ other.value);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Returns complement of flags
|
|
125
|
+
*/
|
|
126
|
+
public complement(): ConsensusRules {
|
|
127
|
+
return new ConsensusRules(~this.value);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Inserts flags from other
|
|
132
|
+
*/
|
|
133
|
+
public insert(other: ConsensusRules): void {
|
|
134
|
+
this.value |= other.value;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Inserts a flag value
|
|
139
|
+
*/
|
|
140
|
+
public insertFlag(flag: u64): void {
|
|
141
|
+
this.value |= flag;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Removes flags from other
|
|
146
|
+
*/
|
|
147
|
+
public remove(other: ConsensusRules): void {
|
|
148
|
+
this.value &= ~other.value;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Removes a flag value
|
|
153
|
+
*/
|
|
154
|
+
public removeFlag(flag: u64): void {
|
|
155
|
+
this.value &= ~flag;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Toggles flags from other
|
|
160
|
+
*/
|
|
161
|
+
public toggle(other: ConsensusRules): void {
|
|
162
|
+
this.value ^= other.value;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Toggles a flag value
|
|
167
|
+
*/
|
|
168
|
+
public toggleFlag(flag: u64): void {
|
|
169
|
+
this.value ^= flag;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Sets or clears flags based on value
|
|
174
|
+
*/
|
|
175
|
+
public set(other: ConsensusRules, value: boolean): void {
|
|
176
|
+
if (value) {
|
|
177
|
+
this.insert(other);
|
|
178
|
+
} else {
|
|
179
|
+
this.remove(other);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Sets or clears a flag based on value
|
|
185
|
+
*/
|
|
186
|
+
public setFlag(flag: u64, value: boolean): void {
|
|
187
|
+
if (value) {
|
|
188
|
+
this.insertFlag(flag);
|
|
189
|
+
} else {
|
|
190
|
+
this.removeFlag(flag);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Creates a copy of this ConsensusFlags
|
|
196
|
+
*/
|
|
197
|
+
public clone(): ConsensusRules {
|
|
198
|
+
return new ConsensusRules(this.value);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Checks equality with another ConsensusFlags
|
|
203
|
+
*/
|
|
204
|
+
public equals(other: ConsensusRules): boolean {
|
|
205
|
+
return this.value == other.value;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Returns binary string representation
|
|
210
|
+
*/
|
|
211
|
+
public toBinaryString(): string {
|
|
212
|
+
let result = '';
|
|
213
|
+
let val = this.value;
|
|
214
|
+
for (let i = 0; i < 64; i++) {
|
|
215
|
+
result = (val & 1 ? '1' : '0') + result;
|
|
216
|
+
val = val >> 1;
|
|
217
|
+
}
|
|
218
|
+
return '0b' + result;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
public unsafeSignaturesAllowed(): boolean {
|
|
222
|
+
return this.containsFlag(ConsensusRules.UNSAFE_QUANTUM_SIGNATURES_ALLOWED);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function createConsensusFlags(flags: u64[]): ConsensusRules {
|
|
227
|
+
return ConsensusRules.combine(flags);
|
|
228
|
+
}
|