diff --git a/chain/src/tests/mod.rs b/chain/src/tests/mod.rs index a0be4443ba..18d202a16c 100644 --- a/chain/src/tests/mod.rs +++ b/chain/src/tests/mod.rs @@ -9,5 +9,6 @@ mod load_input_data_hash_cell; mod non_contextual_block_txs_verify; mod reward; mod truncate; +mod txs_verify_cache; mod uncle; mod util; diff --git a/chain/src/tests/txs_verify_cache.rs b/chain/src/tests/txs_verify_cache.rs new file mode 100644 index 0000000000..4f807eb336 --- /dev/null +++ b/chain/src/tests/txs_verify_cache.rs @@ -0,0 +1,338 @@ +use ckb_app_config::BlockAssemblerConfig; +use ckb_chain_spec::consensus::Consensus; +use ckb_dao_utils::genesis_dao_data; +use ckb_launcher::SharedBuilder; +use ckb_shared::Shared; +use ckb_store::ChainStore; +use ckb_test_chain_utils::always_success_cell; +use ckb_types::{ + bytes, + core::{ + capacity_bytes, hardfork::HardForkSwitch, BlockNumber, BlockView, Capacity, DepType, + EpochExt, EpochNumberWithFraction, HeaderView, ScriptHashType, TransactionView, + }, + packed, + prelude::*, + utilities::difficulty_to_compact, + U256, +}; +use ckb_verification_traits::Switch; +use faketime::unix_time_as_millis; +use lazy_static::lazy_static; + +use std::{convert::TryInto as _, fs::File, io::Read as _, path::Path, sync::Arc}; + +use crate::{ + chain::{ChainController, ChainService}, + tests::util::dummy_network, +}; + +const CYCLES_IN_VM0: u64 = 696; +const CYCLES_IN_VM1: u64 = 686; + +lazy_static! { + static ref LOCK_SCRIPT_CELL: (packed::CellOutput, bytes::Bytes, packed::Script) = + create_lock_script_cell(); +} + +fn create_lock_script_cell() -> (packed::CellOutput, bytes::Bytes, packed::Script) { + let mut buffer = Vec::new(); + { + let file_path = + Path::new(env!("CARGO_MANIFEST_DIR")).join("../script/testdata/mop_adc_lock"); + let mut file = File::open(file_path).unwrap(); + file.read_to_end(&mut buffer).unwrap(); + } + + let data: bytes::Bytes = buffer.into(); + + let (_, _, always_success_script) = always_success_cell(); + let cell = packed::CellOutput::new_builder() + .type_(Some(always_success_script.clone()).pack()) + .capacity(Capacity::bytes(data.len()).unwrap().pack()) + .build(); + + let script = packed::Script::new_builder() + .hash_type(ScriptHashType::Data.into()) + .code_hash(packed::CellOutput::calc_data_hash(&data)) + .build(); + + (cell, data, script) +} + +fn lock_script_cell() -> &'static (packed::CellOutput, bytes::Bytes, packed::Script) { + &LOCK_SCRIPT_CELL +} + +// Creates a consensus with: +// - Fixed epoch length. +// - Hardfork for feature RFC-0032. +// - A cell of a lock script which has different cycles in different VMs. +fn create_consensus() -> ( + Consensus, + Vec, + packed::Script, + TransactionView, +) { + let epoch_length = 10; + let epoch_when_enable_vm1 = 2; + let (always_success_cell, always_success_data, always_success_script) = always_success_cell(); + let (lock_script_cell, lock_script_data, lock_script) = lock_script_cell(); + + let deploy_always_success_tx = TransactionView::new_advanced_builder() + .input(packed::CellInput::new_cellbase_input(0)) + .output(always_success_cell.clone()) + .output_data(always_success_data.pack()) + .witness(always_success_script.clone().into_witness()) + .build(); + let always_success_cell_dep = { + let always_success_cell_op = packed::OutPoint::new(deploy_always_success_tx.hash(), 0); + packed::CellDep::new_builder() + .out_point(always_success_cell_op) + .dep_type(DepType::Code.into()) + .build() + }; + let deploy_lock_script_tx = TransactionView::new_advanced_builder() + .cell_dep(always_success_cell_dep) + .input(packed::CellInput::new_cellbase_input(0)) + .output(lock_script_cell.clone()) + .output_data(lock_script_data.pack()) + .witness(lock_script.clone().into_witness()) + .build(); + let lock_script_cell_dep = { + let lock_script_cell_op = packed::OutPoint::new(deploy_lock_script_tx.hash(), 0); + packed::CellDep::new_builder() + .out_point(lock_script_cell_op) + .dep_type(DepType::Code.into()) + .build() + }; + let lock_script_via_type = { + let type_hash = always_success_script.calc_script_hash(); + packed::Script::new_builder() + .code_hash(type_hash) + .hash_type(ScriptHashType::Type.into()) + .build() + }; + let input_tx = TransactionView::new_advanced_builder() + .cell_dep(lock_script_cell_dep.clone()) + .input(packed::CellInput::new_cellbase_input(0)) + .output( + packed::CellOutput::new_builder() + .capacity(capacity_bytes!(1_000_000).pack()) + .lock(lock_script_via_type.clone()) + .build(), + ) + .output_data(Default::default()) + .witness(Default::default()) + .build(); + + let dao = genesis_dao_data(vec![&deploy_always_success_tx, &deploy_lock_script_tx]).unwrap(); + let genesis = packed::Block::new_advanced_builder() + .timestamp(unix_time_as_millis().pack()) + .compact_target(difficulty_to_compact(U256::from(100u64)).pack()) + .dao(dao) + .transaction(deploy_always_success_tx) + .transaction(deploy_lock_script_tx) + .transaction(input_tx.clone()) + .build(); + + let hardfork_switch = HardForkSwitch::new_without_any_enabled() + .as_builder() + .rfc_0032(epoch_when_enable_vm1) + .build() + .unwrap(); + let mut consensus = Consensus { + permanent_difficulty_in_dummy: true, + hardfork_switch, + genesis_block: genesis, + cellbase_maturity: EpochNumberWithFraction::new(0, 0, 1), + ..Default::default() + }; + consensus.genesis_epoch_ext.set_length(epoch_length); + + ( + consensus, + vec![lock_script_cell_dep], + lock_script_via_type, + input_tx, + ) +} + +fn start_chain(consensus: Consensus, lock_script: &packed::Script) -> (ChainController, Shared) { + let hash_type: ScriptHashType = lock_script.hash_type().try_into().unwrap(); + let config = BlockAssemblerConfig { + code_hash: lock_script.code_hash().unpack(), + args: Default::default(), + hash_type: hash_type.into(), + message: Default::default(), + use_binary_version_as_message_prefix: true, + binary_version: "TEST".to_string(), + }; + let (shared, mut pack) = SharedBuilder::with_temp_db() + .consensus(consensus) + .block_assembler_config(Some(config)) + .build() + .unwrap(); + + let network = dummy_network(&shared); + pack.take_tx_pool_builder().start(network); + + let chain_service = ChainService::new(shared.clone(), pack.take_proposal_table()); + let chain_controller = chain_service.start::<&str>(None); + (chain_controller, shared) +} + +fn create_cellbase( + number: BlockNumber, + epoch: &EpochExt, + lock_script: &packed::Script, +) -> TransactionView { + TransactionView::new_advanced_builder() + .input(packed::CellInput::new_cellbase_input(number)) + .output( + packed::CellOutput::new_builder() + .capacity(epoch.block_reward(number).unwrap().pack()) + .lock(lock_script.clone()) + .build(), + ) + .output_data(Default::default()) + .witness(Default::default()) + .build() +} + +fn generate_block( + parent_header: &HeaderView, + nonce: u128, + epoch: &EpochExt, + lock_script: &packed::Script, +) -> BlockView { + let number = parent_header.number() + 1; + let cellbase = create_cellbase(number, epoch, lock_script); + let dao = genesis_dao_data(vec![&cellbase]).unwrap(); + let header = HeaderView::new_advanced_builder() + .parent_hash(parent_header.hash()) + .timestamp((parent_header.timestamp() + 10).pack()) + .number(number.pack()) + .epoch(epoch.number_with_fraction(number).pack()) + .compact_target(epoch.compact_target().pack()) + .nonce(nonce.pack()) + .dao(dao) + .build(); + packed::Block::new_advanced_builder() + .header(header) + .transaction(cellbase) + .build_unchecked() +} + +fn build_tx( + parent_tx: &TransactionView, + cell_deps: &[packed::CellDep], + lock_script: &packed::Script, +) -> TransactionView { + let input_op = packed::OutPoint::new(parent_tx.hash(), 0); + let input = packed::CellInput::new(input_op, 0); + let output_capacity = Capacity::shannons(parent_tx.output(0).unwrap().capacity().unpack()) + .safe_sub(Capacity::bytes(1).unwrap()) + .unwrap(); + let output = packed::CellOutput::new_builder() + .capacity(output_capacity.pack()) + .lock(lock_script.clone()) + .build(); + let mut tx_builder = TransactionView::new_advanced_builder(); + for cell_dep in cell_deps { + tx_builder = tx_builder.cell_dep(cell_dep.clone()); + } + tx_builder + .input(input) + .output(output) + .output_data(Default::default()) + .build() +} + +#[test] +fn refresh_txs_verify_cache_after_hardfork() { + let (consensus, cell_deps, lock_script, input_tx) = create_consensus(); + let epoch_when_enable_vm1 = consensus.hardfork_switch.vm_version_1_and_syscalls_2(); + + let (chain_controller, shared) = start_chain(consensus, &lock_script); + + // set to genesis header + let mut parent_header = shared + .store() + .get_block_header(&shared.store().get_block_hash(0).unwrap()) + .unwrap(); + + let tx_pool = shared.tx_pool_controller(); + + let tx = build_tx(&input_tx, &cell_deps, &lock_script); + tx_pool.submit_local_tx(tx.clone()).unwrap().unwrap(); + + // at start of the test, the script should be ran with vm0 + { + let raw_tx_pool = tx_pool.get_all_entry_info().unwrap(); + let mut counter = 0; + loop { + if let Some(tx_entry) = raw_tx_pool.pending.get(&tx.hash()) { + assert_eq!(tx_entry.cycles, CYCLES_IN_VM0); + break; + } + // wait tx_pool if got `None` + counter += 1; + if counter > 100 { + panic!("tx-pool is too slow to refresh caches"); + } else { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + } + + for _ in 0..35 { + let epoch = shared + .consensus() + .next_epoch_ext(&parent_header, &shared.store().as_data_provider()) + .unwrap() + .epoch(); + let block = generate_block(&parent_header, 0, &epoch, &lock_script); + + let cycles_expected = if epoch.number() >= epoch_when_enable_vm1 { + CYCLES_IN_VM1 + } else { + CYCLES_IN_VM0 + }; + let mut counter = 0; + loop { + let raw_tx_pool = tx_pool.get_all_entry_info().unwrap(); + if let Some(tx_entry) = raw_tx_pool.pending.get(&tx.hash()) { + assert_eq!( + tx_entry.cycles, + cycles_expected, + "block = {}, epoch = {}, cycles should be {}, but got {}", + block.number(), + epoch.number(), + cycles_expected, + tx_entry.cycles, + ); + break; + } + // wait tx_pool if got `None` + counter += 1; + if counter > 100 { + panic!("tx-pool is too slow to refresh caches"); + } else { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + + chain_controller + .internal_process_block(Arc::new(block.clone()), Switch::ONLY_SCRIPT) + .expect("process block"); + parent_header = block.header().to_owned(); + } + + // at last of the test, the script should be ran with vm1 + { + let raw_tx_pool = tx_pool.get_all_entry_info().unwrap(); + let tx_entry = raw_tx_pool.pending.get(&tx.hash()).unwrap(); + assert_eq!(tx_entry.cycles, CYCLES_IN_VM1); + } +} diff --git a/script/src/ill_transaction_checker.rs b/script/src/ill_transaction_checker.rs index 0805e91c30..1d9ca71c67 100644 --- a/script/src/ill_transaction_checker.rs +++ b/script/src/ill_transaction_checker.rs @@ -34,7 +34,7 @@ impl<'a> IllTransactionChecker<'a> { /// Checks whether the transaction is ill formed. pub fn check(&self) -> Result<(), ScriptError> { - let epoch_number = self.tx_env.current_epoch_number(); + let epoch_number = self.tx_env.epoch_number_without_proposal_window(); let hardfork_switch = self.consensus.hardfork_switch(); // Assume that after ckb2021 is activated, developers will only upload code for vm v1. if !hardfork_switch.is_vm_version_1_and_syscalls_2_enabled(epoch_number) { diff --git a/script/src/verify.rs b/script/src/verify.rs index f9fe239c05..ce65bd18ae 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -403,7 +403,7 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D // it will cause proposal tx to start a new vm in the blocks before hardfork, // destroying the assumption that the transaction execution only uses the old vm // before hardfork, leading to unexpected network splits. - let epoch_number = self.tx_env.current_epoch_number(); + let epoch_number = self.tx_env.epoch_number_without_proposal_window(); let hardfork_switch = self.consensus.hardfork_switch(); let is_vm_version_1_and_syscalls_2_enabled = hardfork_switch.is_vm_version_1_and_syscalls_2_enabled(epoch_number); diff --git a/script/src/verify/tests/ckb_latest/features_since_v2021.rs b/script/src/verify/tests/ckb_latest/features_since_v2021.rs index 3c86b272d6..6fb29af402 100644 --- a/script/src/verify/tests/ckb_latest/features_since_v2021.rs +++ b/script/src/verify/tests/ckb_latest/features_since_v2021.rs @@ -73,6 +73,60 @@ fn test_b_extension() { } } +#[test] +fn test_cycles_difference() { + let script_version = SCRIPT_VERSION; + + let always_success_cell_data = Bytes::from( + std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/mop_adc_lock")).unwrap(), + ); + let always_success_cell = CellOutput::new_builder() + .capacity( + Capacity::bytes(always_success_cell_data.len()) + .unwrap() + .pack(), + ) + .build(); + let always_success_script = Script::new_builder() + .hash_type(script_version.data_hash_type().into()) + .code_hash(CellOutput::calc_data_hash(&always_success_cell_data)) + .build(); + + let output = CellOutputBuilder::default() + .capacity(capacity_bytes!(100).pack()) + .lock(always_success_script) + .build(); + let input = CellInput::new(OutPoint::null(), 0); + + let transaction = TransactionBuilder::default().input(input).build(); + + let dummy_cell = CellMetaBuilder::from_cell_output(output, Bytes::new()) + .transaction_info(default_transaction_info()) + .build(); + let always_success_cell = + CellMetaBuilder::from_cell_output(always_success_cell, always_success_cell_data) + .transaction_info(default_transaction_info()) + .build(); + + let rtx = ResolvedTransaction { + transaction, + resolved_cell_deps: vec![always_success_cell], + resolved_inputs: vec![dummy_cell], + resolved_dep_groups: vec![], + }; + + let verifier = TransactionScriptsVerifierWithEnv::new(); + let result = verifier.verify_without_limit(script_version, &rtx); + assert!(result.is_ok()); + let cycles_actual = result.unwrap(); + let cycles_expected = if script_version >= ScriptVersion::V1 { + 686 + } else { + 696 + }; + assert_eq!(cycles_actual, cycles_expected); +} + #[test] fn check_vm_version() { let script_version = SCRIPT_VERSION; diff --git a/script/src/verify_env.rs b/script/src/verify_env.rs index 937b794e22..600eaa7dbb 100644 --- a/script/src/verify_env.rs +++ b/script/src/verify_env.rs @@ -118,8 +118,13 @@ impl TxVerifyEnv { self.epoch } - /// Current epoch number without proposal window - pub fn current_epoch_number(&self) -> EpochNumber { - self.epoch.number() + /// The epoch number of the earliest epoch which the transaction will committed in without + /// consider about the proposal window. + pub fn epoch_number_without_proposal_window(&self) -> EpochNumber { + let n_blocks = match self.phase { + TxVerifyPhase::Submitted | TxVerifyPhase::Proposed(_) => 1, + TxVerifyPhase::Committed => 0, + }; + self.epoch.minimum_epoch_number_after_n_blocks(n_blocks) } } diff --git a/script/testdata/mop_adc_lock b/script/testdata/mop_adc_lock new file mode 100755 index 0000000000..d24fad349c Binary files /dev/null and b/script/testdata/mop_adc_lock differ diff --git a/script/testdata/mop_adc_lock.S b/script/testdata/mop_adc_lock.S new file mode 100644 index 0000000000..40a8efa3f2 --- /dev/null +++ b/script/testdata/mop_adc_lock.S @@ -0,0 +1,65 @@ +/* Script Description: + * - No Argsj + * - Returns CKB_SUCCESS always. + * - Copy from https://github.com/nervosnetwork/ckb-vm/blob/edb5b83084c3b3b978e26298151ac72a10769fd6/tests/programs/mop_adc.S. + * Compile Environment: + * - Toolchains: nervos/ckb-riscv-gnu-toolchain@sha256:7b168b4b109a0f741078a71b7c4dddaf1d283a5244608f7851f5714fbad273ba + * - Commands: + * ```shell + * riscv64-unknown-elf-as -o mop_adc_lock.o mop_adc_lock.S + * riscv64-unknown-elf-ld -o mop_adc_lock mop_adc_lock.o + * riscv64-unknown-elf-objcopy --strip-debug --strip-all mop_adc_lock + * ``` + */ + +.global _start +_start: + li a0, 0x7fffffffffffffff + li a1, 1 + li a2, 0x8000000000000000 + add a0, a0, a1 + sltu a1, a0, a1 + add a0, a0, a2 + sltu a2, a0, a2 + or a1, a1, a2 + li t0, 0 + bne a0, t0, fail + li t0, 1 + bne a1, t0, fail + li t0, 1 + bne a2, t0, fail + + li a0, 0x390a5a5fad56f578 + li a1, 0xdd8f0883fdd78883 + li a2, 0x7f6dbfa760006b9e + add a0, a0, a1 + sltu a1, a0, a1 + add a0, a0, a2 + sltu a2, a0, a2 + or a1, a1, a2 + li t0, 0x9607228b0b2ee999 + bne a0, t0, fail + li t0, 1 + bne a1, t0, fail + li t0, 0 + bne a2, t0, fail + + li a1, 1 + li a2, 1 + add zero, zero, a1 + sltu a1, zero, a1 + add zero, zero, a2 + sltu a2, zero, a2 + or a1, a1, a2 + li t0, 1 + bne a1, t0, fail + li t0, 1 + bne a2, t0, fail + + li a0, 0 + li a7, 93 + ecall +fail: + li a0, 1 + li a7, 93 + ecall diff --git a/tx-pool/src/process.rs b/tx-pool/src/process.rs index d03ee4b2b0..6e1a850b1b 100644 --- a/tx-pool/src/process.rs +++ b/tx-pool/src/process.rs @@ -681,7 +681,10 @@ impl TxPoolService { // The network protocol is switched after tx-pool confirms the cache, // there will be no problem with the current state as the choice of the broadcast protocol. let with_vm_2021 = { - let epoch = snapshot.tip_header().epoch().number(); + let epoch = snapshot + .tip_header() + .epoch() + .minimum_epoch_number_after_n_blocks(1); self.consensus .hardfork_switch .is_vm_version_1_and_syscalls_2_enabled(epoch) @@ -765,7 +768,10 @@ impl TxPoolService { ._process_tx(orphan.tx.clone(), Some(orphan.cycle)) .await; let with_vm_2021 = { - let epoch = snapshot.tip_header().epoch().number(); + let epoch = snapshot + .tip_header() + .epoch() + .minimum_epoch_number_after_n_blocks(1); self.consensus .hardfork_switch .is_vm_version_1_and_syscalls_2_enabled(epoch) @@ -1006,7 +1012,10 @@ impl TxPoolService { check_if_hardfork_during_blocks(&hardfork_switch, &detached_blocks); let hardfork_during_attach = check_if_hardfork_during_blocks(&hardfork_switch, &attached_blocks); - let epoch = snapshot.tip_header().epoch().number(); + let epoch_of_next_block = snapshot + .tip_header() + .epoch() + .minimum_epoch_number_after_n_blocks(1); for blk in detached_blocks { detached.extend(blk.transactions().into_iter().skip(1)) @@ -1018,12 +1027,17 @@ impl TxPoolService { let retain: Vec = detached.difference(&attached).cloned().collect(); let fetched_cache = if hardfork_during_detach || hardfork_during_attach { + // If the hardfork was happened, don't use the cache. HashMap::new() } else { self.fetch_txs_verify_cache(retain.iter()).await }; { + // If there are any transactions requires re-process, return them. + // + // At present, there is only one situation: + // - If the hardfork was happened, then re-process all transactions. let txs_opt = { // This closure is used to limit the lifetime of mutable tx_pool. let mut tx_pool = self.tx_pool.write().await; @@ -1049,6 +1063,20 @@ impl TxPoolService { snapshot, &self.callbacks, ); + + // Updates network fork switch if required. + // + // This operation should be ahead of any transaction which is processsed with new + // hardfork features. + if !self.network.load_ckb2021() + && self + .consensus + .hardfork_switch + .is_vm_version_1_and_syscalls_2_enabled(epoch_of_next_block) + { + self.network.init_ckb2021() + } + self.readd_dettached_tx(&mut tx_pool, retain, fetched_cache); txs_opt @@ -1068,18 +1096,6 @@ impl TxPoolService { let mut chunk = self.chunk.write().await; chunk.remove_chunk_txs(attached.iter().map(|tx| tx.proposal_short_id())); } - - // update network fork switch each block - { - if !self.network.load_ckb2021() - && self - .consensus - .hardfork_switch - .is_vm_version_1_and_syscalls_2_enabled(epoch) - { - self.network.init_ckb2021() - } - } } fn readd_dettached_tx( diff --git a/util/test-chain-utils/src/mock_store.rs b/util/test-chain-utils/src/mock_store.rs index 9f94bd3998..e12bb8765b 100644 --- a/util/test-chain-utils/src/mock_store.rs +++ b/util/test-chain-utils/src/mock_store.rs @@ -5,11 +5,12 @@ use ckb_types::core::error::OutPointError; use ckb_types::{ core::{ cell::{CellMetaBuilder, CellProvider, CellStatus, HeaderChecker}, - BlockView, EpochExt, HeaderView, + BlockExt, BlockView, EpochExt, HeaderView, }, packed::{Byte32, OutPoint}, prelude::*, }; +use faketime::unix_time_as_millis; use std::sync::Arc; /// A temporary RocksDB for mocking chain storage. @@ -33,7 +34,21 @@ impl MockStore { .get_block_epoch_index(&parent.hash()) .and_then(|index| chain_store.get_epoch_ext(&index)) .unwrap(); + let parent_block_ext = BlockExt { + received_at: unix_time_as_millis(), + total_difficulty: Default::default(), + total_uncles_count: 0, + verified: Some(true), + txs_fees: vec![], + }; let store = Self::default(); + { + let db_txn = store.0.begin_transaction(); + db_txn + .insert_block_ext(&block.parent_hash(), &parent_block_ext) + .unwrap(); + db_txn.commit().unwrap(); + } store.insert_block(&block, &epoch_ext); store } @@ -57,6 +72,19 @@ impl MockStore { db_txn .insert_epoch_ext(&last_block_hash_in_previous_epoch, epoch_ext) .unwrap(); + { + let parent_block_ext = self.0.get_block_ext(&block.parent_hash()).unwrap(); + let block_ext = BlockExt { + received_at: unix_time_as_millis(), + total_difficulty: parent_block_ext.total_difficulty.to_owned() + + block.header().difficulty(), + total_uncles_count: parent_block_ext.total_uncles_count + + block.data().uncles().len() as u64, + verified: Some(true), + txs_fees: vec![], + }; + db_txn.insert_block_ext(&block.hash(), &block_ext).unwrap(); + } db_txn.commit().unwrap(); } diff --git a/verification/traits/src/lib.rs b/verification/traits/src/lib.rs index 259609179f..d8f9f43481 100644 --- a/verification/traits/src/lib.rs +++ b/verification/traits/src/lib.rs @@ -42,6 +42,9 @@ bitflags! { Self::DISABLE_TWO_PHASE_COMMIT.bits | Self::DISABLE_DAOHEADER.bits | Self::DISABLE_REWARD.bits | Self::DISABLE_NON_CONTEXTUAL.bits | Self::DISABLE_SCRIPT.bits; + + /// Only script verification + const ONLY_SCRIPT = Self::DISABLE_ALL.bits & (!Self::DISABLE_SCRIPT.bits); } }