diff --git a/tests/src/script_tests/custodian_lock/finalize_based_on_block_number.rs b/tests/src/script_tests/custodian_lock/finalize_based_on_block_number.rs new file mode 100644 index 00000000..70845a23 --- /dev/null +++ b/tests/src/script_tests/custodian_lock/finalize_based_on_block_number.rs @@ -0,0 +1,108 @@ +use super::utils::{build_context, ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER}; +use crate::script_tests::utils::init_env_log; +use crate::script_tests::utils::layer1::{build_simple_tx_with_out_point, random_out_point}; +use ckb_error::assert_error_eq; +use ckb_script::ScriptError; +use ckb_types::{ + core::{Cycle, ScriptHashType}, + packed::{CellDep, CellInput, CellOutput, Script}, + prelude::*, +}; +use gw_types::packed::CustodianLockArgs; + +// Transaction structure: +// +// ``` +// CellDeps: +// rollup_config_cell: +// rollup_code_cell: +// custodian_code_cell: +// +// Inputs: +// rollup_state_cell: +// custodian_state_cell: +// +// Outputs: +// : +// ``` + +// deposit_block_number <= rollup_state_cell.last_finalized_block_number +#[test] +fn test_success_to_unlock_custodian_via_finalize_based_on_block_number() { + init_env_log(); + let (_, result) = unlock_custodian_via_finalize_based_on_block_number( + ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER, + ); + result.expect("success"); +} + +// deposit_block_number > rollup_state_cell.last_finalized_block_number +#[test] +fn test_fail_to_unlock_custodian_via_finalize_based_on_block_number() { + init_env_log(); + let (custodian_state_cell, result) = unlock_custodian_via_finalize_based_on_block_number( + ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER + 1, + ); + let custodian_code_type_hash: ckb_types::H256 = + custodian_state_cell.lock().code_hash().unpack(); + let expected_err = ScriptError::ValidationFailure( + format!("by-type-hash/{}", custodian_code_type_hash), + 1, // Error::IndexOutOfBound + ) + .input_lock_script(0); + assert_error_eq!(expected_err, result.unwrap_err()); +} + +fn unlock_custodian_via_finalize_based_on_block_number( + deposit_block_number: u64, +) -> (CellOutput, Result) { + let ( + mut ctx, + rollup_state_out_point, + rollup_state_type_script, + custodian_code_out_point, + custodian_code_type_script, + ) = build_context(); + let rollup_state_type_hash = rollup_state_type_script.calc_script_hash(); + let custodian_code_type_hash = custodian_code_type_script.calc_script_hash(); + + // Build a finalized custodian_state_cell + let custodian_state_out_point = random_out_point(); + let custodian_state_cell = CellOutput::new_builder() + .lock( + Script::new_builder() + .code_hash(custodian_code_type_hash.clone()) + .hash_type(ScriptHashType::Type.into()) + .args({ + let custodian_lock_args = CustodianLockArgs::new_builder() + .deposit_block_number(gw_types::prelude::Pack::pack(&deposit_block_number)) + .build(); + let mut args = Vec::new(); + args.extend_from_slice(&rollup_state_type_hash.as_slice()); + args.extend_from_slice(&custodian_lock_args.as_slice()); + args.pack() + }) + .build(), + ) + .build(); + + // Build transaction + let tx = build_simple_tx_with_out_point( + &mut ctx.inner, + (custodian_state_cell.clone(), Default::default()), + custodian_state_out_point, + (CellOutput::new_builder().build(), Default::default()), + ) + .as_advanced_builder() + .input(CellInput::new(rollup_state_out_point, 0)) + .cell_dep( + CellDep::new_builder() + .out_point(custodian_code_out_point) + .build(), + ) + .cell_dep(ctx.rollup_config_dep.clone()) + .cell_dep(ctx.always_success_dep.clone()) + .build(); + let result = ctx.verify_tx(tx); + (custodian_state_cell, result) +} diff --git a/tests/src/script_tests/custodian_lock/finalize_based_on_timestamp.rs b/tests/src/script_tests/custodian_lock/finalize_based_on_timestamp.rs new file mode 100644 index 00000000..4177b5f8 --- /dev/null +++ b/tests/src/script_tests/custodian_lock/finalize_based_on_timestamp.rs @@ -0,0 +1,132 @@ +use super::utils::{ + build_context, ROLLUP_CONFIG_FINALITY_DURATION_MS, ROLLUP_STATE_CELL_TIMESTAMP, +}; +use crate::script_tests::utils::init_env_log; +use crate::script_tests::utils::layer1::{build_simple_tx_with_out_point, random_out_point}; +use ckb_error::assert_error_eq; +use ckb_script::ScriptError; +use ckb_types::{ + core::{Cycle, HeaderBuilder, ScriptHashType, TransactionInfo}, + packed::{CellDep, CellInput, CellOutput, Script}, + prelude::*, +}; +use gw_types::packed::CustodianLockArgs; + +// Transaction structure: +// +// ``` +// HeaderDeps: +// +// +// CellDeps: +// rollup_config_cell: +// rollup_code_cell: +// custodian_code_cell: +// +// Inputs: +// rollup_state_cell: +// custodian_state_cell: +// +// Outputs: +// : +// ``` + +// deposit_block_timestamp <= rollup_state_cell's L1 timestamp +#[test] +fn test_success_to_unlock_custodian_via_finalize_based_on_timestamp() { + init_env_log(); + let (_, result) = unlock_custodian_via_finalize_based_on_timestamp( + ROLLUP_STATE_CELL_TIMESTAMP - ROLLUP_CONFIG_FINALITY_DURATION_MS, + ); + result.expect("success"); +} + +// deposit_block_timestamp > rollup_state_cell's header timestamp +#[test] +fn test_fail_to_unlock_custodian_via_finalize_based_on_timestamp() { + init_env_log(); + let (custodian_state_cell, result) = unlock_custodian_via_finalize_based_on_timestamp( + ROLLUP_STATE_CELL_TIMESTAMP - ROLLUP_CONFIG_FINALITY_DURATION_MS + 1, + ); + let custodian_code_type_hash: ckb_types::H256 = + custodian_state_cell.lock().code_hash().unpack(); + let expected_err = ScriptError::ValidationFailure( + format!("by-type-hash/{}", custodian_code_type_hash), + 1, // Error::IndexOutOfBound + ) + .input_lock_script(0); + assert_error_eq!(expected_err, result.unwrap_err()); +} + +fn unlock_custodian_via_finalize_based_on_timestamp( + deposit_block_timestamp: u64, +) -> (CellOutput, Result) { + let ( + mut ctx, + rollup_state_out_point, + rollup_state_type_script, + custodian_code_out_point, + custodian_code_type_script, + ) = build_context(); + let rollup_state_type_hash = rollup_state_type_script.calc_script_hash(); + let custodian_code_type_hash = custodian_code_type_script.calc_script_hash(); + + // Build rollup_state_cell's header_dep + let rollup_state_header = HeaderBuilder::default() + .timestamp(ROLLUP_STATE_CELL_TIMESTAMP.pack()) + .build(); + let rollup_state_tx_info = TransactionInfo { + block_hash: rollup_state_header.hash(), + block_number: rollup_state_header.number(), + block_epoch: rollup_state_header.epoch(), + index: 1, + }; + ctx.inner + .transaction_infos + .insert(rollup_state_out_point.clone(), rollup_state_tx_info); + ctx.inner + .headers + .insert(rollup_state_header.hash(), rollup_state_header.clone()); + + // Build a finalized custodian_state_cell + let custodian_state_out_point = random_out_point(); + let custodian_state_cell = CellOutput::new_builder() + .lock( + Script::new_builder() + .code_hash(custodian_code_type_hash.clone()) + .hash_type(ScriptHashType::Type.into()) + .args({ + let timepoint = deposit_block_timestamp | (1 << 63); + let custodian_lock_args = CustodianLockArgs::new_builder() + .deposit_block_number(gw_types::prelude::Pack::pack(&timepoint)) + .build(); + let mut args = Vec::new(); + args.extend_from_slice(&rollup_state_type_hash.as_slice()); + args.extend_from_slice(&custodian_lock_args.as_slice()); + args.pack() + }) + .build(), + ) + .build(); + + // Build transaction + let tx = build_simple_tx_with_out_point( + &mut ctx.inner, + (custodian_state_cell.clone(), Default::default()), + custodian_state_out_point, + (CellOutput::new_builder().build(), Default::default()), + ) + .as_advanced_builder() + .input(CellInput::new(rollup_state_out_point, 0)) + .cell_dep( + CellDep::new_builder() + .out_point(custodian_code_out_point) + .build(), + ) + .cell_dep(ctx.rollup_config_dep.clone()) + .cell_dep(ctx.always_success_dep.clone()) + .header_dep(rollup_state_header.hash()) + .build(); + + (custodian_state_cell, ctx.verify_tx(tx)) +} diff --git a/tests/src/script_tests/custodian_lock/mod.rs b/tests/src/script_tests/custodian_lock/mod.rs new file mode 100644 index 00000000..16ee4456 --- /dev/null +++ b/tests/src/script_tests/custodian_lock/mod.rs @@ -0,0 +1,3 @@ +pub mod finalize_based_on_block_number; +pub mod finalize_based_on_timestamp; +pub mod utils; diff --git a/tests/src/script_tests/custodian_lock/utils.rs b/tests/src/script_tests/custodian_lock/utils.rs new file mode 100644 index 00000000..cfacb54f --- /dev/null +++ b/tests/src/script_tests/custodian_lock/utils.rs @@ -0,0 +1,71 @@ +use crate::script_tests::utils::rollup::{random_always_success_script, CellContext}; +use crate::testing_tool::programs::CUSTODIAN_LOCK_PROGRAM; +use ckb_types::{ + packed::{CellOutput, OutPoint, Script}, + prelude::*, +}; +use gw_types::packed::{GlobalState, RollupConfig}; + +const BLOCK_INTERVAL_IN_MILLISECONDS: u64 = 36000; +pub(super) const ROLLUP_CONFIG_FINALITY_BLOCKS: u64 = 1; +pub(super) const ROLLUP_CONFIG_FINALITY_DURATION_MS: u64 = + ROLLUP_CONFIG_FINALITY_BLOCKS * BLOCK_INTERVAL_IN_MILLISECONDS; +pub(super) const ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER: u64 = 100; +pub(super) const ROLLUP_STATE_CELL_TIMESTAMP: u64 = 1555204979310; + +// Build common-used cells for testing custodian-lock: +// - rollup_config_cell, finality_blocks = ROLLUP_CONFIG_FINALITY_BLOCKS +// - rollup_code_cell, is ALWAYS_SUCCESS_PROGRAM +// - rollup_state_cell, last_finalized_block_number = ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER +// - custodian_code_cell, is CUSTODIAN_LOCK_PROGRAM +// +// Return (ctx, rollup_state_out_point, rollup_state_type_script, custodian_code_out_point, custodian_code_type_script); +pub(super) fn build_context() -> (CellContext, OutPoint, Script, OutPoint, Script) { + let rollup_config = RollupConfig::new_builder() + .finality_blocks(gw_types::prelude::Pack::pack( + &ROLLUP_CONFIG_FINALITY_BLOCKS, + )) + .build(); + let mut ctx = CellContext::new(&rollup_config, Default::default()); + + // Build a always-success rollup_state_cell, because we are testing + // custodian-lock only + let rollup_state_data = GlobalState::new_builder() + .last_finalized_block_number(gw_types::prelude::Pack::pack( + &ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER, + )) + .rollup_config_hash({ + let rollup_config_data_hash = CellOutput::calc_data_hash(&rollup_config.as_bytes()); + gw_types::packed::Byte32::new_unchecked(rollup_config_data_hash.as_bytes()) + }) + .build(); + let rollup_state_cell = CellOutput::new_builder() + .lock(random_always_success_script()) + .type_(Some(random_always_success_script()).pack()) + .build(); + let rollup_state_type_script = rollup_state_cell + .type_() + .to_opt() + .expect("should be always-success"); + let rollup_state_out_point = ctx.insert_cell(rollup_state_cell, rollup_state_data.as_bytes()); + + // Build costodian_code_cell + let custodian_code_data = CUSTODIAN_LOCK_PROGRAM.clone(); + let custodian_code_cell = CellOutput::new_builder() + .lock(random_always_success_script()) + .type_(Some(random_always_success_script()).pack()) + .build(); + let custodian_code_type_script = custodian_code_cell + .type_() + .to_opt() + .expect("should be always-success"); + let custodian_code_out_point = ctx.insert_cell(custodian_code_cell, custodian_code_data); + + return ( + ctx, + rollup_state_out_point, + rollup_state_type_script, + custodian_code_out_point, + custodian_code_type_script, + ); +} diff --git a/tests/src/script_tests/mod.rs b/tests/src/script_tests/mod.rs index ffb4577a..10c23901 100644 --- a/tests/src/script_tests/mod.rs +++ b/tests/src/script_tests/mod.rs @@ -1,6 +1,8 @@ mod account_lock_scripts; +mod custodian_lock; mod l2_scripts; mod l2_scripts_validator; +mod stake_lock; mod state_validator; pub mod utils; mod withdrawal; diff --git a/tests/src/script_tests/stake_lock/finalize_based_on_block_number.rs b/tests/src/script_tests/stake_lock/finalize_based_on_block_number.rs new file mode 100644 index 00000000..8c73349d --- /dev/null +++ b/tests/src/script_tests/stake_lock/finalize_based_on_block_number.rs @@ -0,0 +1,125 @@ +use super::utils::{build_context, ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER}; +use crate::script_tests::utils::init_env_log; +use crate::script_tests::utils::layer1::{build_simple_tx_with_out_point, random_out_point}; +use crate::script_tests::utils::rollup::random_always_success_script; +use ckb_error::assert_error_eq; +use ckb_script::ScriptError; +use ckb_types::{ + core::{Cycle, ScriptHashType}, + packed::{CellDep, CellInput, CellOutput, Script}, + prelude::*, +}; +use gw_types::packed::StakeLockArgs; + +// Transaction structure: +// +// ``` +// CellDeps: +// rollup_config_cell: +// rollup_code_cell: +// stake_code_cell: +// rollup_state_cell: +// +// Inputs: +// stake_state_cell: +// stake_owner_cell: +// Lock: +// +// Outputs: +// +// ``` + +// stake_block_number <= rollup_state_cell.last_finalized_block_number +#[test] +fn test_success_to_unlock_stake_via_finalize_based_on_block_number() { + init_env_log(); + let (_, result) = + unlock_stake_via_finalize_based_on_block_number(ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER); + result.expect("success"); +} + +// stake_block_number > rollup_state_cell.last_finalized_block_number +#[test] +fn test_fail_to_unlock_stake_via_finalize_based_on_block_number() { + init_env_log(); + let (stake_state_cell, result) = unlock_stake_via_finalize_based_on_block_number( + ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER + 1, + ); + let stake_code_type_hash: ckb_types::H256 = stake_state_cell.lock().code_hash().unpack(); + let expected_err = ScriptError::ValidationFailure( + format!("by-type-hash/{}", stake_code_type_hash), + 45, // Error::NotFinalized + ) + .input_lock_script(0); + assert_error_eq!(expected_err, result.unwrap_err()); +} + +fn unlock_stake_via_finalize_based_on_block_number( + stake_block_number: u64, +) -> (CellOutput, Result) { + let ( + mut ctx, + rollup_state_out_point, + rollup_state_type_script, + stake_code_out_point, + stake_code_type_script, + ) = build_context(); + let rollup_state_type_hash = rollup_state_type_script.calc_script_hash(); + let stake_code_type_hash = stake_code_type_script.calc_script_hash(); + + // Build a finalized stake_state_cell + let stake_state_out_point = random_out_point(); + let stake_owner_lock_script = random_always_success_script(); + let stake_owner_lock_hash = stake_owner_lock_script.calc_script_hash(); + let stake_state_cell = CellOutput::new_builder() + .lock( + Script::new_builder() + .code_hash(stake_code_type_hash.clone()) + .hash_type(ScriptHashType::Type.into()) + .args({ + let stake_lock_args = StakeLockArgs::new_builder() + .stake_block_number(gw_types::prelude::Pack::pack(&stake_block_number)) + .owner_lock_hash(gw_types::packed::Byte32::new_unchecked( + stake_owner_lock_hash.as_bytes(), + )) + .build(); + let mut args = Vec::new(); + args.extend_from_slice(&rollup_state_type_hash.as_slice()); + args.extend_from_slice(&stake_lock_args.as_slice()); + args.pack() + }) + .build(), + ) + .build(); + + // Build stake_owner_cell + let stake_owner_cell = CellOutput::new_builder() + .lock(stake_owner_lock_script) + .build(); + let stake_owner_out_point = ctx.insert_cell(stake_owner_cell, Default::default()); + + // Build transaction + let tx = build_simple_tx_with_out_point( + &mut ctx.inner, + (stake_state_cell.clone(), Default::default()), + stake_state_out_point, + (CellOutput::new_builder().build(), Default::default()), + ) + .as_advanced_builder() + .input(CellInput::new(stake_owner_out_point, 0)) + .cell_dep( + CellDep::new_builder() + .out_point(rollup_state_out_point) + .build(), + ) + .cell_dep( + CellDep::new_builder() + .out_point(stake_code_out_point) + .build(), + ) + .cell_dep(ctx.rollup_config_dep.clone()) + .cell_dep(ctx.always_success_dep.clone()) + .build(); + let result = ctx.verify_tx(tx); + (stake_state_cell, result) +} diff --git a/tests/src/script_tests/stake_lock/finalize_based_on_timestamp.rs b/tests/src/script_tests/stake_lock/finalize_based_on_timestamp.rs new file mode 100644 index 00000000..458af270 --- /dev/null +++ b/tests/src/script_tests/stake_lock/finalize_based_on_timestamp.rs @@ -0,0 +1,150 @@ +use super::utils::{ + build_context, ROLLUP_CONFIG_FINALITY_DURATION_MS, ROLLUP_STATE_CELL_TIMESTAMP, +}; +use crate::script_tests::utils::init_env_log; +use crate::script_tests::utils::layer1::{build_simple_tx_with_out_point, random_out_point}; +use crate::script_tests::utils::rollup::random_always_success_script; +use ckb_error::assert_error_eq; +use ckb_script::ScriptError; +use ckb_types::{ + core::{Cycle, HeaderBuilder, ScriptHashType, TransactionInfo}, + packed::{CellDep, CellInput, CellOutput, Script}, + prelude::*, +}; +use gw_types::packed::StakeLockArgs; + +// Transaction structure: +// +// ``` +// HeaderDeps: +// +// +// CellDeps: +// rollup_config_cell: +// rollup_code_cell: +// stake_code_cell: +// rollup_state_cell: +// +// Inputs: +// stake_state_cell: +// stake_owner_cell: +// Lock: +// +// Outputs: +// +// ``` + +// stake_block_timestamp <= rollup_state_cell's L1 timestamp +#[test] +fn test_success_to_unlock_stake_via_finalize_based_on_timestamp() { + init_env_log(); + let (_, result) = unlock_stake_via_finalize_based_on_timestamp( + ROLLUP_STATE_CELL_TIMESTAMP - ROLLUP_CONFIG_FINALITY_DURATION_MS, + ); + result.expect("success"); +} + +// stake_block_timestamp > rollup_state_cell's L1 timestamp +#[test] +fn test_fail_to_unlock_stake_via_finalize_based_on_timestamp() { + init_env_log(); + let (stake_state_cell, result) = unlock_stake_via_finalize_based_on_timestamp( + ROLLUP_STATE_CELL_TIMESTAMP - ROLLUP_CONFIG_FINALITY_DURATION_MS + 1, + ); + let stake_code_type_hash: ckb_types::H256 = stake_state_cell.lock().code_hash().unpack(); + let expected_err = ScriptError::ValidationFailure( + format!("by-type-hash/{}", stake_code_type_hash), + 45, // Error::NotFinalized + ) + .input_lock_script(0); + assert_error_eq!(expected_err, result.unwrap_err()); +} + +fn unlock_stake_via_finalize_based_on_timestamp( + stake_block_timestamp: u64, +) -> (CellOutput, Result) { + let ( + mut ctx, + rollup_state_out_point, + rollup_state_type_script, + stake_code_out_point, + stake_code_type_script, + ) = build_context(); + let rollup_state_type_hash = rollup_state_type_script.calc_script_hash(); + let stake_code_type_hash = stake_code_type_script.calc_script_hash(); + + // Build rollup_state_cell's header_dep + let rollup_state_header = HeaderBuilder::default() + .timestamp(ROLLUP_STATE_CELL_TIMESTAMP.pack()) + .build(); + let rollup_state_tx_info = TransactionInfo { + block_hash: rollup_state_header.hash(), + block_number: rollup_state_header.number(), + block_epoch: rollup_state_header.epoch(), + index: 1, + }; + ctx.inner + .transaction_infos + .insert(rollup_state_out_point.clone(), rollup_state_tx_info); + ctx.inner + .headers + .insert(rollup_state_header.hash(), rollup_state_header.clone()); + + // Build a finalized stake_state_cell + let stake_state_out_point = random_out_point(); + let stake_owner_lock_script = random_always_success_script(); + let stake_owner_lock_hash = stake_owner_lock_script.calc_script_hash(); + let stake_state_cell = CellOutput::new_builder() + .lock( + Script::new_builder() + .code_hash(stake_code_type_hash.clone()) + .hash_type(ScriptHashType::Type.into()) + .args({ + let timepoint = stake_block_timestamp | (1 << 63); + let stake_lock_args = StakeLockArgs::new_builder() + .stake_block_number(gw_types::prelude::Pack::pack(&timepoint)) + .owner_lock_hash(gw_types::packed::Byte32::new_unchecked( + stake_owner_lock_hash.as_bytes(), + )) + .build(); + let mut args = Vec::new(); + args.extend_from_slice(&rollup_state_type_hash.as_slice()); + args.extend_from_slice(&stake_lock_args.as_slice()); + args.pack() + }) + .build(), + ) + .build(); + + // Build stake_owner_cell + let stake_owner_cell = CellOutput::new_builder() + .lock(stake_owner_lock_script) + .build(); + let stake_owner_out_point = ctx.insert_cell(stake_owner_cell, Default::default()); + + // Build transaction + let tx = build_simple_tx_with_out_point( + &mut ctx.inner, + (stake_state_cell.clone(), Default::default()), + stake_state_out_point, + (CellOutput::new_builder().build(), Default::default()), + ) + .as_advanced_builder() + .input(CellInput::new(stake_owner_out_point, 0)) + .cell_dep( + CellDep::new_builder() + .out_point(rollup_state_out_point) + .build(), + ) + .cell_dep( + CellDep::new_builder() + .out_point(stake_code_out_point) + .build(), + ) + .cell_dep(ctx.rollup_config_dep.clone()) + .cell_dep(ctx.always_success_dep.clone()) + .header_dep(rollup_state_header.hash()) + .build(); + let result = ctx.verify_tx(tx); + (stake_state_cell, result) +} diff --git a/tests/src/script_tests/stake_lock/mod.rs b/tests/src/script_tests/stake_lock/mod.rs new file mode 100644 index 00000000..c3dd162b --- /dev/null +++ b/tests/src/script_tests/stake_lock/mod.rs @@ -0,0 +1,3 @@ +mod finalize_based_on_block_number; +mod finalize_based_on_timestamp; +mod utils; diff --git a/tests/src/script_tests/stake_lock/utils.rs b/tests/src/script_tests/stake_lock/utils.rs new file mode 100644 index 00000000..3443b55a --- /dev/null +++ b/tests/src/script_tests/stake_lock/utils.rs @@ -0,0 +1,71 @@ +use crate::script_tests::utils::rollup::{random_always_success_script, CellContext}; +use crate::testing_tool::programs::STAKE_LOCK_PROGRAM; +use ckb_types::{ + packed::{CellOutput, OutPoint, Script}, + prelude::*, +}; +use gw_types::packed::{GlobalState, RollupConfig}; + +const BLOCK_INTERVAL_IN_MILLISECONDS: u64 = 36000; +pub(super) const ROLLUP_CONFIG_FINALITY_BLOCKS: u64 = 1; +pub(super) const ROLLUP_CONFIG_FINALITY_DURATION_MS: u64 = + ROLLUP_CONFIG_FINALITY_BLOCKS * BLOCK_INTERVAL_IN_MILLISECONDS; +pub(super) const ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER: u64 = 100; +pub(super) const ROLLUP_STATE_CELL_TIMESTAMP: u64 = 1555204979310; + +// Build common-used cells for testing stake-lock: +// - rollup_config_cell, finality_blocks = ROLLUP_CONFIG_FINALITY_BLOCKS +// - rollup_code_cell, is ALWAYS_SUCCESS_PROGRAM +// - rollup_state_cell, last_finalized_block_number = ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER +// - stake_code_cell, is STAKE_LOCK_PROGRAM +// +// Return (ctx, rollup_state_out_point, rollup_state_type_script, stake_code_out_point, stake_code_type_script); +pub(super) fn build_context() -> (CellContext, OutPoint, Script, OutPoint, Script) { + let rollup_config = RollupConfig::new_builder() + .finality_blocks(gw_types::prelude::Pack::pack( + &ROLLUP_CONFIG_FINALITY_BLOCKS, + )) + .build(); + let mut ctx = CellContext::new(&rollup_config, Default::default()); + + // Build a always-success rollup_state_cell, because we are testing + // stake-lock only + let rollup_state_data = GlobalState::new_builder() + .last_finalized_block_number(gw_types::prelude::Pack::pack( + &ROLLUP_STATE_LAST_FINALIZED_BLOCK_NUMBER, + )) + .rollup_config_hash({ + let rollup_config_data_hash = CellOutput::calc_data_hash(&rollup_config.as_bytes()); + gw_types::packed::Byte32::new_unchecked(rollup_config_data_hash.as_bytes()) + }) + .build(); + let rollup_state_cell = CellOutput::new_builder() + .lock(random_always_success_script()) + .type_(Some(random_always_success_script()).pack()) + .build(); + let rollup_state_type_script = rollup_state_cell + .type_() + .to_opt() + .expect("should be always-success"); + let rollup_state_out_point = ctx.insert_cell(rollup_state_cell, rollup_state_data.as_bytes()); + + // Build costodian_code_cell + let stake_code_data = STAKE_LOCK_PROGRAM.clone(); + let stake_code_cell = CellOutput::new_builder() + .lock(random_always_success_script()) + .type_(Some(random_always_success_script()).pack()) + .build(); + let stake_code_type_script = stake_code_cell + .type_() + .to_opt() + .expect("should be always-success"); + let stake_code_out_point = ctx.insert_cell(stake_code_cell, stake_code_data); + + return ( + ctx, + rollup_state_out_point, + rollup_state_type_script, + stake_code_out_point, + stake_code_type_script, + ); +} diff --git a/tests/src/script_tests/state_validator/cancel_challenge/mod.rs b/tests/src/script_tests/state_validator/cancel_challenge/mod.rs index be8e3462..5270dfb1 100644 --- a/tests/src/script_tests/state_validator/cancel_challenge/mod.rs +++ b/tests/src/script_tests/state_validator/cancel_challenge/mod.rs @@ -7,8 +7,8 @@ use crate::script_tests::utils::init_env_log; use crate::script_tests::utils::layer1::build_simple_tx_with_out_point; use crate::script_tests::utils::layer1::random_out_point; use crate::script_tests::utils::rollup::{ - build_always_success_cell, build_rollup_locked_cell, build_type_id_script, - calculate_state_validator_type_id, CellContext, CellContextParam, + build_always_success_cell, build_rollup_locked_cell, named_always_success_script, calculate_type_id, + CellContext, CellContextParam, }; use crate::testing_tool::chain::setup_chain_with_account_lock_manage; use crate::testing_tool::chain::{apply_block_result, construct_block}; @@ -64,7 +64,7 @@ pub(crate) fn build_merkle_proof(leaves: &[H256], indices: &[u32]) -> CKBMerkleP async fn test_burn_challenge_capacity() { init_env_log(); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -78,9 +78,9 @@ async fn test_burn_challenge_capacity() { .code_hash(CKBPack::pack(&[0u8; 32])) .build(); let reward_burn_lock_hash: [u8; 32] = reward_burn_lock.calc_script_hash().unpack(); - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); - let challenge_lock_type = build_type_id_script(b"challenge_lock_type_id"); - let eoa_lock_type = build_type_id_script(b"eoa_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); + let challenge_lock_type = named_always_success_script(b"challenge_lock_type_id"); + let eoa_lock_type = named_always_success_script(b"eoa_lock_type_id"); let challenge_script_type_hash: [u8; 32] = challenge_lock_type.calc_script_hash().unpack(); let eoa_lock_type_hash: [u8; 32] = eoa_lock_type.calc_script_hash().unpack(); let allowed_eoa_type_hashes: Vec = vec![AllowedTypeHash::new( diff --git a/tests/src/script_tests/state_validator/cancel_challenge/tx_execution.rs b/tests/src/script_tests/state_validator/cancel_challenge/tx_execution.rs index 112ecd1f..f3324201 100644 --- a/tests/src/script_tests/state_validator/cancel_challenge/tx_execution.rs +++ b/tests/src/script_tests/state_validator/cancel_challenge/tx_execution.rs @@ -7,8 +7,8 @@ use crate::script_tests::utils::init_env_log; use crate::script_tests::utils::layer1::build_simple_tx_with_out_point; use crate::script_tests::utils::layer1::random_out_point; use crate::script_tests::utils::rollup::{ - build_always_success_cell, build_rollup_locked_cell, build_type_id_script, - calculate_state_validator_type_id, CellContext, CellContextParam, + build_always_success_cell, build_rollup_locked_cell, named_always_success_script, calculate_type_id, + CellContext, CellContextParam, }; use crate::testing_tool::chain::setup_chain; use crate::testing_tool::chain::{apply_block_result, construct_block}; @@ -46,7 +46,7 @@ use gw_types::{ async fn test_cancel_tx_execute() { init_env_log(); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -55,10 +55,10 @@ async fn test_cancel_tx_execute() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); - let challenge_lock_type = build_type_id_script(b"challenge_lock_type_id"); - let eoa_lock_type = build_type_id_script(b"eoa_lock_type_id"); - let l2_sudt_type = build_type_id_script(b"l2_sudt_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); + let challenge_lock_type = named_always_success_script(b"challenge_lock_type_id"); + let eoa_lock_type = named_always_success_script(b"eoa_lock_type_id"); + let l2_sudt_type = named_always_success_script(b"l2_sudt_type_id"); let challenge_script_type_hash: [u8; 32] = challenge_lock_type.calc_script_hash().unpack(); let l2_sudt_type_hash: [u8; 32] = l2_sudt_type.calc_script_hash().unpack(); diff --git a/tests/src/script_tests/state_validator/cancel_challenge/tx_signature.rs b/tests/src/script_tests/state_validator/cancel_challenge/tx_signature.rs index be10eff0..243900e7 100644 --- a/tests/src/script_tests/state_validator/cancel_challenge/tx_signature.rs +++ b/tests/src/script_tests/state_validator/cancel_challenge/tx_signature.rs @@ -7,8 +7,8 @@ use crate::script_tests::utils::init_env_log; use crate::script_tests::utils::layer1::build_simple_tx_with_out_point; use crate::script_tests::utils::layer1::random_out_point; use crate::script_tests::utils::rollup::{ - build_always_success_cell, build_rollup_locked_cell, build_type_id_script, - calculate_state_validator_type_id, CellContext, CellContextParam, + build_always_success_cell, build_rollup_locked_cell, named_always_success_script, calculate_type_id, + CellContext, CellContextParam, }; use crate::testing_tool::chain::setup_chain_with_account_lock_manage; use crate::testing_tool::chain::{apply_block_result, construct_block}; @@ -52,7 +52,7 @@ use gw_types::{ async fn test_cancel_tx_signature() { init_env_log(); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -61,10 +61,10 @@ async fn test_cancel_tx_signature() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); - let challenge_lock_type = build_type_id_script(b"challenge_lock_type_id"); - let eoa_lock_type = build_type_id_script(b"eoa_lock_type_id"); - let l2_sudt_type = build_type_id_script(b"l2_sudt_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); + let challenge_lock_type = named_always_success_script(b"challenge_lock_type_id"); + let eoa_lock_type = named_always_success_script(b"eoa_lock_type_id"); + let l2_sudt_type = named_always_success_script(b"l2_sudt_type_id"); let challenge_script_type_hash: [u8; 32] = challenge_lock_type.calc_script_hash().unpack(); let eoa_lock_type_hash: [u8; 32] = eoa_lock_type.calc_script_hash().unpack(); let l2_sudt_type_hash: [u8; 32] = l2_sudt_type.calc_script_hash().unpack(); diff --git a/tests/src/script_tests/state_validator/cancel_challenge/withdrawal.rs b/tests/src/script_tests/state_validator/cancel_challenge/withdrawal.rs index c88d5706..37ffb214 100644 --- a/tests/src/script_tests/state_validator/cancel_challenge/withdrawal.rs +++ b/tests/src/script_tests/state_validator/cancel_challenge/withdrawal.rs @@ -8,8 +8,8 @@ use crate::script_tests::utils::init_env_log; use crate::script_tests::utils::layer1::build_simple_tx_with_out_point; use crate::script_tests::utils::layer1::random_out_point; use crate::script_tests::utils::rollup::{ - build_always_success_cell, build_rollup_locked_cell, build_type_id_script, - calculate_state_validator_type_id, CellContext, CellContextParam, + build_always_success_cell, build_rollup_locked_cell, named_always_success_script, calculate_type_id, + CellContext, CellContextParam, }; use crate::testing_tool::chain::{ apply_block_result, construct_block, setup_chain_with_account_lock_manage, @@ -50,7 +50,7 @@ use gw_types::{ async fn test_cancel_withdrawal() { init_env_log(); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -59,9 +59,9 @@ async fn test_cancel_withdrawal() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); - let challenge_lock_type = build_type_id_script(b"challenge_lock_type_id"); - let eoa_lock_type = build_type_id_script(b"eoa_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); + let challenge_lock_type = named_always_success_script(b"challenge_lock_type_id"); + let eoa_lock_type = named_always_success_script(b"eoa_lock_type_id"); let challenge_script_type_hash: [u8; 32] = challenge_lock_type.calc_script_hash().unpack(); let eoa_lock_type_hash: [u8; 32] = eoa_lock_type.calc_script_hash().unpack(); let allowed_eoa_type_hashes: Vec = vec![AllowedTypeHash::new( diff --git a/tests/src/script_tests/state_validator/enter_challenge.rs b/tests/src/script_tests/state_validator/enter_challenge.rs index e402c4c2..3d77ab55 100644 --- a/tests/src/script_tests/state_validator/enter_challenge.rs +++ b/tests/src/script_tests/state_validator/enter_challenge.rs @@ -6,13 +6,16 @@ use crate::script_tests::utils::init_env_log; use crate::script_tests::utils::layer1::build_simple_tx_with_out_point; use crate::script_tests::utils::layer1::random_out_point; use crate::script_tests::utils::rollup::{ - build_always_success_cell, build_rollup_locked_cell, build_type_id_script, - calculate_state_validator_type_id, CellContext, CellContextParam, + build_always_success_cell, build_rollup_locked_cell, named_always_success_script, calculate_type_id, + CellContext, CellContextParam, +}; +use crate::testing_tool::chain::{ + apply_block_result, construct_block, construct_block_from_timestamp, setup_chain, }; -use crate::testing_tool::chain::{apply_block_result, construct_block, setup_chain}; use crate::testing_tool::programs::{ALWAYS_SUCCESS_CODE_HASH, STATE_VALIDATOR_CODE_HASH}; use ckb_error::assert_error_eq; use ckb_script::ScriptError; +use ckb_types::core::{HeaderBuilder, TransactionInfo}; use ckb_types::packed::CellOutput; use ckb_types::prelude::{Pack as CKBPack, Unpack}; use gw_chain::chain::Chain; @@ -41,7 +44,7 @@ const INVALID_CHALLENGE_TARGET_ERROR: i8 = 32; async fn test_enter_challenge() { init_env_log(); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -50,10 +53,10 @@ async fn test_enter_challenge() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); - let challenge_lock_type = build_type_id_script(b"challenge_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); + let challenge_lock_type = named_always_success_script(b"challenge_lock_type_id"); let challenge_script_type_hash: [u8; 32] = challenge_lock_type.calc_script_hash().unpack(); - let finality_blocks = 10; + let finality_blocks: u64 = 10; let rollup_config = RollupConfig::new_builder() .challenge_script_type_hash(Pack::pack(&challenge_script_type_hash)) .finality_blocks(Pack::pack(&finality_blocks)) @@ -170,7 +173,7 @@ async fn test_enter_challenge() { let mem_pool = chain.mem_pool().as_ref().unwrap(); let mut mem_pool = mem_pool.lock().await; mem_pool.push_transaction(tx).await.unwrap(); - construct_block(&chain, &mut mem_pool, Vec::default()) + construct_block_from_timestamp(&chain, &mut mem_pool, Vec::default(), 1557311767000) .await .unwrap() }; @@ -248,6 +251,34 @@ async fn test_enter_challenge() { .status(Status::Halting.into()) .build() .as_bytes(); + + // Prepare the L1 Header and TransactionInfo of the rollup cell, which will + // be used for finality check. + let rollup_cell_header = HeaderBuilder::default() + .timestamp({ + // Ensure the challenged block **is not finalized** + const BLOCK_INTERVAL_IN_MILLISECONDS: u64 = 36000; + let finality_duration_ms = finality_blocks * BLOCK_INTERVAL_IN_MILLISECONDS; + let challenged_block_timestamp: u64 = + gw_types::prelude::Unpack::unpack(&challenged_block.raw().timestamp()); + let rollup_cell_timestamp = challenged_block_timestamp + finality_duration_ms - 1; + CKBPack::pack(&rollup_cell_timestamp) + }) + .build(); + let rollup_cell_tx_info = TransactionInfo { + block_hash: rollup_cell_header.hash(), + block_number: rollup_cell_header.number(), + block_epoch: rollup_cell_header.epoch(), + index: 1, + }; + ctx.inner + .transaction_infos + .insert(input_out_point.clone(), rollup_cell_tx_info); + ctx.inner + .headers + .insert(rollup_cell_header.hash(), rollup_cell_header.clone()); + + // Build tx let tx = build_simple_tx_with_out_point( &mut ctx.inner, (rollup_cell.clone(), initial_rollup_cell_data), @@ -261,6 +292,7 @@ async fn test_enter_challenge() { .cell_dep(ctx.always_success_dep.clone()) .cell_dep(ctx.state_validator_dep.clone()) .cell_dep(ctx.rollup_config_dep.clone()) + .header_dep(rollup_cell_header.hash()) .witness(CKBPack::pack(&witness.as_bytes())) .build(); ctx.verify_tx(tx).expect("return success"); @@ -270,7 +302,7 @@ async fn test_enter_challenge() { async fn test_enter_challenge_finalized_block() { init_env_log(); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -279,8 +311,8 @@ async fn test_enter_challenge_finalized_block() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); - let challenge_lock_type = build_type_id_script(b"challenge_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); + let challenge_lock_type = named_always_success_script(b"challenge_lock_type_id"); let challenge_script_type_hash: [u8; 32] = challenge_lock_type.calc_script_hash().unpack(); let finality_blocks = 1; let rollup_config = RollupConfig::new_builder() @@ -452,6 +484,33 @@ async fn test_enter_challenge_finalized_block() { .status(Status::Halting.into()) .build() .as_bytes(); + + // Prepare the L1 Header and TransactionInfo of the rollup cell, which will + // be used for finality check. + let rollup_cell_header = HeaderBuilder::default() + .timestamp({ + // Ensure the challenged block **is finalized** + const BLOCK_INTERVAL_IN_MILLISECONDS: u64 = 36000; + let finality_duration_ms = finality_blocks * BLOCK_INTERVAL_IN_MILLISECONDS; + let challenged_block_timestamp: u64 = + gw_types::prelude::Unpack::unpack(&challenged_block.raw().timestamp()); + let rollup_cell_timestamp = challenged_block_timestamp + finality_duration_ms; + CKBPack::pack(&rollup_cell_timestamp) + }) + .build(); + let rollup_cell_tx_info = TransactionInfo { + block_hash: rollup_cell_header.hash(), + block_number: rollup_cell_header.number(), + block_epoch: rollup_cell_header.epoch(), + index: 1, + }; + ctx.inner + .transaction_infos + .insert(input_out_point.clone(), rollup_cell_tx_info); + ctx.inner + .headers + .insert(rollup_cell_header.hash(), rollup_cell_header.clone()); + let tx = build_simple_tx_with_out_point( &mut ctx.inner, (rollup_cell.clone(), initial_rollup_cell_data), @@ -465,6 +524,7 @@ async fn test_enter_challenge_finalized_block() { .cell_dep(ctx.always_success_dep.clone()) .cell_dep(ctx.state_validator_dep.clone()) .cell_dep(ctx.rollup_config_dep.clone()) + .header_dep(rollup_cell_header.hash()) .witness(CKBPack::pack(&witness.as_bytes())) .build(); diff --git a/tests/src/script_tests/state_validator/revert.rs b/tests/src/script_tests/state_validator/revert.rs index ad84dde9..93a10e8f 100644 --- a/tests/src/script_tests/state_validator/revert.rs +++ b/tests/src/script_tests/state_validator/revert.rs @@ -6,8 +6,8 @@ use crate::script_tests::utils::init_env_log; use crate::script_tests::utils::layer1::build_simple_tx_with_out_point; use crate::script_tests::utils::layer1::{always_success_script, random_out_point}; use crate::script_tests::utils::rollup::{ - build_always_success_cell, build_rollup_locked_cell, build_type_id_script, - calculate_state_validator_type_id, CellContext, CellContextParam, + build_always_success_cell, build_rollup_locked_cell, named_always_success_script, calculate_type_id, + CellContext, CellContextParam, }; use crate::testing_tool::chain::{apply_block_result, construct_block, setup_chain}; use crate::testing_tool::programs::{ALWAYS_SUCCESS_CODE_HASH, STATE_VALIDATOR_CODE_HASH}; @@ -40,7 +40,7 @@ use gw_types::{packed::StakeLockArgs, prelude::*}; async fn test_revert() { init_env_log(); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -58,9 +58,9 @@ async fn test_revert() { .code_hash(CKBPack::pack(&[0u8; 32])) .build(); let reward_burn_lock_hash: [u8; 32] = reward_burn_lock.calc_script_hash().unpack(); - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); let stake_script_type_hash: [u8; 32] = stake_lock_type.calc_script_hash().unpack(); - let challenge_lock_type = build_type_id_script(b"challenge_lock_type_id"); + let challenge_lock_type = named_always_success_script(b"challenge_lock_type_id"); let challenge_script_type_hash: [u8; 32] = challenge_lock_type.calc_script_hash().unpack(); let finality_blocks = 10; let rollup_config = RollupConfig::new_builder() diff --git a/tests/src/script_tests/state_validator/submit_block.rs b/tests/src/script_tests/state_validator/submit_block.rs index 9dead1a9..a384f133 100644 --- a/tests/src/script_tests/state_validator/submit_block.rs +++ b/tests/src/script_tests/state_validator/submit_block.rs @@ -4,8 +4,8 @@ use crate::script_tests::utils::layer1::{ build_simple_tx_with_out_point_and_since, random_out_point, since_timestamp, }; use crate::script_tests::utils::rollup::{ - build_always_success_cell, build_rollup_locked_cell, build_type_id_script, - calculate_state_validator_type_id, CellContext, CellContextParam, + build_always_success_cell, build_rollup_locked_cell, named_always_success_script, calculate_type_id, + CellContext, CellContextParam, }; use crate::testing_tool::chain::{build_sync_tx, construct_block_from_timestamp}; use crate::testing_tool::programs::{ALWAYS_SUCCESS_CODE_HASH, STATE_VALIDATOR_CODE_HASH}; @@ -44,7 +44,7 @@ async fn test_submit_block() { let capacity = 1000_00000000u64; let spend_cell = build_always_success_cell(capacity, None); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -53,7 +53,7 @@ async fn test_submit_block() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); let stake_script_type_hash: [u8; 32] = stake_lock_type.calc_script_hash().unpack(); let rollup_config = RollupConfig::new_builder() .stake_script_type_hash(Pack::pack(&stake_script_type_hash)) @@ -163,7 +163,7 @@ async fn test_downgrade_rollup_cell() { let capacity = 1000_00000000u64; let spend_cell = build_always_success_cell(capacity, None); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -172,7 +172,7 @@ async fn test_downgrade_rollup_cell() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); let stake_script_type_hash: [u8; 32] = stake_lock_type.calc_script_hash().unpack(); let rollup_config = RollupConfig::new_builder() .stake_script_type_hash(Pack::pack(&stake_script_type_hash)) @@ -297,7 +297,7 @@ async fn test_v1_block_timestamp_smaller_or_equal_than_previous_block_in_submit_ let capacity = 1000_00000000u64; let spend_cell = build_always_success_cell(capacity, None); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -306,7 +306,7 @@ async fn test_v1_block_timestamp_smaller_or_equal_than_previous_block_in_submit_ .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); let stake_script_type_hash: [u8; 32] = stake_lock_type.calc_script_hash().unpack(); let rollup_config = RollupConfig::new_builder() .stake_script_type_hash(Pack::pack(&stake_script_type_hash)) @@ -504,7 +504,7 @@ async fn test_v1_block_timestamp_bigger_than_rollup_input_since_in_submit_block( let capacity = 1000_00000000u64; let spend_cell = build_always_success_cell(capacity, None); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -513,7 +513,7 @@ async fn test_v1_block_timestamp_bigger_than_rollup_input_since_in_submit_block( .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); let stake_script_type_hash: [u8; 32] = stake_lock_type.calc_script_hash().unpack(); let rollup_config = RollupConfig::new_builder() .stake_script_type_hash(Pack::pack(&stake_script_type_hash)) @@ -639,7 +639,7 @@ async fn test_v0_v1_wrong_global_state_tip_block_timestamp_in_submit_block() { let capacity = 1000_00000000u64; let spend_cell = build_always_success_cell(capacity, None); let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -648,7 +648,7 @@ async fn test_v0_v1_wrong_global_state_tip_block_timestamp_in_submit_block() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); let stake_script_type_hash: [u8; 32] = stake_lock_type.calc_script_hash().unpack(); let rollup_config = RollupConfig::new_builder() .stake_script_type_hash(Pack::pack(&stake_script_type_hash)) @@ -846,7 +846,7 @@ async fn test_v0_v1_wrong_global_state_tip_block_timestamp_in_submit_block() { async fn test_check_reverted_cells_in_submit_block() { let capacity = 1000_00000000u64; let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -855,13 +855,13 @@ async fn test_check_reverted_cells_in_submit_block() { .build() }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); let stake_script_type_hash: [u8; 32] = stake_lock_type.calc_script_hash().unpack(); - let deposit_lock_type = build_type_id_script(b"deposit_lock_type_id"); + let deposit_lock_type = named_always_success_script(b"deposit_lock_type_id"); let deposit_script_type_hash: [u8; 32] = deposit_lock_type.calc_script_hash().unpack(); - let custodian_lock_type = build_type_id_script(b"custodian_lock_type_id"); + let custodian_lock_type = named_always_success_script(b"custodian_lock_type_id"); let custodian_script_type_hash: [u8; 32] = custodian_lock_type.calc_script_hash().unpack(); - let withdrawal_lock_type = build_type_id_script(b"withdrawal_lock_type_id"); + let withdrawal_lock_type = named_always_success_script(b"withdrawal_lock_type_id"); let withdrawal_script_type_hash: [u8; 32] = withdrawal_lock_type.calc_script_hash().unpack(); let rollup_config = RollupConfig::new_builder() .stake_script_type_hash(Pack::pack(&stake_script_type_hash)) @@ -1102,7 +1102,7 @@ async fn test_withdrawal_cell_lock_args_with_owner_lock_in_submit_block() { let capacity = 1000_00000000u64; let input_out_point = random_out_point(); - let type_id = calculate_state_validator_type_id(input_out_point.clone()); + let type_id = calculate_type_id(input_out_point.clone()); let rollup_type_script = { Script::new_builder() .code_hash(Pack::pack(&*STATE_VALIDATOR_CODE_HASH)) @@ -1112,11 +1112,11 @@ async fn test_withdrawal_cell_lock_args_with_owner_lock_in_submit_block() { }; // rollup lock & config - let stake_lock_type = build_type_id_script(b"stake_lock_type_id"); + let stake_lock_type = named_always_success_script(b"stake_lock_type_id"); let stake_script_type_hash: [u8; 32] = stake_lock_type.calc_script_hash().unpack(); - let custodian_lock_type = build_type_id_script(b"custodian_lock_type_id"); + let custodian_lock_type = named_always_success_script(b"custodian_lock_type_id"); let custodian_script_type_hash: [u8; 32] = custodian_lock_type.calc_script_hash().unpack(); - let withdrawal_lock_type = build_type_id_script(b"withdrawal_lock_type_id"); + let withdrawal_lock_type = named_always_success_script(b"withdrawal_lock_type_id"); let withdrawal_script_type_hash: [u8; 32] = withdrawal_lock_type.calc_script_hash().unpack(); let rollup_config = RollupConfig::new_builder() .stake_script_type_hash(Pack::pack(&stake_script_type_hash)) diff --git a/tests/src/script_tests/utils/layer1.rs b/tests/src/script_tests/utils/layer1.rs index 94240804..c9365c2f 100644 --- a/tests/src/script_tests/utils/layer1.rs +++ b/tests/src/script_tests/utils/layer1.rs @@ -4,7 +4,7 @@ use ckb_types::{ bytes::Bytes, core::{ cell::{CellMetaBuilder, ResolvedTransaction}, - EpochExt, HeaderView, ScriptHashType, TransactionView, + EpochExt, HeaderView, ScriptHashType, TransactionInfo, TransactionView, }, packed::{Byte32, CellInput, CellOutput, OutPoint, Script, Transaction, Uint64}, prelude::*, @@ -21,6 +21,12 @@ pub struct DummyDataLoader { pub cells: HashMap, pub headers: HashMap, pub epoches: HashMap, + + /// For syscall `load_header` with `Source::Input` and `Source::CellDep`, + /// CKB-VM find the block header referenced via + /// `cell.transaction_info.block_hash`. + /// See more: https://github.com/nervosnetwork/ckb/blob/d3fddf863be951b128e2978077fa747bad096e9a/script/src/syscalls/load_header.rs#L47-L59 + pub transaction_infos: HashMap, } impl CellDataProvider for DummyDataLoader { @@ -121,30 +127,33 @@ pub fn build_resolved_tx( data_loader: &DummyDataLoader, tx: &TransactionView, ) -> ResolvedTransaction { + let get_cell_meta = |out_point: OutPoint| { + let (output, output_data) = data_loader.cells.get(&out_point).unwrap(); + match data_loader.transaction_infos.get(&out_point) { + Some(transaction_info) => { + CellMetaBuilder::from_cell_output(output.to_owned(), output_data.to_owned()) + .out_point(out_point) + .transaction_info(transaction_info.clone()) + .build() + } + None => CellMetaBuilder::from_cell_output(output.to_owned(), output_data.to_owned()) + .out_point(out_point) + .build(), + } + }; let resolved_cell_deps = tx .cell_deps() .into_iter() - .map(|dep| { - let deps_out_point = dep.clone(); - let (dep_output, dep_data) = - data_loader.cells.get(&deps_out_point.out_point()).unwrap(); - CellMetaBuilder::from_cell_output(dep_output.to_owned(), dep_data.to_owned()) - .out_point(deps_out_point.out_point().clone()) - .build() + .map(|cell_dep| get_cell_meta(cell_dep.out_point())) + .collect(); + let resolved_inputs = (0..tx.inputs().len()) + .map(|i| { + let cell_input = tx.inputs().get(i).unwrap(); + let out_point = cell_input.previous_output(); + get_cell_meta(out_point) }) .collect(); - let mut resolved_inputs = Vec::new(); - for i in 0..tx.inputs().len() { - let previous_out_point = tx.inputs().get(i).unwrap().previous_output(); - let (input_output, input_data) = data_loader.cells.get(&previous_out_point).unwrap(); - resolved_inputs.push( - CellMetaBuilder::from_cell_output(input_output.to_owned(), input_data.to_owned()) - .out_point(previous_out_point) - .build(), - ); - } - ResolvedTransaction { transaction: tx.clone(), resolved_cell_deps, diff --git a/tests/src/script_tests/utils/rollup.rs b/tests/src/script_tests/utils/rollup.rs index e81c775f..22da315f 100644 --- a/tests/src/script_tests/utils/rollup.rs +++ b/tests/src/script_tests/utils/rollup.rs @@ -34,15 +34,15 @@ pub struct CellContextParam { impl Default for CellContextParam { fn default() -> Self { Self { - stake_lock_type: random_type_id_script(), - challenge_lock_type: random_type_id_script(), - deposit_lock_type: random_type_id_script(), - custodian_lock_type: random_type_id_script(), - withdrawal_lock_type: random_type_id_script(), - l2_sudt_type: random_type_id_script(), - always_success_type: random_type_id_script(), - eoa_lock_type: random_type_id_script(), - eth_lock_type: random_type_id_script(), + stake_lock_type: random_always_success_script(), + challenge_lock_type: random_always_success_script(), + deposit_lock_type: random_always_success_script(), + custodian_lock_type: random_always_success_script(), + withdrawal_lock_type: random_always_success_script(), + l2_sudt_type: random_always_success_script(), + always_success_type: random_always_success_script(), + eoa_lock_type: random_always_success_script(), + eth_lock_type: random_always_success_script(), } } } @@ -303,16 +303,16 @@ impl CellContext { } } -pub fn build_type_id_script(name: &[u8]) -> ckb_types::packed::Script { +pub fn named_always_success_script(name: &[u8]) -> ckb_types::packed::Script { ckb_types::packed::Script::new_builder() .code_hash(CKBPack::pack(&ALWAYS_SUCCESS_CODE_HASH.clone())) .args(CKBPack::pack(&Bytes::from(name.to_vec()))) .build() } -fn random_type_id_script() -> ckb_types::packed::Script { +pub fn random_always_success_script() -> ckb_types::packed::Script { let random_bytes: [u8; 32] = rand::random(); - build_type_id_script(&random_bytes) + named_always_success_script(&random_bytes) } pub fn build_rollup_locked_cell( @@ -337,6 +337,7 @@ pub fn build_rollup_locked_cell( .build() } +/// Build always-success cell pub fn build_always_success_cell( capacity: u64, type_: Option, @@ -348,7 +349,8 @@ pub fn build_always_success_cell( .build() } -pub fn calculate_state_validator_type_id(input_out_point: ckb_types::packed::OutPoint) -> [u8; 32] { +/// Calculate type_id according to the CKB built-in TYPE_ID rule +pub fn calculate_type_id(input_out_point: ckb_types::packed::OutPoint) -> [u8; 32] { let input = ckb_types::packed::CellInput::new_builder() .previous_output(input_out_point) .build(); diff --git a/tests/src/script_tests/withdrawal.rs b/tests/src/script_tests/withdrawal.rs index 54b07a63..31f2a409 100644 --- a/tests/src/script_tests/withdrawal.rs +++ b/tests/src/script_tests/withdrawal.rs @@ -9,7 +9,7 @@ use crate::testing_tool::programs::{ use ckb_error::assert_error_eq; use ckb_script::ScriptError; -use ckb_types::core::TransactionView; +use ckb_types::core::{HeaderBuilder, TransactionInfo, TransactionView}; use ckb_types::prelude::{Builder, Entity}; use gw_common::blake2b::new_blake2b; use gw_types::bytes::Bytes; @@ -25,6 +25,184 @@ use secp256k1::{Message, Secp256k1, SecretKey}; const OWNER_CELL_NOT_FOUND_ERROR: i8 = 8; +#[test] +fn test_unlock_withdrawal_via_finalize_by_input_owner_cell_based_on_timestamp() { + init_env_log(); + + const ROLLUP_STATE_CELL_TIMESTAMP: u64 = 1555204979310; + const BLOCK_INTERVAL_IN_MILLISECONDS: u64 = 36000; + const ROLLUP_CONFIG_FINALITY_BLOCKS: u64 = 10; + const ROLLUP_CONFIG_FINALITY_DURATION_MS: u64 = + ROLLUP_CONFIG_FINALITY_BLOCKS * BLOCK_INTERVAL_IN_MILLISECONDS; + const DEFAULT_CAPACITY: u64 = 1000 * 10u64.pow(8); + + let rollup_type_script = random_always_success_script(); + let rollup_type_hash = rollup_type_script.hash(); + let (mut verify_ctx, script_ctx) = build_verify_context(); + + let last_finalized_block_number = 100; + let rollup_cell = { + let global_state = GlobalState::new_builder() + .rollup_config_hash({ + let (_, rollup_config) = verify_ctx + .inner + .cells + .get(&verify_ctx.rollup_config_dep.out_point()) + .unwrap(); + let rollup_config_data_hash = + ckb_types::packed::CellOutput::calc_data_hash(&rollup_config); + gw_types::packed::Byte32::new_unchecked(rollup_config_data_hash.as_bytes()) + }) + .last_finalized_block_number(last_finalized_block_number.pack()) + .build(); + + let output = CellOutput::new_builder() + .lock(random_always_success_script()) + .type_(Some(rollup_type_script).pack()) + .capacity(DEFAULT_CAPACITY.pack()) + .build(); + + (output, global_state.as_bytes()) + }; + let rollup_dep = { + let out_point = verify_ctx.insert_cell(rollup_cell.0.to_ckb(), rollup_cell.1); + CellDep::new_builder().out_point(out_point.to_gw()).build() + }; + + let (sk, pk) = { + let secp = Secp256k1::new(); + let mut rng = OsRng::new().unwrap(); + secp.generate_keypair(&mut rng) + }; + let (err_sk, _err_pk) = { + let secp = Secp256k1::new(); + let mut rng = OsRng::new().unwrap(); + secp.generate_keypair(&mut rng) + }; + let owner_lock = { + let args = { + let mut buf = [0u8; 32]; + let mut hasher = new_blake2b(); + hasher.update(&pk.serialize()); + hasher.finalize(&mut buf); + + Bytes::copy_from_slice(&buf[..20]) + }; + + Script::new_builder() + .code_hash(script_ctx.acp.script.hash().pack()) + .hash_type(ScriptHashType::Type.into()) + .args(args.pack()) + .build() + }; + let finalized_withdrawal_cell = { + let lock_args = WithdrawalLockArgs::new_builder() + .account_script_hash(random_always_success_script().hash().pack()) + .withdrawal_block_hash(random_always_success_script().hash().pack()) + .withdrawal_block_number({ + let timestamp = ROLLUP_STATE_CELL_TIMESTAMP - ROLLUP_CONFIG_FINALITY_DURATION_MS; + gw_types::prelude::Pack::pack(&(timestamp | (1 << 63))) + }) + .owner_lock_hash(owner_lock.hash().pack()) + .build(); + let mut args = Vec::new(); + args.extend_from_slice(&lock_args.as_bytes()); + args.extend_from_slice(&(owner_lock.as_bytes().len() as u32).to_be_bytes()); + args.extend_from_slice(&owner_lock.as_bytes()); + + let output = build_rollup_locked_cell( + &rollup_type_hash, + &script_ctx.withdrawal.script.hash(), + DEFAULT_CAPACITY, + args.into(), + ); + + (output, 0u128.pack().as_bytes()) + }; + let finalized_withdrawal_input = { + let out_point = + verify_ctx.insert_cell(finalized_withdrawal_cell.0.clone(), 0u128.pack().as_bytes()); + CellInput::new_builder() + .previous_output(out_point.to_gw()) + .build() + }; + let owner_input = { + let output = CellOutput::new_builder() + .capacity(DEFAULT_CAPACITY.pack()) + .lock(owner_lock.clone()) + .build(); + + let out_point = verify_ctx.insert_cell(output.to_ckb(), 0u128.pack().as_bytes()); + CellInput::new_builder() + .previous_output(out_point.to_gw()) + .build() + }; + let output_cell = { + let output = CellOutput::new_builder() + .capacity((DEFAULT_CAPACITY * 2).pack()) + .lock(owner_lock) + .build(); + + (output.to_ckb(), 0u128.pack().as_bytes()) + }; + let unlock_via_finalize_witness = { + let unlock_args = UnlockWithdrawalViaFinalize::new_builder().build(); + let unlock_witness = UnlockWithdrawalWitness::new_builder() + .set(UnlockWithdrawalWitnessUnion::UnlockWithdrawalViaFinalize( + unlock_args, + )) + .build(); + WitnessArgs::new_builder() + .lock(Some(unlock_witness.as_bytes()).pack()) + .build() + }; + + // Build rollup_state_cell's header_dep + let rollup_state_header = HeaderBuilder::default() + .timestamp(ckb_types::prelude::Pack::pack(&ROLLUP_STATE_CELL_TIMESTAMP)) + .build(); + let rollup_state_tx_info = TransactionInfo { + block_hash: rollup_state_header.hash(), + block_number: rollup_state_header.number(), + block_epoch: rollup_state_header.epoch(), + index: 1, + }; + verify_ctx + .inner + .transaction_infos + .insert(rollup_dep.out_point().to_ckb(), rollup_state_tx_info); + verify_ctx + .inner + .headers + .insert(rollup_state_header.hash(), rollup_state_header.clone()); + + let tx = build_simple_tx_with_out_point( + &mut verify_ctx.inner, + finalized_withdrawal_cell, + finalized_withdrawal_input.to_ckb().previous_output(), + output_cell, + ) + .as_advanced_builder() + .witness(unlock_via_finalize_witness.as_bytes().to_ckb()) + .input(owner_input.to_ckb()) + .witness(Default::default()) + .cell_dep(script_ctx.withdrawal.dep.to_ckb()) + .cell_dep(script_ctx.acp.dep.to_ckb()) + .cell_dep(script_ctx.secp256k1_data.dep.to_ckb()) + .cell_dep(rollup_dep.to_ckb()) + .cell_dep(verify_ctx.rollup_config_dep.clone()) + .header_dep(rollup_state_header.hash()) + .build(); + + let err_sign_tx = sign_tx(tx.clone(), 1, &err_sk); + verify_ctx + .verify_tx(err_sign_tx) + .expect_err("wrong privtate key"); + + let sign_tx = sign_tx(tx, 1, &sk); + verify_ctx.verify_tx(sign_tx).expect("success"); +} + #[test] fn test_unlock_withdrawal_via_finalize_by_input_owner_cell() { init_env_log(); @@ -35,7 +213,7 @@ fn test_unlock_withdrawal_via_finalize_by_input_owner_cell() { let rollup_type_hash = rollup_type_script.hash(); let (mut verify_ctx, script_ctx) = build_verify_context(); - let last_finalized_block_number = rand::random::() + 100; + let last_finalized_block_number = 100; let rollup_cell = { let global_state = GlobalState::new_builder() .last_finalized_block_number(last_finalized_block_number.pack()) @@ -174,7 +352,7 @@ fn test_unlock_withdrawal_via_finalize_by_switch_indexed_output_to_owner_lock() let rollup_type_hash = rollup_type_script.hash(); let (mut verify_ctx, script_ctx) = build_verify_context(); - let last_finalized_block_number = rand::random::() + 100; + let last_finalized_block_number = (rand::random::() + 100) as u64; let rollup_cell = { let global_state = GlobalState::new_builder() .last_finalized_block_number(last_finalized_block_number.pack()) @@ -343,7 +521,7 @@ fn test_unlock_withdrawal_via_finalize_fallback_to_input_owner_cell() { let rollup_type_hash = rollup_type_script.hash(); let (mut verify_ctx, script_ctx) = build_verify_context(); - let last_finalized_block_number = rand::random::() + 100; + let last_finalized_block_number = (rand::random::() + 100) as u64; let rollup_cell = { let global_state = GlobalState::new_builder() .last_finalized_block_number(last_finalized_block_number.pack()) @@ -633,6 +811,7 @@ mod conversion { } }; } + impl_to_ckb!(OutPoint); impl_to_ckb!(Script); impl_to_ckb!(CellInput); impl_to_ckb!(CellOutput); diff --git a/tests/src/testing_tool/programs.rs b/tests/src/testing_tool/programs.rs index 2cb00aff..5372eb10 100644 --- a/tests/src/testing_tool/programs.rs +++ b/tests/src/testing_tool/programs.rs @@ -7,6 +7,8 @@ use std::{fs, io::Read, path::PathBuf}; const SCRIPT_DIR: &str = "../build/debug"; const CHALLENGE_LOCK_PATH: &str = "challenge-lock"; const WITHDRAWAL_LOCK_PATH: &str = "withdrawal-lock"; +const CUSTODIAN_LOCK_PATH: &str = "custodian-lock"; +const STAKE_LOCK_PATH: &str = "stake-lock"; const STATE_VALIDATOR: &str = "state-validator"; const ALWAYS_SUCCESS_PATH: &str = "always-success"; const SECP256K1_DATA_PATH: &str = "../c/deps/ckb-production-scripts/build/secp256k1_data"; @@ -174,4 +176,37 @@ lazy_static! { hasher.finalize(&mut buf); buf }; + pub static ref CUSTODIAN_LOCK_PROGRAM: Bytes = { + let mut buf = Vec::new(); + let mut path = PathBuf::new(); + path.push(&SCRIPT_DIR); + path.push(&CUSTODIAN_LOCK_PATH); + let mut f = fs::File::open(&path).expect("load custodian lock program"); + f.read_to_end(&mut buf) + .expect("read custodian lock program"); + Bytes::from(buf.to_vec()) + }; + pub static ref CUSTODIAN_LOCK_CODE_HASH: [u8; 32] = { + let mut buf = [0u8; 32]; + let mut hasher = new_blake2b(); + hasher.update(&CUSTODIAN_LOCK_PROGRAM); + hasher.finalize(&mut buf); + buf + }; + pub static ref STAKE_LOCK_PROGRAM: Bytes = { + let mut buf = Vec::new(); + let mut path = PathBuf::new(); + path.push(&SCRIPT_DIR); + path.push(&STAKE_LOCK_PATH); + let mut f = fs::File::open(&path).expect("load stake lock program"); + f.read_to_end(&mut buf).expect("read stake lock program"); + Bytes::from(buf.to_vec()) + }; + pub static ref STAKE_LOCK_CODE_HASH: [u8; 32] = { + let mut buf = [0u8; 32]; + let mut hasher = new_blake2b(); + hasher.update(&STAKE_LOCK_PROGRAM); + hasher.finalize(&mut buf); + buf + }; }