@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.
@@ -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 { ApprovedEvent, ApprovedForAllEvent, MAX_URI_LENGTH, TransferredEvent, URIEvent } from '../events/predefined';
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() || to === Address.dead()) {
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() || to === Address.dead()) {
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() || owner === Address.dead()) {
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
- ADDRESS_BYTE_LENGTH * 2 +
759
- U256_BYTE_LENGTH +
760
- U32_BYTE_LENGTH +
761
- data.length,
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
- verifySchnorrSignature,
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: Address = Address.dead();
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.readAddress();
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
- this._tx = new Transaction(caller, origin, txId, txHash);
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
- const result: u32 = verifySchnorrSignature(publicKey.buffer, signature.buffer, hash.buffer);
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
- public readonly sender: Address, // "immediate caller"
14
- public readonly origin: Address, // "leftmost thing in the call chain"
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
+ }