diff --git a/.github/workflows/godwoken-tests.yml b/.github/workflows/godwoken-tests.yml index fee6da5e8..88735e1c8 100644 --- a/.github/workflows/godwoken-tests.yml +++ b/.github/workflows/godwoken-tests.yml @@ -28,4 +28,4 @@ jobs: POLYJUICE_GIT_URL=https://github.com/${{ github.repository }} POLYJUICE_GIT_CHECKOUT=${{ github.ref }} GODWOKEN_KICKER_REPO=godwokenrises/godwoken-kicker - GODWOKEN_KICKER_REF=refs/pull/338/merge + GODWOKEN_KICKER_REF=refs/pull/345/merge diff --git a/Cargo.lock b/Cargo.lock index 99061a8a0..e300bd129 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -594,7 +594,7 @@ dependencies = [ "ckb-types", "ckb-util", "serde", - "toml", + "toml 0.5.9", ] [[package]] @@ -1717,7 +1717,7 @@ dependencies = [ "tentacle-secio", "tikv-jemallocator", "tokio", - "toml", + "toml 0.5.9", ] [[package]] @@ -1820,7 +1820,7 @@ dependencies = [ "log", "thiserror", "tokio", - "toml", + "toml 0.5.9", "tracing", ] @@ -1875,7 +1875,7 @@ dependencies = [ "hex", "lazy_static", "serde", - "toml", + "toml 0.5.9", ] [[package]] @@ -2038,7 +2038,7 @@ dependencies = [ "log", "thiserror", "tokio", - "toml", + "toml 0.5.9", ] [[package]] @@ -2250,7 +2250,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "toml_edit", + "toml 0.7.1", "tracing-subscriber", "url", ] @@ -2296,6 +2296,7 @@ name = "gw-utils" version = "1.8.0-rc3" dependencies = [ "anyhow", + "ckb-chain-spec", "ckb-crypto", "ckb-types", "ethabi", @@ -3675,7 +3676,7 @@ checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ "once_cell", "thiserror", - "toml", + "toml 0.5.9", ] [[package]] @@ -4971,6 +4972,18 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772c1426ab886e7362aedf4abc9c0d1348a979517efedfc25862944d10137af0" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.1" diff --git a/crates/block-producer/src/block_producer.rs b/crates/block-producer/src/block_producer.rs index 8a5cf9923..8161fa69a 100644 --- a/crates/block-producer/src/block_producer.rs +++ b/crates/block-producer/src/block_producer.rs @@ -22,7 +22,7 @@ use gw_types::{ h256::*, offchain::{global_state_from_slice, CompatibleFinalizedTimepoint, DepositInfo, InputCellInfo}, packed::{ - CellDep, CellInput, CellOutput, GlobalState, L2Block, RollupAction, RollupActionUnion, + CellDep, CellOutput, GlobalState, L2Block, RollupAction, RollupActionUnion, RollupSubmitBlock, Script, Transaction, WithdrawalRequestExtra, WitnessArgs, }, prelude::*, @@ -225,13 +225,10 @@ impl BlockProducer { .map(|d| d.clone().into()); // rollup cell - tx_skeleton.inputs_mut().push(InputCellInfo { - input: CellInput::new_builder() - .previous_output(rollup_cell.out_point.clone()) - .since(since.as_u64().pack()) - .build(), - cell: rollup_cell.clone(), - }); + tx_skeleton.inputs_mut().push(InputCellInfo::with_since( + rollup_cell.clone(), + since.as_u64(), + )); // rollup deps tx_skeleton.cell_deps_mut().extend(rollup_deps); // deposit lock dep @@ -353,13 +350,7 @@ impl BlockProducer { // deposit cells for deposit in &deposit_cells { log::info!("using deposit cell {:?}", deposit.cell.out_point); - let input = CellInput::new_builder() - .previous_output(deposit.cell.out_point.clone()) - .build(); - tx_skeleton.inputs_mut().push(InputCellInfo { - input, - cell: deposit.cell.clone(), - }); + tx_skeleton.inputs_mut().push(deposit.cell.clone().into()); } // custodian cells diff --git a/crates/block-producer/src/challenger.rs b/crates/block-producer/src/challenger.rs index 43663cd4a..f3cc19d7f 100644 --- a/crates/block-producer/src/challenger.rs +++ b/crates/block-producer/src/challenger.rs @@ -33,8 +33,8 @@ use gw_types::{ h256::*, offchain::{global_state_from_slice, CellInfo, InputCellInfo}, packed::{ - CellDep, CellInput, CellOutput, ChallengeLockArgs, ChallengeLockArgsReader, - ChallengeTarget, GlobalState, OutPoint, Script, Transaction, WitnessArgs, + CellDep, CellOutput, ChallengeLockArgs, ChallengeLockArgsReader, ChallengeTarget, + GlobalState, OutPoint, Script, Transaction, WitnessArgs, }, prelude::*, }; @@ -350,7 +350,7 @@ impl Challenger { .with_context(|| format!("waiting for tx proposed 0x{}", to_hex(&verifier_tx_hash)))??; // Build cancellation transaction - let challenge_input = to_input_cell_info(challenge_cell); + let challenge_input = challenge_cell.into(); let verifier_context = { let contracts_dep = self.contracts_dep_manager.load(); let cell_dep = cancel_output.verifier_dep(&contracts_dep)?; @@ -523,13 +523,13 @@ impl Challenger { tx_skeleton.witnesses_mut().push(rollup_witness); // Challenge - let challenge_input = to_input_cell_info_with_since(challenge_cell, since); + let challenge_input = InputCellInfo::with_since(challenge_cell, since); let challenge_dep = contracts_dep.challenge_cell_lock.clone().into(); tx_skeleton.cell_deps_mut().push(challenge_dep); tx_skeleton.inputs_mut().push(challenge_input); // Stake - let stake_inputs = stake_cells.into_iter().map(to_input_cell_info); + let stake_inputs = stake_cells.into_iter().map(Into::into); let stake_dep = contracts_dep.stake_cell_lock.clone().into(); tx_skeleton.cell_deps_mut().push(stake_dep); tx_skeleton.inputs_mut().extend(stake_inputs); @@ -722,7 +722,7 @@ impl Challenger { let owner_lock = self.wallet.lock_script().to_owned(); if let Ok(Some(cell)) = rpc_client.query_owner_cell(owner_lock, spent_inputs).await { - return Ok(to_input_cell_info(cell)); + return Ok(cell.into()); } log::debug!("can't find a owner cell for verifier, try wait verifier tx committed"); @@ -739,7 +739,7 @@ impl Challenger { query.ok_or_else(|| anyhow!("can't find an owner cell for verifier"))? }; - Ok(to_input_cell_info(cell)) + Ok(cell.into()) } async fn dry_run_transaction(&self, tx: &Transaction, action: &str) -> Result<()> { @@ -784,7 +784,7 @@ impl RollupState { } fn rollup_input(&self) -> InputCellInfo { - to_input_cell_info(self.rollup_cell.clone()) + self.rollup_cell.clone().into() } fn rollup_output(&self) -> CellOutput { @@ -852,25 +852,6 @@ fn to_tip_number(event: &ChainEvent) -> u64 { tip_block.header().raw().number().unpack() } -fn to_input_cell_info(cell_info: CellInfo) -> InputCellInfo { - InputCellInfo { - input: CellInput::new_builder() - .previous_output(cell_info.out_point.clone()) - .build(), - cell: cell_info, - } -} - -fn to_input_cell_info_with_since(cell_info: CellInfo, since: u64) -> InputCellInfo { - InputCellInfo { - input: CellInput::new_builder() - .previous_output(cell_info.out_point.clone()) - .since(since.pack()) - .build(), - cell: cell_info, - } -} - fn to_hex(hash: &H256) -> String { hex::encode(hash.as_slice()) } diff --git a/crates/block-producer/src/cleaner.rs b/crates/block-producer/src/cleaner.rs index 71e55fab8..7888d5cfc 100644 --- a/crates/block-producer/src/cleaner.rs +++ b/crates/block-producer/src/cleaner.rs @@ -4,13 +4,12 @@ use gw_utils::transaction_skeleton::TransactionSkeleton; use gw_utils::{fee::fill_tx_fee, wallet::Wallet}; use anyhow::{anyhow, Result}; -use ckb_types::prelude::{Builder, Entity}; use gw_challenge::cancel_challenge::RecoverAccountsContext; use gw_rpc_client::rpc_client::RPCClient; use gw_types::core::Status; use gw_types::h256::*; -use gw_types::offchain::{global_state_from_slice, CellInfo, InputCellInfo}; -use gw_types::packed::{CellDep, CellInput, Transaction, WitnessArgs}; +use gw_types::offchain::{global_state_from_slice, InputCellInfo}; +use gw_types::packed::{CellDep, Transaction, WitnessArgs}; use gw_types::prelude::Unpack; use tracing::instrument; @@ -237,9 +236,7 @@ impl Cleaner { let owner_lock_dep = self.ckb_genesis_info.sighash_dep(); tx_skeleton.cell_deps_mut().push(owner_lock_dep); - tx_skeleton - .inputs_mut() - .push(to_input_cell_info(owner_input)); + tx_skeleton.inputs_mut().push(owner_input.into()); let owner_lock = self.wallet.lock_script().to_owned(); fill_tx_fee( @@ -252,12 +249,3 @@ impl Cleaner { self.wallet.sign_tx_skeleton(tx_skeleton) } } - -fn to_input_cell_info(cell_info: CellInfo) -> InputCellInfo { - InputCellInfo { - input: CellInput::new_builder() - .previous_output(cell_info.out_point.clone()) - .build(), - cell: cell_info, - } -} diff --git a/crates/block-producer/src/deposit.rs b/crates/block-producer/src/deposit.rs index 284abe4e6..ba57bb61d 100644 --- a/crates/block-producer/src/deposit.rs +++ b/crates/block-producer/src/deposit.rs @@ -4,8 +4,7 @@ use gw_types::bytes::Bytes; use gw_types::core::ScriptHashType; use gw_types::offchain::{CellInfo, InputCellInfo}; use gw_types::packed::{ - CellDep, CellInput, CellOutput, CustodianLockArgs, Script, UnlockCustodianViaRevertWitness, - WitnessArgs, + CellDep, CellOutput, CustodianLockArgs, Script, UnlockCustodianViaRevertWitness, WitnessArgs, }; use gw_types::prelude::*; use gw_utils::RollupContext; @@ -56,16 +55,7 @@ pub fn revert( output_builder.lock(deposit_lock.clone()).build() }; - let custodian_input = { - let input = CellInput::new_builder() - .previous_output(revert_custodian.out_point.clone()) - .build(); - - InputCellInfo { - input, - cell: revert_custodian.clone(), - } - }; + let custodian_input = InputCellInfo::from(revert_custodian.clone()); let unlock_custodian_witness = UnlockCustodianViaRevertWitness::new_builder() .deposit_lock_hash(deposit_lock.hash().pack()) diff --git a/crates/block-producer/src/runner.rs b/crates/block-producer/src/runner.rs index 379f03f84..10dae81e2 100644 --- a/crates/block-producer/src/runner.rs +++ b/crates/block-producer/src/runner.rs @@ -1067,21 +1067,6 @@ fn check_locks( )); } - // check wallet lock - let opt_wallet_config = block_producer_config.wallet_config.as_ref(); - if let Some(block_producer_wallet_lock) = opt_wallet_config.map(|c| &c.lock) { - if zeros == block_producer_wallet_lock.code_hash { - bail!("[block_producer.wallet.lock.code_hash] shouldn't be zero"); - } - - let challenger_rewards_receiver_lock = &block_producer_config - .challenger_config - .rewards_receiver_lock; - if block_producer_wallet_lock == challenger_rewards_receiver_lock { - bail!("[block_producer.challenger.rewards_receiver_lock] and [block_producer.wallet.lock] have the same address, which is not recommended"); - } - } - Ok(()) } diff --git a/crates/block-producer/src/stake.rs b/crates/block-producer/src/stake.rs index e6ff36eae..a2c6adbf3 100644 --- a/crates/block-producer/src/stake.rs +++ b/crates/block-producer/src/stake.rs @@ -11,7 +11,7 @@ use gw_types::offchain::CompatibleFinalizedTimepoint; use gw_types::{ core::ScriptHashType, offchain::{CellInfo, InputCellInfo}, - packed::{CellDep, CellInput, CellOutput, L2Block, Script, StakeLockArgs, StakeLockArgsReader}, + packed::{CellDep, CellOutput, L2Block, Script, StakeLockArgs, StakeLockArgsReader}, prelude::*, }; use gw_utils::local_cells::{ @@ -81,16 +81,9 @@ pub async fn generate( .lock(lock) .build(); - let input_unlocked_stake = InputCellInfo { - input: CellInput::new_builder() - .previous_output(unlocked_stake.out_point.clone()) - .build(), - cell: unlocked_stake, - }; - let generated_stake = GeneratedStake { deps: vec![stake_lock_dep.into()], - inputs: vec![input_unlocked_stake], + inputs: vec![unlocked_stake.into()], output: stake_cell, output_data: Bytes::new(), }; diff --git a/crates/block-producer/src/withdrawal.rs b/crates/block-producer/src/withdrawal.rs index 5c64078c4..6ebb3a450 100644 --- a/crates/block-producer/src/withdrawal.rs +++ b/crates/block-producer/src/withdrawal.rs @@ -12,7 +12,7 @@ use gw_types::{ core::{DepType, ScriptHashType}, offchain::{global_state_from_slice, CellInfo, CollectedCustodianCells, InputCellInfo}, packed::{ - CellDep, CellInput, CellOutput, CustodianLockArgs, DepositLockArgs, L2Block, Script, + CellDep, CellOutput, CustodianLockArgs, DepositLockArgs, L2Block, Script, UnlockWithdrawalViaFinalize, UnlockWithdrawalViaRevert, UnlockWithdrawalWitness, UnlockWithdrawalWitnessUnion, WithdrawalRequestExtra, WitnessArgs, }, @@ -67,16 +67,9 @@ pub fn generate( cell_deps.push(sudt_type_dep.into()); } - let custodian_inputs = cells_info.into_iter().map(|cell| { - let input = CellInput::new_builder() - .previous_output(cell.out_point.clone()) - .build(); - InputCellInfo { input, cell } - }); - let generated_withdrawals = GeneratedWithdrawals { deps: cell_deps, - inputs: custodian_inputs.collect(), + inputs: cells_info.into_iter().map(Into::into).collect(), outputs: generator.finish(), }; @@ -143,17 +136,6 @@ pub fn revert( output_builder.lock(custodian_lock.clone()).build() }; - let withdrawal_input = { - let input = CellInput::new_builder() - .previous_output(withdrawal.out_point.clone()) - .build(); - - InputCellInfo { - input, - cell: withdrawal.clone(), - } - }; - let unlock_withdrawal_witness = { let unlock_withdrawal_via_revert = UnlockWithdrawalViaRevert::new_builder() .custodian_lock_hash(custodian_lock.hash().pack()) @@ -169,9 +151,9 @@ pub fn revert( .lock(Some(unlock_withdrawal_witness.as_bytes()).pack()) .build(); - withdrawal_inputs.push(withdrawal_input); - withdrawal_witness.push(withdrawal_witness_args); custodian_outputs.push((custodian_output, withdrawal.data.clone())); + withdrawal_inputs.push(InputCellInfo::from(withdrawal)); + withdrawal_witness.push(withdrawal_witness_args); } let withdrawal_lock_dep = contracts_dep.withdrawal_cell_lock.clone(); @@ -260,17 +242,8 @@ pub fn unlock_to_owner( } }; - let withdrawal_input = { - let input = CellInput::new_builder() - .previous_output(withdrawal_cell.out_point.clone()) - .since(global_state_since.pack()) - .build(); - - InputCellInfo { - input, - cell: withdrawal_cell.clone(), - } - }; + let withdrawal_input = + InputCellInfo::with_since(withdrawal_cell.clone(), global_state_since); // Switch to owner lock let output = withdrawal_cell.output.as_builder().lock(owner_lock).build(); @@ -350,8 +323,8 @@ mod test { CellInfo, CollectedCustodianCells, CompatibleFinalizedTimepoint, InputCellInfo, }; use gw_types::packed::{ - BlockMerkleState, CellDep, CellInput, CellOutput, GlobalState, L2Block, OutPoint, - RawL2Block, RawWithdrawalRequest, RollupConfig, Script, UnlockWithdrawalViaFinalize, + BlockMerkleState, CellDep, CellOutput, GlobalState, L2Block, OutPoint, RawL2Block, + RawWithdrawalRequest, RollupConfig, Script, UnlockWithdrawalViaFinalize, UnlockWithdrawalWitness, UnlockWithdrawalWitnessUnion, WithdrawalLockArgs, WithdrawalRequest, WithdrawalRequestExtra, WitnessArgs, }; @@ -592,16 +565,7 @@ mod test { assert_eq!(expected_output.as_slice(), output.as_slice()); assert_eq!(withdrawal_with_owner_lock.data, data); - let expected_input = { - let input = CellInput::new_builder() - .previous_output(withdrawal_with_owner_lock.out_point.clone()) - .build(); - - InputCellInfo { - input, - cell: withdrawal_with_owner_lock, - } - }; + let expected_input = InputCellInfo::from(withdrawal_with_owner_lock); let input = unlocked.inputs.first().unwrap().to_owned(); assert_eq!(expected_input.input.as_slice(), input.input.as_slice()); assert_eq!( diff --git a/crates/challenge/src/cancel_challenge.rs b/crates/challenge/src/cancel_challenge.rs index 87eb4164f..29fee5f5a 100644 --- a/crates/challenge/src/cancel_challenge.rs +++ b/crates/challenge/src/cancel_challenge.rs @@ -9,8 +9,8 @@ use gw_types::{ offchain::{CellInfo, InputCellInfo, RecoverAccount}, packed::{ CCTransactionSignatureWitness, CCTransactionWitness, CCWithdrawalWitness, CellDep, - CellInput, CellOutput, GlobalState, OutPoint, RollupAction, RollupActionUnion, - RollupCancelChallenge, Script, WitnessArgs, + CellOutput, GlobalState, OutPoint, RollupAction, RollupActionUnion, RollupCancelChallenge, + Script, WitnessArgs, }, prelude::{Builder, CalcHash, Entity, OccupiedCapacityBytes, Pack, Unpack}, }; @@ -75,17 +75,12 @@ impl CancelChallengeOutput { .index(tx_index.pack()) .build(); - let input = CellInput::new_builder() - .previous_output(out_point.clone()) - .build(); - - let cell = CellInfo { + CellInfo { out_point, output, data, - }; - - InputCellInfo { input, cell } + } + .into() } pub fn verifier_dep(&self, contracts_dep: &ContractsCellDep) -> Result { @@ -258,19 +253,13 @@ impl LoadData { .dep_type(DepType::Code.into()) .build(); - let input = CellInput::new_builder() - .previous_output(out_point.clone()) - .build(); - let cell = CellInfo { out_point, output, data, }; - let cell_info = InputCellInfo { input, cell }; - - (cell_dep, cell_info) + (cell_dep, cell.into()) }; let (cell_deps, inputs) = { @@ -319,17 +308,12 @@ impl RecoverAccounts { .index((idx as u32).pack()) .build(); - let input = CellInput::new_builder() - .previous_output(out_point.clone()) - .build(); - - let cell = CellInfo { + CellInfo { out_point, output, data, - }; - - InputCellInfo { input, cell } + } + .into() }; let inputs = { diff --git a/crates/challenge/src/offchain.rs b/crates/challenge/src/offchain.rs index 5bbac0639..78abceb3a 100644 --- a/crates/challenge/src/offchain.rs +++ b/crates/challenge/src/offchain.rs @@ -10,10 +10,8 @@ use gw_store::state::MemStateDB; use gw_store::transaction::StoreTransaction; use gw_types::core::DepType; use gw_types::h256::*; -use gw_types::offchain::{CellInfo, InputCellInfo}; -use gw_types::packed::{ - CellDep, CellInput, L2Block, OutPoint, OutPointVec, WithdrawalRequestExtra, -}; +use gw_types::offchain::InputCellInfo; +use gw_types::packed::{CellDep, L2Block, OutPoint, OutPointVec, WithdrawalRequestExtra}; use gw_types::prelude::*; use gw_utils::wallet::Wallet; use gw_utils::RollupContext; @@ -80,9 +78,11 @@ impl OffChainMockContext { contracts_dep_manager, } = args; - let rollup_cell = { + let rollup_cell: InputCellInfo = { let query = rpc_client.query_rollup_cell().await?; - into_input_cell_info(query.ok_or_else(|| anyhow!("can't found rollup cell"))?) + query + .ok_or_else(|| anyhow!("can't found rollup cell"))? + .into() }; let median_time = { @@ -581,7 +581,7 @@ async fn resolve_cell_deps( .and_then(|q| q.cell) .ok_or_else(|| anyhow!("can't find dep cell"))? }; - resolved_deps.push(into_input_cell_info(dep_cell)); + resolved_deps.push(dep_cell.into()); } Ok(resolved_deps) @@ -614,15 +614,6 @@ async fn resolve_dep_group(rpc_client: &RPCClient, dep: &CellDep) -> Result InputCellInfo { - InputCellInfo { - input: CellInput::new_builder() - .previous_output(cell_info.out_point.clone()) - .build(), - cell: cell_info, - } -} - impl From for TxWithContext { fn from(output: MockOutput) -> Self { TxWithContext { diff --git a/crates/challenge/src/offchain/mock_tx.rs b/crates/challenge/src/offchain/mock_tx.rs index c5ad786be..1f1d18234 100644 --- a/crates/challenge/src/offchain/mock_tx.rs +++ b/crates/challenge/src/offchain/mock_tx.rs @@ -16,8 +16,8 @@ use gw_types::{ h256::*, offchain::{CellInfo, InputCellInfo}, packed::{ - Byte32, CellDep, CellInput, CellOutput, ChallengeTarget, ChallengeWitness, GlobalState, - OutPoint, Script, ScriptOpt, Transaction, WitnessArgs, + Byte32, CellDep, CellOutput, ChallengeTarget, ChallengeWitness, GlobalState, OutPoint, + Script, ScriptOpt, Transaction, WitnessArgs, }, prelude::*, }; @@ -260,22 +260,17 @@ impl MockRollup { .index(0u32.pack()) .build(); - let input = CellInput::new_builder() - .previous_output(out_point.clone()) - .build(); - let output = CellOutput::new_builder() .capacity((u64::max_value() / 2).pack()) .lock(self.wallet.lock_script().to_owned()) .build(); - let cell = CellInfo { + CellInfo { out_point, output, data: Bytes::new(), - }; - - InputCellInfo { input, cell } + } + .into() } fn mock_rollup_cell(&self, global_state: GlobalState, lock: Script) -> InputCellInfo { @@ -284,10 +279,6 @@ impl MockRollup { .index(0u32.pack()) .build(); - let input = CellInput::new_builder() - .previous_output(out_point.clone()) - .build(); - let output = { let rollup_output = CellOutput::new_builder() .capacity(100_000_000u64.pack()) @@ -302,13 +293,12 @@ impl MockRollup { rollup_output.as_builder().capacity(capacity.pack()).build() }; - let cell = CellInfo { + CellInfo { out_point, output, data: global_state.as_bytes(), - }; - - InputCellInfo { input, cell } + } + .into() } fn mock_challenge_cell(&self, target: ChallengeTarget) -> InputCellInfo { @@ -334,19 +324,14 @@ impl MockRollup { .index(0u32.pack()) .build(); - let input = CellInput::new_builder() - .previous_output(out_point.clone()) - .build(); - let (output, data) = challenge_output.challenge_cell; - let cell = CellInfo { + CellInfo { out_point, output, data, - }; - - InputCellInfo { input, cell } + } + .into() } fn fill_tx_fee( diff --git a/crates/config/src/config.rs b/crates/config/src/config.rs index 85e3ddfcf..f102cd177 100644 --- a/crates/config/src/config.rs +++ b/crates/config/src/config.rs @@ -97,7 +97,6 @@ pub struct RPCRateLimit { #[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct WalletConfig { pub privkey_path: PathBuf, - pub lock: Script, } // NOTE: Rewards receiver lock must be different than lock in WalletConfig, diff --git a/crates/tests/src/tests/unlock_withdrawal_to_owner.rs b/crates/tests/src/tests/unlock_withdrawal_to_owner.rs index c8f8b36ab..e60b09459 100644 --- a/crates/tests/src/tests/unlock_withdrawal_to_owner.rs +++ b/crates/tests/src/tests/unlock_withdrawal_to_owner.rs @@ -30,7 +30,7 @@ use gw_types::offchain::{ InputCellInfo, }; use gw_types::packed::{ - self, AllowedTypeHash, BlockMerkleState, CellDep, CellInput, CellOutput, CustodianLockArgs, + self, AllowedTypeHash, BlockMerkleState, CellDep, CellOutput, CustodianLockArgs, DepositRequest, GlobalState, OutPoint, RawWithdrawalRequest, RollupAction, RollupActionUnion, RollupConfig, RollupSubmitBlock, Script, StakeLockArgs, WithdrawalRequest, WithdrawalRequestExtra, WitnessArgs, @@ -447,11 +447,11 @@ async fn test_build_unlock_to_owner_tx() { }; let input_cell_deps = vec![ - into_input_cell(always_cell.clone()), - into_input_cell(stake_lock_cell.clone()), - into_input_cell(state_validator_cell.clone()), - into_input_cell(custodian_lock_cell), - into_input_cell(rollup_config_cell.clone()), + InputCellInfo::from(always_cell.clone()), + stake_lock_cell.clone().into(), + state_validator_cell.clone().into(), + custodian_lock_cell.into(), + rollup_config_cell.clone().into(), ]; let cell_deps = { let deps = input_cell_deps.iter(); @@ -470,9 +470,9 @@ async fn test_build_unlock_to_owner_tx() { SINCE_BLOCK_TIMESTAMP_FLAG | input_timestamp }; let inputs = vec![ - into_input_cell_since(input_rollup_cell, block_since), - into_input_cell(input_stake_cell.clone()), - into_input_cell(input_custodian_cell), + InputCellInfo::with_since(input_rollup_cell, block_since), + input_stake_cell.clone().into(), + input_custodian_cell.clone().into(), ]; let mut outputs = vec![output_rollup_cell, output_stake]; outputs.extend(generated_withdrawals.outputs.clone()); @@ -512,10 +512,10 @@ async fn test_build_unlock_to_owner_tx() { withdrawals: random_withdrawal_cells.clone(), }; let cell_deps = vec![ - into_input_cell(rollup_config_cell.clone()), - into_input_cell(rollup_cell.clone()), - into_input_cell(always_cell.clone()), - into_input_cell(withdrawal_lock_cell.clone()), + rollup_config_cell.clone().into(), + rollup_cell.clone().into(), + always_cell.clone().into(), + withdrawal_lock_cell.clone().into(), ]; let unlocked = Default::default(); @@ -526,10 +526,11 @@ async fn test_build_unlock_to_owner_tx() { .expect("skip no owner lock"); assert_eq!(to_unlock.len(), accounts.len()); - let inputs = { - let cells = random_withdrawal_cells.clone().into_iter(); - cells.map(into_input_cell).collect() - }; + let inputs = random_withdrawal_cells + .iter() + .cloned() + .map(Into::into) + .collect(); let tx_with_context = TxWithContext { tx, cell_deps: cell_deps.clone(), @@ -565,9 +566,9 @@ async fn test_build_unlock_to_owner_tx() { .expect("some withdrawals tx"); let inputs = unlockable_random_withdrawals - .clone() - .into_iter() - .map(into_input_cell) + .iter() + .cloned() + .map(Into::into) .collect(); let tx_with_context = TxWithContext { @@ -689,11 +690,11 @@ async fn test_build_unlock_to_owner_tx() { .expect("one custodian"); let input_cell_deps = vec![ - into_input_cell(always_cell), - into_input_cell(stake_lock_cell), - into_input_cell(state_validator_cell), - into_input_cell(withdrawal_lock_cell), - into_input_cell(rollup_config_cell), + InputCellInfo::from(always_cell), + stake_lock_cell.into(), + state_validator_cell.into(), + withdrawal_lock_cell.into(), + rollup_config_cell.into(), ]; let cell_deps = { let deps = input_cell_deps.iter(); @@ -710,8 +711,8 @@ async fn test_build_unlock_to_owner_tx() { SINCE_BLOCK_TIMESTAMP_FLAG | input_timestamp }; let mut inputs = vec![ - into_input_cell_since(input_rollup_cell, block_since), - into_input_cell(input_stake_cell), + InputCellInfo::with_since(input_rollup_cell, block_since), + input_stake_cell.into(), ]; inputs.extend(reverted_withdrawals.inputs); @@ -794,25 +795,6 @@ impl BuildUnlockWithdrawalToOwner for DummyUnlocker { } } -fn into_input_cell(cell: CellInfo) -> InputCellInfo { - InputCellInfo { - input: CellInput::new_builder() - .previous_output(cell.out_point.clone()) - .build(), - cell, - } -} - -fn into_input_cell_since(cell: CellInfo, since: u64) -> InputCellInfo { - InputCellInfo { - input: CellInput::new_builder() - .previous_output(cell.out_point.clone()) - .since(since.pack()) - .build(), - cell, - } -} - fn random_always_success_script(opt_rollup_script_hash: Option<&H256>) -> Script { let random_bytes: [u8; 20] = rand::random(); Script::new_builder() diff --git a/crates/tools/Cargo.toml b/crates/tools/Cargo.toml index 173fa7e6d..85e071bd2 100644 --- a/crates/tools/Cargo.toml +++ b/crates/tools/Cargo.toml @@ -13,7 +13,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" anyhow = "1.0" thiserror = "1.0" -toml_edit = { version = "0.19.1", features = ["serde"] } tempfile = "3.1" lazy_static = "1.4" secp256k1 = "0.24" @@ -46,8 +45,11 @@ csv = "1.1.6" bech32 = "0.8.1" tracing-subscriber = { version = "0.3.11", default-features = false, features = ["tracing-log", "env-filter", "fmt"] } parking_lot = "0.12" +toml = "0.7.1" [dev-dependencies] ckb-chain-spec = "0.105.1" ckb-script = "0.105.1" ckb-mock-tx-types = "0.105.1" + +[features] diff --git a/crates/tools/src/create_creator_account.rs b/crates/tools/src/create_creator_account.rs index a8af4a946..0ee42b5ac 100644 --- a/crates/tools/src/create_creator_account.rs +++ b/crates/tools/src/create_creator_account.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use anyhow::{anyhow, Result}; use ckb_jsonrpc_types::JsonBytes; use ckb_types::prelude::{Builder, Entity}; @@ -7,8 +9,8 @@ use gw_generator::account_lock_manage::eip712::{self, traits::EIP712Encode}; use gw_types::{ core::ScriptHashType, packed::{CreateAccount, Fee, L2Transaction, MetaContractArgs, RawL2Transaction, Script}, + prelude::*, }; -use std::path::Path; use crate::{ account::{eth_sign, privkey_to_eth_address, privkey_to_l2_script_hash, read_privkey}, @@ -16,7 +18,6 @@ use crate::{ types::ScriptsDeploymentResult, utils::transaction::{read_config, wait_for_l2_tx}, }; -use gw_types::{bytes::Bytes as GwBytes, prelude::*}; pub async fn create_creator_account( godwoken_rpc_url: &str, @@ -57,7 +58,7 @@ pub async fn create_creator_account( let mut l2_args_vec = rollup_type_hash.as_bytes().to_vec(); l2_args_vec.append(&mut sudt_id.to_le_bytes().to_vec()); - let l2_script_args = Pack::pack(&GwBytes::from(l2_args_vec)); + let l2_script_args = l2_args_vec.pack(); let l2_script = Script::new_builder() .code_hash(polyjuice_validator_script_hash.pack()) .hash_type(ScriptHashType::Type.into()) diff --git a/crates/tools/src/deploy_genesis.rs b/crates/tools/src/deploy_genesis.rs index c74c89ee6..5aee75ebb 100644 --- a/crates/tools/src/deploy_genesis.rs +++ b/crates/tools/src/deploy_genesis.rs @@ -3,178 +3,38 @@ use std::{ iter::FromIterator, ops::Sub, path::Path, - str::FromStr, time::{SystemTime, UNIX_EPOCH}, }; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, ensure, Context, Result}; use ckb_fixed_hash::H256; -use ckb_hash::new_blake2b; -use ckb_jsonrpc_types as rpc_types; use ckb_resource::CODE_HASH_SECP256K1_DATA; -use ckb_types::{ - bytes::Bytes, - core::{BlockView, Capacity, DepType, ScriptHashType, TransactionBuilder, TransactionView}, - packed as ckb_packed, -}; use gw_config::GenesisConfig; use gw_generator::genesis::build_genesis; use gw_jsonrpc_types::JsonCalcHash; use gw_rpc_client::ckb_client::CkbClient; use gw_types::{ - core::{AllowedContractType, AllowedEoaType}, - packed as gw_packed, - packed::RollupConfig, - prelude::{Pack as CKBPack, Unpack as CKBUnpack, *}, + bytes::Bytes, + core::{AllowedContractType, AllowedEoaType, DepType, ScriptHashType}, + packed, + prelude::*, +}; +use gw_utils::{ + fee::collect_payment_cells, + local_cells::LocalCellsManager, + transaction_skeleton::TransactionSkeleton, + type_id::{type_id_args, type_id_type_script}, }; -use tempfile::NamedTempFile; use crate::{ - sdk::constants::TYPE_ID_CODE_HASH, types::{RollupDeploymentResult, ScriptsDeploymentResult, UserRollupConfig}, - utils::{ - sdk::{ - constants::{MIN_SECP_CELL_CAPACITY, ONE_CKB}, - traits::DefaultCellDepResolver, - util::get_max_mature_number, - Address, AddressPayload, HumanCapacity, - }, - transaction::{get_network_type, run_cmd}, - }, + utils::deploy::DeployContextArgs, }; -struct GenesisInfo(DefaultCellDepResolver); - -struct DeployContext<'a> { - privkey_path: &'a Path, - owner_address: &'a Address, - genesis_info: &'a GenesisInfo, - deployment_result: &'a ScriptsDeploymentResult, - ckb_client: &'a CkbClient, -} - -impl<'a> DeployContext<'a> { - async fn deploy( - &mut self, - mut outputs: Vec, - mut outputs_data: Vec, - mut deps: Vec, - first_cell_input: Option<&ckb_packed::CellInput>, - witness_0: ckb_packed::WitnessArgs, - ) -> Result { - let tx_fee = ONE_CKB; - let total_output_capacity: u64 = outputs - .iter() - .map(|output| { - let value: u64 = CKBUnpack::unpack(&output.capacity()); - value - }) - .sum(); - let total_capacity = total_output_capacity + tx_fee; - let tip_number = self.ckb_client.get_tip_block_number().await?; - let max_mature_number = get_max_mature_number(self.ckb_client).await?; - let (inputs, total_input_capacity) = collect_live_cells( - self.ckb_client.url(), - self.owner_address.to_string().as_str(), - max_mature_number, - tip_number.into(), - total_capacity, - )?; - if let Some(first_input) = first_cell_input { - if inputs[0].as_slice() != first_input.as_slice() { - return Err(anyhow!("first input cell changed")); - } - } - // collect_live_cells will ensure `total_input_capacity >= total_capacity`. - let rest_capacity = total_input_capacity - total_capacity; - let max_tx_fee_str = if rest_capacity >= MIN_SECP_CELL_CAPACITY { - outputs.push( - ckb_packed::CellOutput::new_builder() - .lock(ckb_packed::Script::from(self.owner_address.payload())) - .capacity(CKBPack::pack(&rest_capacity)) - .build(), - ); - outputs_data.push(Default::default()); - "1.0" - } else { - "62.0" - }; - let outputs_data: Vec = outputs_data.iter().map(CKBPack::pack).collect(); - deps.extend_from_slice(&[ - self.deployment_result - .state_validator - .cell_dep - .clone() - .into(), - self.genesis_info - .0 - .sighash_dep() - .context("genesis sighash dep")? - .0 - .to_owned(), - ]); - let tx: TransactionView = TransactionBuilder::default() - .cell_deps(deps) - .set_inputs(inputs) - .set_outputs(outputs) - .set_outputs_data(outputs_data) - .set_witnesses(vec![CKBPack::pack(&witness_0.as_bytes())]) - .build(); - - // 7. build ckb-cli tx and sign - let tx_file = NamedTempFile::new()?; - let tx_path_str = tx_file.path().to_str().unwrap(); - let _output = run_cmd(&[ - "--url", - self.ckb_client.url(), - "tx", - "init", - "--tx-file", - tx_path_str, - ])?; - let tx_json = rpc_types::Transaction::from(tx.data()); - let tx_body: serde_json::Value = serde_json::to_value(&tx_json).unwrap(); - let cli_tx_content = std::fs::read_to_string(tx_path_str).unwrap(); - let mut cli_tx: serde_json::Value = serde_json::from_str(&cli_tx_content).unwrap(); - cli_tx["transaction"] = tx_body; - let cli_tx_content = serde_json::to_string_pretty(&cli_tx).unwrap(); - std::fs::write(tx_path_str, cli_tx_content.as_bytes())?; - let _output = run_cmd(&[ - "--url", - self.ckb_client.url(), - "tx", - "sign-inputs", - "--privkey-path", - self.privkey_path.to_str().expect("non-utf8 file path"), - "--tx-file", - tx_path_str, - "--add-signatures", - ])?; - - // 8. send and then wait for tx - let send_output = run_cmd(&[ - "--url", - self.ckb_client.url(), - "tx", - "send", - "--tx-file", - tx_path_str, - "--max-tx-fee", - max_tx_fee_str, - "--skip-check", - ])?; - let tx_hash = H256::from_str(send_output.trim().trim_start_matches("0x"))?; - log::info!("tx_hash: {:#x}", tx_hash); - self.ckb_client - .wait_tx_committed_with_timeout_and_logging(tx_hash.0, 600) - .await?; - Ok(tx_hash) - } -} - pub struct DeployRollupCellArgs<'a> { pub privkey_path: &'a Path, pub ckb_rpc_url: &'a str, + pub ckb_indexer_rpc_url: Option<&'a str>, pub scripts_result: &'a ScriptsDeploymentResult, pub user_rollup_config: &'a UserRollupConfig, pub timestamp: Option, @@ -185,78 +45,55 @@ pub async fn deploy_rollup_cell(args: DeployRollupCellArgs<'_>) -> Result = { - let meta_hash = Pack::pack(&scripts_result.meta_contract_validator.script_type_hash); - let meta = gw_packed::AllowedTypeHash::new_builder() + let allowed_contract_type_hashes: Vec = { + let meta = packed::AllowedTypeHash::new_builder() .type_(AllowedContractType::Meta.into()) - .hash(meta_hash) + .hash(r.meta_contract_validator.script_type_hash.pack()) .build(); - - let sudt_hash = Pack::pack(&scripts_result.l2_sudt_validator.script_type_hash); - let sudt = gw_packed::AllowedTypeHash::new_builder() + let sudt = packed::AllowedTypeHash::new_builder() .type_(AllowedContractType::Sudt.into()) - .hash(sudt_hash) + .hash(r.l2_sudt_validator.script_type_hash.pack()) .build(); - - let polyjuice_hash = Pack::pack(&scripts_result.polyjuice_validator.script_type_hash); - let polyjuice = gw_packed::AllowedTypeHash::new_builder() + let polyjuice = packed::AllowedTypeHash::new_builder() .type_(AllowedContractType::Polyjuice.into()) - .hash(polyjuice_hash) + .hash(r.polyjuice_validator.script_type_hash.pack()) .build(); - let eth_addr_reg_validator_hash = - Pack::pack(&scripts_result.eth_addr_reg_validator.script_type_hash); - let eth_addr_reg_validator = gw_packed::AllowedTypeHash::new_builder() + let eth_addr_reg_validator = packed::AllowedTypeHash::new_builder() .type_(AllowedContractType::EthAddrReg.into()) - .hash(eth_addr_reg_validator_hash) + .hash(r.eth_addr_reg_validator.script_type_hash.pack()) .build(); let mut type_hashes = vec![meta, sudt, polyjuice, eth_addr_reg_validator]; @@ -274,15 +111,15 @@ pub async fn deploy_rollup_cell(args: DeployRollupCellArgs<'_>) -> Result = { - let eth_hash = Pack::pack(&scripts_result.eth_account_lock.script_type_hash); - let eth = gw_packed::AllowedTypeHash::new_builder() + let allowed_eoa_type_hashes: Vec = { + let eth_hash = scripts_result.eth_account_lock.script_type_hash.pack(); + let eth = packed::AllowedTypeHash::new_builder() .type_(AllowedEoaType::Eth.into()) .hash(eth_hash) .build(); @@ -297,68 +134,60 @@ pub async fn deploy_rollup_cell(args: DeployRollupCellArgs<'_>) -> Result) -> Result) -> Result) -> Result Bytes { - let mut blake2b = new_blake2b(); - blake2b.update(first_cell_input.as_slice()); - blake2b.update(&first_output_index.to_le_bytes()); - let mut ret = [0; 32]; - blake2b.finalize(&mut ret); - Bytes::from(ret.to_vec()) -} - -fn fit_output_capacity(output: ckb_packed::CellOutput, data_size: usize) -> ckb_packed::CellOutput { - let data_capacity = Capacity::bytes(data_size).expect("data capacity"); - let capacity = output - .occupied_capacity(data_capacity) - .expect("occupied_capacity"); - output - .as_builder() - .capacity(CKBPack::pack(&capacity.as_u64())) - .build() -} - -fn collect_live_cells( - rpc_client_url: &str, - owner_address_str: &str, - max_mature_number: u64, - tip_number: u64, - total_capacity: u64, -) -> Result<(Vec, u64)> { - let number_step = 10000; - let limit = Some(usize::max_value()); - let mut from_number = 0; - let mut to_number = from_number + number_step - 1; - let mut total_input_capacity = 0; - let mut inputs = Vec::new(); - while total_input_capacity < total_capacity { - if from_number > tip_number { - return Err(anyhow!( - "not enough capacity from {}, expected: {}, found: {}", - owner_address_str, - HumanCapacity(total_capacity), - HumanCapacity(total_input_capacity), - )); - } - let new_cells = get_live_cells( - rpc_client_url, - owner_address_str, - max_mature_number, - Some(from_number), - Some(to_number), - limit, - )?; - for (new_input, new_capacity) in new_cells { - total_input_capacity += new_capacity; - inputs.push(new_input); - if total_input_capacity >= total_capacity { - break; - } - } - from_number += number_step; - to_number += number_step; - } - Ok((inputs, total_input_capacity)) -} - -// NOTE: This is an inefficient way to collect cells -fn get_live_cells( - rpc_client_url: &str, - owner_address_str: &str, - max_mature_number: u64, - from_number: Option, - to_number: Option, - limit: Option, -) -> Result> { - let from_number_string = from_number.map(|value| value.to_string()); - let to_number_string = to_number.map(|value| value.to_string()); - let mut actual_limit = limit.unwrap_or(20); - let mut cells = Vec::new(); - while cells.is_empty() { - let limit_string = actual_limit.to_string(); - // wallet get-live-cells --address {address} --fast-mode --limit {limit} --from {from-number} --to {to-number} - let mut args: Vec<&str> = vec![ - "--output-format", - "json", - "--url", - rpc_client_url, - "wallet", - "get-live-cells", - "--address", - owner_address_str, - "--fast-mode", - ]; - if let Some(from_number) = from_number_string.as_ref() { - args.push("--from"); - args.push(from_number.as_str()); - }; - if let Some(to_number) = to_number_string.as_ref() { - args.push("--to"); - args.push(to_number.as_str()); - }; - args.push("--limit"); - args.push(limit_string.as_str()); - - let live_cells_output = run_cmd(args)?; - let live_cells: serde_json::Value = serde_json::from_str(&live_cells_output)?; - cells = live_cells["live_cells"] - .as_array() - .expect("josn live cells") - .iter() - .filter_map(|live_cell| { - /* - { - "capacity": "1200.0 (CKB)", - "data_bytes": 968, - "index": { - "output_index": 0, - "tx_index": 1 - }, - "lock_hash": "0x1cdeae55a5768fe14b628001c6247ae84c70310a7ddcfdc73ac68494251e46ec", - "mature": true, - "number": 6617, - "output_index": 0, - "tx_hash": "0x0d0d63184973ccdaf2c972783e1ed5f984a3e31b971e3294b092e54fe1d86961", - "type_hashes": null - } - */ - let tx_index = live_cell["index"]["tx_index"] - .as_u64() - .expect("live cell tx_index"); - let number = live_cell["number"].as_u64().expect("live cell number"); - let data_bytes = live_cell["data_bytes"] - .as_u64() - .expect("live cell data_bytes"); - let type_is_null = live_cell["type_hashes"].is_null(); - if !type_is_null - || data_bytes > 0 - || !is_mature(number, tx_index, max_mature_number) - { - log::debug!( - "has type: {}, data not empty: {}, immature: {}, number: {}, tx_index: {}", - !type_is_null, - data_bytes > 0, - !is_mature(number, tx_index, max_mature_number), - number, - tx_index, - ); - return None; - } - - let input_tx_hash = H256::from_str( - live_cell["tx_hash"] - .as_str() - .expect("live cell tx hash") - .trim_start_matches("0x"), - ) - .expect("convert to h256"); - let input_index = live_cell["output_index"] - .as_u64() - .expect("live cell output index") as u32; - let capacity = HumanCapacity::from_str( - live_cell["capacity"] - .as_str() - .expect("live cell capacity") - .split(' ') - .next() - .expect("capacity"), - ) - .map(|human_capacity| human_capacity.0) - .expect("parse capacity"); - let out_point = - ckb_packed::OutPoint::new(CKBPack::pack(&input_tx_hash), input_index); - let input = ckb_packed::CellInput::new(out_point, 0); - Some((input, capacity)) - }) - .collect(); - if actual_limit > u32::max_value() as usize / 2 { - log::debug!("Can not find live cells for {}", owner_address_str); - break; - } - actual_limit *= 2; - } - Ok(cells) -} - -pub fn is_mature(number: u64, tx_index: u64, max_mature_number: u64) -> bool { - // Not cellbase cell - tx_index > 0 - // Live cells in genesis are all mature - || number == 0 - || number <= max_mature_number -} - pub async fn get_secp_data( rpc_client: &CkbClient, ) -> Result<(Bytes, gw_jsonrpc_types::ckb_jsonrpc_types::CellDep)> { diff --git a/crates/tools/src/deploy_scripts.rs b/crates/tools/src/deploy_scripts.rs index 7a9eb0ee4..bcef43d79 100644 --- a/crates/tools/src/deploy_scripts.rs +++ b/crates/tools/src/deploy_scripts.rs @@ -1,305 +1,94 @@ -use crate::utils::sdk::{Address, AddressPayload, HumanCapacity}; -use crate::{ - types::{BuildScriptsResult, DeployItem, Programs, ScriptsDeploymentResult}, - utils::transaction::{get_network_type, run_cmd, TYPE_ID_CODE_HASH}, -}; -use anyhow::{anyhow, Result}; +use std::{collections::HashMap, fs, path::PathBuf}; + +use anyhow::{Context, Result}; use ckb_fixed_hash::H256; -use ckb_jsonrpc_types::{CellDep, DepType, OutPoint, Script}; -use ckb_types::{ - bytes::Bytes, - core::{Capacity, ScriptHashType}, - packed, - prelude::*, -}; -use gw_types::prelude::CalcHash; -use serde::{Deserialize, Serialize}; -use std::fs; -use std::path::Path; -use std::str::FromStr; +use clap::Parser; +use gw_types::{packed, prelude::CalcHash}; +use gw_utils::local_cells::LocalCellsManager; -#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Default)] -struct DeploymentIndex { - pub programs: Programs, - pub lock: Script, -} +use crate::{ + types::{BuildScriptsResult, DeployItem, ScriptsDeploymentResult}, + utils::deploy::{DeployContext, DeployContextArgs}, +}; -pub async fn deploy_program( - privkey_path: &Path, - ckb_rpc_url: &str, - gw_ckb_client: &gw_rpc_client::ckb_client::CkbClient, - binary_path: &Path, - target_lock: &packed::Script, - target_address: &Address, -) -> Result { - log::info!("deploy binary {:?}", binary_path); - let file_size = fs::metadata(binary_path)?.len(); - let min_output_capacity = { - let data_capacity = Capacity::bytes(file_size as usize)?; - let type_script = packed::Script::new_builder() - .code_hash(TYPE_ID_CODE_HASH.pack()) - .hash_type(ScriptHashType::Type.into()) - .args(Bytes::from(vec![0u8; 32]).pack()) - .build(); - let output = packed::CellOutput::new_builder() - .lock(target_lock.clone()) - .type_(Some(type_script).pack()) - .build(); - output.occupied_capacity(data_capacity)?.as_u64() - }; - let capacity_string = HumanCapacity(min_output_capacity).to_string(); - let target_address_string = target_address.to_string(); - let tx_fee_str = "0.1"; +pub const DEPLOY_SCRIPTS_COMMAND: &str = "deploy-scripts"; - /* ckb-cli - --url {ckb_rpc_url} - wallet transfer - --privkey-path {privkey_path} - --to-address {target_address} - --to-data-path {binary_path} - --capacity {capacity?} - --tx-fee {fee?} - --type-id - --skip-check-to-address - */ - log::info!( - "file_size: {} bytes, output cell capacity: {} CKB", - file_size, - capacity_string - ); - let output = run_cmd(vec![ - "--url", - ckb_rpc_url, - "wallet", - "transfer", - "--privkey-path", - privkey_path.to_str().expect("non-utf8 file path"), - "--to-address", - target_address_string.as_str(), - "--to-data-path", - binary_path.to_str().expect("non-utf8 file path"), - "--capacity", - capacity_string.as_str(), - "--tx-fee", - tx_fee_str, - "--type-id", - "--skip-check-to-address", - ])?; - let tx_hash = H256::from_str(output.trim().trim_start_matches("0x"))?; - log::info!("tx_hash: {:#x}", tx_hash); +/// Deploy scripts used by godwoken +#[derive(Parser)] +#[clap(name = DEPLOY_SCRIPTS_COMMAND)] +pub struct DeployScriptsCommand { + #[clap(flatten)] + context_args: DeployContextArgs, + /// The input json file path + #[clap(short)] + input_path: PathBuf, + /// The output json file path + #[clap(short)] + output_path: PathBuf, +} - gw_ckb_client - .wait_tx_committed_with_timeout_and_logging(tx_hash.0, 600) - .await?; - let tx = gw_ckb_client - .get_packed_transaction(tx_hash.0) - .await? - .ok_or_else(|| anyhow!("tx not found"))?; - let first_output_type_script = tx - .raw() - .outputs() - .get(0) - .expect("output 0") - .type_() - .to_opt() - .expect("type id cell"); - let script_type_hash = first_output_type_script.hash().into(); - let cell_dep = CellDep { - out_point: OutPoint { - tx_hash, - index: 0.into(), - }, - dep_type: DepType::Code, - }; - Ok(DeployItem { - script_type_hash, - cell_dep, - }) +impl DeployScriptsCommand { + pub async fn run(self) -> Result<()> { + let context = self.context_args.build().await?; + let ctx = || format!("reading input from {}", self.input_path.to_string_lossy()); + let scripts: BuildScriptsResult = + serde_json::from_slice(&fs::read(&self.input_path).with_context(ctx)?) + .with_context(ctx)?; + let result = deploy_scripts(&context, &scripts).await?; + fs::write(&self.output_path, serde_json::to_string_pretty(&result)?) + .with_context(|| format!("write output to {}", self.output_path.to_string_lossy()))?; + Ok(()) + } } pub async fn deploy_scripts( - privkey_path: &Path, - ckb_rpc_url: &str, - scripts_result: &BuildScriptsResult, + context: &DeployContext, + scripts: &BuildScriptsResult, ) -> Result { - if let Err(err) = run_cmd(vec!["--version"]) { - return Err(anyhow!( - "Please install ckb-cli (cargo install ckb-cli) first: {}", - err - )); - } - - let ckb_client = gw_rpc_client::ckb_client::CkbClient::with_url(ckb_rpc_url)?; - let network_type = get_network_type(&ckb_client).await?; - let target_lock = packed::Script::from(scripts_result.lock.clone()); - let address_payload = AddressPayload::from(target_lock.clone()); - let target_address = Address::new(network_type, address_payload, true); + // To iterator the programs by name. + let programs: HashMap = + serde_json::from_value(serde_json::to_value(&scripts.programs)?)?; + let lock: packed::Script = scripts.lock.clone().into(); - let mut total_file_size = 0; + let mut local_cells = LocalCellsManager::default(); + let mut txs = Vec::new(); + let mut result = serde_json::Map::new(); + for (name, path) in programs { + let data = + fs::read(&path).with_context(|| format!("read file {}", path.to_string_lossy()))?; + log::info!( + "deploy {name}({}), size {}", + path.to_string_lossy(), + data.len(), + ); - for path in &[ - &scripts_result.programs.custodian_lock, - &scripts_result.programs.deposit_lock, - &scripts_result.programs.withdrawal_lock, - &scripts_result.programs.challenge_lock, - &scripts_result.programs.stake_lock, - &scripts_result.programs.omni_lock, - &scripts_result.programs.state_validator, - &scripts_result.programs.l2_sudt_validator, - &scripts_result.programs.eth_account_lock, - &scripts_result.programs.meta_contract_validator, - &scripts_result.programs.polyjuice_validator, - &scripts_result.programs.eth_addr_reg_validator, - &scripts_result.programs.delegate_cell_lock, - ] { - match fs::metadata(path).map_err(|err| err.to_string()) { - Ok(metadata) => { - if !metadata.is_file() { - return Err(anyhow!("binary path is not a file: {:?}", path)); - } - total_file_size += metadata.len(); - log::info!("cost {:>6} CKBytes for file: {:?}", metadata.len(), path); - } - Err(err) => { - return Err(anyhow!("error read metadata of {:?}, error: {}", path, err)); - } - } + let (tx, out_point, type_script) = context + .deploy_type_id_cell(lock.clone(), data.into(), &local_cells) + .await?; + let tx_hash = tx.hash(); + log::info!("tx: {:#}", H256::from(tx_hash)); + local_cells.apply_tx(&tx.as_reader()); + txs.push(tx_hash); + result.insert( + name, + serde_json::to_value(&DeployItem { + script_type_hash: type_script.hash().into(), + type_script: type_script.into(), + cell_dep: ckb_jsonrpc_types::CellDep { + out_point: out_point.into(), + dep_type: ckb_jsonrpc_types::DepType::Code, + }, + })?, + ); } - log::info!("total_file_size: {}", total_file_size); - let custodian_lock = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.custodian_lock, - &target_lock, - &target_address, - ) - .await?; - let deposit_lock = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.deposit_lock, - &target_lock, - &target_address, - ) - .await?; - let withdrawal_lock = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.withdrawal_lock, - &target_lock, - &target_address, - ) - .await?; - let challenge_lock = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.challenge_lock, - &target_lock, - &target_address, - ) - .await?; - let stake_lock = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.stake_lock, - &target_lock, - &target_address, - ) - .await?; - let omni_lock = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.omni_lock, - &target_lock, - &target_address, - ) - .await?; - let state_validator = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.state_validator, - &target_lock, - &target_address, - ) - .await?; - let l2_sudt_validator = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.l2_sudt_validator, - &target_lock, - &target_address, - ) - .await?; - let meta_contract_validator = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.meta_contract_validator, - &target_lock, - &target_address, - ) - .await?; - let eth_account_lock = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.eth_account_lock, - &target_lock, - &target_address, - ) - .await?; - // FIXME: write godwoken-polyjuice binary to named temp file then use the path - let polyjuice_validator = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.polyjuice_validator, - &target_lock, - &target_address, - ) - .await?; - let eth_addr_reg_validator = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.eth_addr_reg_validator, - &target_lock, - &target_address, - ) - .await?; - let delegate_cell_lock = deploy_program( - privkey_path, - ckb_rpc_url, - &ckb_client, - &scripts_result.programs.delegate_cell_lock, - &target_lock, - &target_address, - ) - .await?; + for tx in txs { + log::info!("waiting for tx {:#}", H256::from(tx)); + context + .ckb_client + .wait_tx_committed_with_timeout_and_logging(tx, 180) + .await?; + } - let deployment_result = ScriptsDeploymentResult { - custodian_lock, - deposit_lock, - withdrawal_lock, - challenge_lock, - stake_lock, - omni_lock, - state_validator, - l2_sudt_validator, - meta_contract_validator, - eth_account_lock, - polyjuice_validator, - eth_addr_reg_validator, - delegate_cell_lock, - }; - Ok(deployment_result) + Ok(serde_json::from_value(result.into())?) } diff --git a/crates/tools/src/deposit_ckb.rs b/crates/tools/src/deposit_ckb.rs index 1640d76fe..3bee689d2 100644 --- a/crates/tools/src/deposit_ckb.rs +++ b/crates/tools/src/deposit_ckb.rs @@ -1,26 +1,27 @@ -use crate::account::{privkey_to_eth_address, read_privkey}; -use crate::godwoken_rpc::GodwokenRpcClient; -use crate::hasher::CkbHasher; -use crate::types::ScriptsDeploymentResult; -use crate::utils::sdk::{Address, AddressPayload, HumanCapacity, SECP256K1}; -use crate::utils::transaction::{get_network_type, read_config, run_cmd}; -use anyhow::{anyhow, Result}; +use std::{ + path::Path, + str::FromStr, + time::{Duration, Instant}, +}; + +use anyhow::{anyhow, bail, Result}; use ckb_fixed_hash::H256; use ckb_types::core::Capacity; -use ckb_types::{bytes::Bytes as CKBBytes, core::ScriptHashType, packed::Script as CKBScript}; use gw_common::builtins::{CKB_SUDT_ACCOUNT_ID, ETH_REGISTRY_ACCOUNT_ID}; -use gw_rpc_client::ckb_client::CkbClient; -use gw_types::core::Timepoint; -use gw_types::packed::{CellOutput, CustodianLockArgs}; -use gw_types::U256; use gw_types::{ - bytes::Bytes as GwBytes, - packed::{Byte32, DepositLockArgs, Script}, + core::{ScriptHashType, Timepoint}, + packed::{CellOutput, CustodianLockArgs, DepositLockArgs, Script}, prelude::*, + U256, +}; +use gw_utils::transaction_skeleton::TransactionSkeleton; + +use crate::{ + account::{privkey_to_eth_address, read_privkey}, + godwoken_rpc::GodwokenRpcClient, + types::ScriptsDeploymentResult, + utils::{deploy::DeployContextArgs, sdk::HumanCapacity, transaction::read_config}, }; -use std::path::Path; -use std::str::FromStr; -use std::time::{Duration, Instant}; #[allow(clippy::too_many_arguments)] pub async fn deposit_ckb( @@ -28,8 +29,10 @@ pub async fn deposit_ckb( scripts_deployment_path: &Path, config_path: &Path, capacity: &str, - fee: &str, + // TODO: setting fee. + _fee: &str, ckb_rpc_url: &str, + ckb_indexer_rpc_url: Option<&str>, eth_address: Option<&str>, godwoken_rpc_url: &str, ) -> Result<()> { @@ -39,57 +42,59 @@ pub async fn deposit_ckb( let config = read_config(&config_path)?; + let context = DeployContextArgs { + ckb_rpc: ckb_rpc_url.into(), + ckb_indexer_rpc: ckb_indexer_rpc_url.map(Into::into), + privkey_path: privkey_path.into(), + } + .build() + .await?; + let privkey = read_privkey(privkey_path)?; // Using private key to calculate eth address when eth_address not provided. let eth_address_bytes = match eth_address { - Some(addr) => { - let addr_vec = hex::decode(&addr.trim_start_matches("0x").as_bytes())?; - CKBBytes::from(addr_vec) - } + Some(addr) => hex::decode(&addr.trim_start_matches("0x").as_bytes())?.into(), None => privkey_to_eth_address(&privkey)?, }; log::info!("eth address: 0x{:#x}", eth_address_bytes); let rollup_type_hash = &config.consensus.get_config().genesis.rollup_type_hash; - let owner_lock_hash = Byte32::from_slice(privkey_to_lock_hash(&privkey)?.as_bytes())?; + let owner_lock_hash = context.wallet.lock_script().hash(); // build layer2 lock let l2_code_hash = &scripts_deployment.eth_account_lock.script_type_hash; - let mut l2_args_vec = rollup_type_hash.as_bytes().to_vec(); - l2_args_vec.append(&mut eth_address_bytes.to_vec()); - let l2_lock_args = Pack::pack(&GwBytes::from(l2_args_vec)); + let mut l2_args = rollup_type_hash.as_bytes().to_vec(); + l2_args.append(&mut eth_address_bytes.to_vec()); let l2_lock = Script::new_builder() - .code_hash(Byte32::from_slice(l2_code_hash.as_bytes())?) + .code_hash(l2_code_hash.pack()) .hash_type(ScriptHashType::Type.into()) - .args(l2_lock_args) + .args(l2_args.pack()) .build(); - let l2_lock_hash = CkbHasher::new().update(l2_lock.as_slice()).finalize(); + let l2_lock_hash: H256 = l2_lock.hash().into(); - let l2_lock_hash_str = format!("0x{}", faster_hex::hex_string(l2_lock_hash.as_bytes())?); - log::info!("layer2 script hash: {}", l2_lock_hash_str); + log::info!("layer2 script hash: 0x{}", l2_lock_hash); // cancel_timeout default to 20 minutes let deposit_lock_args = DepositLockArgs::new_builder() - .owner_lock_hash(owner_lock_hash) - .cancel_timeout(Pack::pack(&0xc0000000000004b0u64)) + .owner_lock_hash(owner_lock_hash.pack()) + .cancel_timeout(0xc0000000000004b0u64.pack()) .layer2_lock(l2_lock) - .registry_id(Pack::pack(Ð_REGISTRY_ACCOUNT_ID)) + .registry_id(ETH_REGISTRY_ACCOUNT_ID.pack()) .build(); let minimal_capacity = minimal_deposit_capacity(&deposit_lock_args)?; let capacity_in_shannons = parse_capacity(capacity)?; if capacity_in_shannons < minimal_capacity { - let msg = anyhow!( + bail!( "Deposit CKB required {} CKB at least, provided {}.", HumanCapacity::from(minimal_capacity).to_string(), HumanCapacity::from(capacity_in_shannons).to_string() ); - return Err(msg); } let mut l1_lock_args = rollup_type_hash.as_bytes().to_vec(); @@ -97,67 +102,27 @@ pub async fn deposit_ckb( let deposit_lock_code_hash = &scripts_deployment.deposit_lock.script_type_hash; - let rpc_client = CkbClient::with_url(ckb_rpc_url)?; - let network_type = get_network_type(&rpc_client).await?; - let address_payload = AddressPayload::new_full( - ScriptHashType::Type, - Pack::pack(deposit_lock_code_hash), - GwBytes::from(l1_lock_args), - ); - let address: Address = Address::new(network_type, address_payload, true); + let deposit_lock = Script::new_builder() + .code_hash(deposit_lock_code_hash.pack()) + .hash_type(ScriptHashType::Type.into()) + .args(l1_lock_args.pack()) + .build(); let mut godwoken_rpc_client = GodwokenRpcClient::new(godwoken_rpc_url); - log::info!("script hash: 0x{}", hex::encode(l2_lock_hash.as_bytes())); - let init_balance = get_balance_by_script_hash(&mut godwoken_rpc_client, &l2_lock_hash).await?; log::info!("balance before deposit: {}", init_balance); - loop { - let result = run_cmd(vec![ - "--url", - ckb_rpc_url, - "wallet", - "transfer", - "--privkey-path", - privkey_path.to_str().expect("non-utf8 file path"), - "--to-address", - address.to_string().as_str(), - "--capacity", - capacity, - "--tx-fee", - fee, - "--skip-check-to-address", - ]); - let output = match result { - Ok(output) => output, - Err(e) => { - // Sending transaction may fail because there is another - // **proposed** but not committed transaction using the same - // input. - log::warn!("Running ckb-cli failed: {:?}. Retrying.", e); - tokio::time::sleep(Duration::from_secs(3)).await; - continue; - } - }; - - let tx_hash = H256::from_str(output.trim().trim_start_matches("0x"))?; - log::info!("tx_hash: {:#x}", tx_hash); - - if let Err(e) = rpc_client - .wait_tx_committed_with_timeout_and_logging(tx_hash.0, 600) - .await - { - if e.to_string().contains("rejected") { - // Transaction can be rejected due to double spending. Retry. - log::warn!("Transaction is rejected. Retrying."); - } else { - return Err(e); - } - } else { - break; - } - } + let mut tx = TransactionSkeleton::new([0u8; 32]); + tx.transfer_to(deposit_lock, capacity_in_shannons)?; + let tx = context.deploy(tx, &Default::default()).await?; + + let tx_hash: H256 = tx.hash().into(); + log::info!("Sent transaction 0x{tx_hash}"); + context + .ckb_client + .wait_tx_committed_with_timeout_and_logging(tx_hash.0, 600) + .await?; wait_for_balance_change( &mut godwoken_rpc_client, @@ -170,17 +135,6 @@ pub async fn deposit_ckb( Ok(()) } -fn privkey_to_lock_hash(privkey: &H256) -> Result { - let privkey = secp256k1::SecretKey::from_slice(privkey.as_bytes())?; - let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &privkey); - let address_payload = AddressPayload::from_pubkey(&pubkey); - - let lock_hash: H256 = CKBScript::from(&address_payload) - .calc_script_hash() - .unpack(); - Ok(lock_hash) -} - async fn wait_for_balance_change( godwoken_rpc_client: &mut GodwokenRpcClient, from_script_hash: &H256, diff --git a/crates/tools/src/dump_tx.rs b/crates/tools/src/dump_tx.rs index 74c677293..79590191c 100644 --- a/crates/tools/src/dump_tx.rs +++ b/crates/tools/src/dump_tx.rs @@ -1,10 +1,10 @@ -use crate::godwoken_rpc::GodwokenRpcClient; +use std::{fs::write, path::Path, str::FromStr}; use anyhow::Result; use ckb_fixed_hash::H256; use gw_jsonrpc_types::{debugger::DumpChallengeTarget, godwoken::ChallengeTargetType}; -use std::{fs::write, path::Path, str::FromStr}; +use crate::godwoken_rpc::GodwokenRpcClient; pub enum ChallengeBlock { Number(u64), diff --git a/crates/tools/src/generate_config.rs b/crates/tools/src/generate_config.rs index 269a3849d..f14a66784 100644 --- a/crates/tools/src/generate_config.rs +++ b/crates/tools/src/generate_config.rs @@ -1,8 +1,11 @@ -use std::path::Path; +use std::{ + fs, + path::{Path, PathBuf}, +}; use anyhow::{anyhow, Context, Result}; use ckb_jsonrpc_types::{CellDep, JsonBytes}; -use ckb_types::prelude::{Builder, Entity}; +use clap::{Parser, ValueEnum}; use gw_builtin_binaries::{file_checksum, Resource}; use gw_config::{ BackendConfig, BackendForkConfig, BlockProducerConfig, ChainConfig, ChallengerConfig, Config, @@ -12,51 +15,125 @@ use gw_config::{ }; use gw_jsonrpc_types::{godwoken::L2BlockCommittedInfo, JsonCalcHash}; use gw_rpc_client::ckb_client::CkbClient; -use gw_types::{core::ScriptHashType, packed::Script, prelude::*}; +use gw_types::prelude::*; +use serde::de::DeserializeOwned; use crate::{ deploy_genesis::get_secp_data, - setup::get_wallet_info, types::{ BuildScriptsResult, OmniLockConfig, RollupDeploymentResult, ScriptsDeploymentResult, UserRollupConfig, }, + utils::cli_args::H160Arg, }; -pub struct GenerateNodeConfigArgs<'a> { - pub rollup_result: &'a RollupDeploymentResult, - pub scripts_deployment: &'a ScriptsDeploymentResult, - pub privkey_path: &'a Path, - pub ckb_url: String, - pub indexer_url: Option, - pub build_scripts_result: &'a BuildScriptsResult, - pub server_url: String, - pub user_rollup_config: &'a UserRollupConfig, - pub omni_lock_config: &'a OmniLockConfig, - pub node_mode: NodeMode, - pub block_producer_address: Vec, - pub p2p_listen: Option, - pub p2p_dial: Vec, +pub const GENERATE_CONFIG_COMMAND: &str = "generate-config"; + +#[derive(ValueEnum, Clone)] +enum NodeModeV { + Readonly, + Fullnode, +} + +impl From for NodeMode { + fn from(m: NodeModeV) -> Self { + match m { + NodeModeV::Fullnode => Self::FullNode, + NodeModeV::Readonly => Self::ReadOnly, + } + } +} + +/// Generate config +#[derive(Parser)] +#[clap(name = GENERATE_CONFIG_COMMAND)] +pub struct GenerateConfigCommand { + // Output. + /// Output config file path + #[clap(short = 'o', long)] + output_path: PathBuf, + /// Output withdrawal to v1 config path + #[clap(long)] + output_withdrawal_to_v1_config: Option, + + // Input. + /// The scripts deployment results json file path + #[clap(long)] + scripts_deployment_path: PathBuf, + /// The genesis deployment results json file path + #[clap(short = 'g', long)] + genesis_deployment_path: PathBuf, + /// The user rollup config json file path + #[clap(short, long)] + rollup_config: PathBuf, + /// The omni lock config json file path + #[clap(long)] + omni_lock_config_path: Option, + /// Scripts deployment config json file path + #[clap(short = 'c', long)] + scripts_deployment_config_path: PathBuf, + + /// CKB jsonrpc URL + #[clap(long, default_value = "http://127.0.0.1:8114")] + ckb_rpc: String, + /// CKB indexer jsonrpc URL + #[clap(long)] + ckb_indexer_rpc: Option, + + #[clap(value_enum, long, default_value_t = NodeModeV::Readonly)] + node_mode: NodeModeV, + /// The private key file path + #[clap(short = 'k', long)] + privkey_path: Option, + /// Store path. + #[clap(long)] + store_path: Option, + /// Block producer address + #[clap(long)] + block_producer_address: Option, + /// RPC server listening address + /// + /// RPC server listening address in the generated config file. + #[clap(long, default_value = "localhost:8119")] + rpc_server_url: String, + /// P2P network listen multiaddr + /// + /// E.g. /ip4/1.2.3.4/tcp/443 + #[clap(long)] + p2p_listen: Option, + /// P2P network dial addresses + /// + /// E.g. /dns4/godwoken/tcp/443 + #[clap(long)] + p2p_dial: Vec, +} + +impl GenerateConfigCommand { + pub async fn run(self) -> Result<()> { + generate_node_config(self).await?; + Ok(()) + } +} + +fn read_json(p: &Path) -> Result { + let ctx = || format!("read file {}", p.to_string_lossy()); + let c = fs::read(p).with_context(ctx)?; + let r = serde_json::from_slice(&c).with_context(ctx)?; + Ok(r) } -pub async fn generate_node_config(args: GenerateNodeConfigArgs<'_>) -> Result { - let GenerateNodeConfigArgs { - rollup_result, - scripts_deployment, - privkey_path, - ckb_url, - indexer_url, - build_scripts_result, - server_url, - user_rollup_config, - omni_lock_config, - node_mode, - block_producer_address, - p2p_listen, - p2p_dial, - } = args; - - let rpc_client = CkbClient::with_url(&ckb_url)?; +pub async fn generate_node_config(cmd: GenerateConfigCommand) -> Result<()> { + let rpc_client = CkbClient::with_url(&cmd.ckb_rpc)?; + let scripts: BuildScriptsResult = read_json(&cmd.scripts_deployment_config_path)?; + let scripts_deployment: ScriptsDeploymentResult = read_json(&cmd.scripts_deployment_path)?; + let rollup_result: RollupDeploymentResult = read_json(&cmd.genesis_deployment_path)?; + let user_rollup_config: UserRollupConfig = read_json(&cmd.rollup_config)?; + let omni_lock_config: Option = if let Some(ref o) = cmd.omni_lock_config_path { + Some(read_json(o)?) + } else { + None + }; + let tx_with_status = rpc_client .get_transaction(rollup_result.tx_hash.clone(), 2.into()) .await? @@ -74,25 +151,6 @@ pub async fn generate_node_config(args: GenerateNodeConfigArgs<'_>) -> Result) -> Result) -> Result = vec![ { - let generator_path = - build_scripts_result.built_scripts["meta_contract_generator"].clone(); + let generator_path = scripts.built_scripts["meta_contract_generator"].clone(); let generator = Resource::file_system(generator_path.clone()); let generator_checksum = file_checksum(&generator_path)?.into(); BackendConfig { @@ -143,7 +197,7 @@ pub async fn generate_node_config(args: GenerateNodeConfigArgs<'_>) -> Result) -> Result) -> Result) -> Result) -> Result = Some(BlockProducerConfig { + let block_producer = Some(BlockProducerConfig { block_producer: RegistryAddressConfig { address_type: RegistryType::Eth, - address: JsonBytes::from_vec(block_producer_address), + address: JsonBytes::from_vec( + cmd.block_producer_address + .unwrap_or_default() + .0 + .as_bytes() + .to_vec(), + ), }, challenger_config, - wallet_config: Some(wallet_config), + wallet_config, ..Default::default() }); - let p2p_network_config = if !p2p_dial.is_empty() || p2p_listen.is_some() { + let p2p_network_config = if !cmd.p2p_dial.is_empty() || cmd.p2p_listen.is_some() { Some(P2PNetworkConfig { - listen: p2p_listen, - dial: p2p_dial, + listen: cmd.p2p_listen, + dial: cmd.p2p_dial, ..Default::default() }) } else { @@ -271,138 +326,84 @@ pub async fn generate_node_config(args: GenerateNodeConfigArgs<'_>) -> Result, delegate_cell_type_script: gw_jsonrpc_types::blockchain::Script, ) -> Result { let query = |contract: &'static str, cell_dep: CellDep| -> _ { ckb_client.query_type_script(contract, cell_dep) }; - let state_validator = query( - "state validator", - deployment.state_validator.cell_dep.clone(), - ) - .await?; - assert_eq!( - state_validator.hash(), - deployment.state_validator.script_type_hash - ); - - let deposit_lock = query("deposit", deployment.deposit_lock.cell_dep.clone()).await?; - assert_eq!( - deposit_lock.hash(), - deployment.deposit_lock.script_type_hash - ); - - let stake_lock = query("stake", deployment.stake_lock.cell_dep.clone()).await?; - assert_eq!(stake_lock.hash(), deployment.stake_lock.script_type_hash); - - let custodian_lock = query("custodian", deployment.custodian_lock.cell_dep.clone()).await?; - assert_eq!( - custodian_lock.hash(), - deployment.custodian_lock.script_type_hash - ); - - let withdrawal_lock = query("withdrawal", deployment.withdrawal_lock.cell_dep.clone()).await?; - assert_eq!( - withdrawal_lock.hash(), - deployment.withdrawal_lock.script_type_hash - ); - - let challenge_lock = query("challenge", deployment.challenge_lock.cell_dep.clone()).await?; - assert_eq!( - challenge_lock.hash(), - deployment.challenge_lock.script_type_hash - ); - let l1_sudt = query("l1 sudt", user_rollup_config.l1_sudt_cell_dep.clone()).await?; assert_eq!(l1_sudt.hash(), user_rollup_config.l1_sudt_script_type_hash); - let delegate_cell_lock = query( - "delegate_cell_lock", - deployment.delegate_cell_lock.cell_dep.clone(), - ) - .await?; - assert_eq!( - delegate_cell_lock.hash(), - deployment.delegate_cell_lock.script_type_hash - ); - - // Allowed eoa script deps - let eth_account_lock = - query("eth account", deployment.eth_account_lock.cell_dep.clone()).await?; - assert_eq!( - eth_account_lock.hash(), - deployment.eth_account_lock.script_type_hash - ); - - // Allowed contract script deps - let meta_validator = query("meta", deployment.meta_contract_validator.cell_dep.clone()).await?; - assert_eq!( - meta_validator.hash(), - deployment.meta_contract_validator.script_type_hash - ); - - let l2_sudt_validator = query("l2 sudt", deployment.l2_sudt_validator.cell_dep.clone()).await?; - assert_eq!( - l2_sudt_validator.hash(), - deployment.l2_sudt_validator.script_type_hash - ); - - let polyjuice_validator = - query("polyjuice", deployment.polyjuice_validator.cell_dep.clone()).await?; - assert_eq!( - polyjuice_validator.hash(), - deployment.polyjuice_validator.script_type_hash - ); - - let eth_addr_reg_validator = query( - "eth_addr_reg_validator", - deployment.eth_addr_reg_validator.cell_dep.clone(), - ) - .await?; - assert_eq!( - eth_addr_reg_validator.hash(), - deployment.eth_addr_reg_validator.script_type_hash - ); - - let allowed_eoa_scripts = vec![eth_account_lock]; + let d = deployment; + let allowed_eoa_scripts = vec![d.eth_account_lock.type_script.clone()]; let allowed_contract_scripts = vec![ - meta_validator, - l2_sudt_validator, - polyjuice_validator, - eth_addr_reg_validator, + d.meta_contract_validator.type_script.clone(), + d.l2_sudt_validator.type_script.clone(), + d.polyjuice_validator.type_script.clone(), + d.eth_addr_reg_validator.type_script.clone(), ]; - let omni_lock = query("omni lock", omni_lock_config.cell_dep.clone()).await?; - assert_eq!(omni_lock.hash(), omni_lock_config.script_type_hash); + let omni_lock = if let Some(o) = omni_lock_config { + let omni_lock = query("omni lock", o.cell_dep.clone()).await?; + assert_eq!(omni_lock.hash(), o.script_type_hash); + omni_lock + } else { + d.omni_lock.type_script.clone() + }; Ok(SystemTypeScriptConfig { - state_validator, - deposit_lock, - stake_lock, - custodian_lock, - withdrawal_lock, - challenge_lock, + state_validator: d.state_validator.type_script.clone(), + deposit_lock: d.deposit_lock.type_script.clone(), + stake_lock: d.stake_lock.type_script.clone(), + custodian_lock: d.custodian_lock.type_script.clone(), + withdrawal_lock: d.withdrawal_lock.type_script.clone(), + challenge_lock: d.challenge_lock.type_script.clone(), l1_sudt, omni_lock, allowed_eoa_scripts, allowed_contract_scripts, - delegate_cell_lock: Some(delegate_cell_lock), + delegate_cell_lock: Some(d.delegate_cell_lock.type_script.clone()), delegate_cell: Some(delegate_cell_type_script), }) } diff --git a/crates/tools/src/godwoken_rpc.rs b/crates/tools/src/godwoken_rpc.rs index 2aa7bb16f..195fea4e5 100644 --- a/crates/tools/src/godwoken_rpc.rs +++ b/crates/tools/src/godwoken_rpc.rs @@ -1,3 +1,11 @@ +use std::{ + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, + u32, +}; + use anyhow::{anyhow, Result}; use ckb_jsonrpc_types::Script; use ckb_types::H256; @@ -8,13 +16,6 @@ use gw_jsonrpc_types::{ godwoken::{RunResult, TxReceipt}, }; use gw_types::U256; -use std::{ - sync::{ - atomic::{AtomicU64, Ordering}, - Arc, - }, - u32, -}; type AccountID = Uint32; diff --git a/crates/tools/src/main.rs b/crates/tools/src/main.rs index 476840af7..6176e807c 100644 --- a/crates/tools/src/main.rs +++ b/crates/tools/src/main.rs @@ -18,7 +18,7 @@ mod scan_eth_address; mod setup; mod stat; mod sudt; -pub(crate) mod types; +mod types; mod update_cell; mod utils; mod withdraw; @@ -30,27 +30,24 @@ use std::{ }; use account::read_privkey; -use anyhow::{anyhow, Context, Result}; -use clap::{value_t, App, Arg, SubCommand}; +use anyhow::{anyhow, Result}; +use clap::{value_t, App, Arg, CommandFactory, FromArgMatches, SubCommand}; use deploy_genesis::DeployRollupCellArgs; +use deploy_scripts::{DeployScriptsCommand, DEPLOY_SCRIPTS_COMMAND}; use dump_tx::ChallengeBlock; -use generate_config::GenerateNodeConfigArgs; +use generate_config::{GenerateConfigCommand, GENERATE_CONFIG_COMMAND}; use godwoken_rpc::GodwokenRpcClient; use gw_common::builtins::ETH_REGISTRY_ACCOUNT_ID; use gw_jsonrpc_types::godwoken::ChallengeTargetType; use gw_rpc_client::indexer_client::CkbIndexerClient; use gw_types::{offchain::CompatibleFinalizedTimepoint, prelude::*}; use tracing_subscriber::prelude::*; -use types::{ - BuildScriptsResult, RollupDeploymentResult, ScriptsDeploymentResult, UserRollupConfig, -}; -use utils::{cli_args, transaction::read_config}; -use self::types::OmniLockConfig; -// So rust don't complain about unused items. -pub use crate::utils::sdk; use crate::{ - setup::SetupArgs, sudt::account::build_l1_sudt_type_script, utils::sdk::constants::ONE_CKB, + setup::SetupArgs, + sudt::account::build_l1_sudt_type_script, + types::{ScriptsDeploymentResult, UserRollupConfig}, + utils::{cli_args, sdk::constants::ONE_CKB, transaction::read_config}, }; #[tokio::main(flavor = "current_thread")] @@ -99,31 +96,13 @@ async fn main() -> Result<()> { let mut app = App::new("godwoken tools") .about("Godwoken cli tools") .version(version) - .subcommand( - SubCommand::with_name("deploy-scripts") - .about("Deploy scripts used by godwoken") - .arg(arg_privkey_path.clone()) - .arg(arg_ckb_rpc.clone()) - .arg( - Arg::with_name("input-path") - .short('i') - .takes_value(true) - .required(true) - .help("The input json file path"), - ) - .arg( - Arg::with_name("output-path") - .short('o') - .takes_value(true) - .required(true) - .help("The output json file path"), - ), - ) + .subcommand(DeployScriptsCommand::command()) .subcommand( SubCommand::with_name("deploy-genesis") .about("Deploy genesis block of godwoken") .arg(arg_privkey_path.clone()) .arg(arg_ckb_rpc.clone()) + .arg(arg_indexer_rpc.clone()) .arg( arg_deployment_results_path.clone() ) @@ -142,16 +121,6 @@ async fn main() -> Result<()> { .required(true) .help("The user rollup config json file path"), ) - // This is unused but kept for compatibility for now. - // TODO: remove. - .arg( - Arg::with_name("omni-lock-config-path") - .long("omni-lock-config-path") - .short('l') - .takes_value(true) - .required(false) - .help("The omni lock config json file path"), - ) .arg( Arg::with_name("output-path") .short('o') @@ -165,83 +134,7 @@ async fn main() -> Result<()> { .help("Force to accept unsafe config file"), ), ) - .subcommand( - SubCommand::with_name("generate-config") - .about("Generate configure") - .arg(arg_ckb_rpc.clone()) - .arg( - arg_indexer_rpc.clone() - ) - .arg( - arg_deployment_results_path.clone() - ) - .arg( - Arg::with_name("genesis-deployment-path") - .short('g') - .takes_value(true) - .required(true) - .help("The genesis deployment results json file path"), - ) - .arg( - Arg::with_name("user-rollup-config-path") - .long("rollup-config") - .short('r') - .takes_value(true) - .required(true) - .help("The user rollup config json file path"), - ) - .arg( - Arg::with_name("omni-lock-config-path") - .long("omni-lock-config-path") - .short('l') - .takes_value(true) - .required(true) - .help("The omni lock config json file path"), - ) - .arg(arg_privkey_path.clone()) - .arg( - Arg::with_name("block-producer-address") - .long("block-producer-address") - .takes_value(true) - .default_value("0x0000000000000000000000000000000000000000") - .help("Block producer address"), - ) - .arg( - Arg::with_name("output-path") - .short('o') - .takes_value(true) - .required(true) - .help("The output json file path"), - ) - .arg( - Arg::with_name("scripts-deployment-config-path") - .short('c') - .takes_value(true) - .required(true) - .help("Scripts deployment config json file path"), - ) - .arg( - Arg::with_name("rpc-server-url") - .long("rpc-server-url") - .takes_value(true) - .default_value("localhost:8119") - .required(true) - .help("The URL of rpc server"), - ) - .arg( - Arg::with_name("p2p-listen") - .long("p2p-listen") - .takes_value(true) - .help("P2P network listen multiaddr, e.g. /ip4/1.2.3.4/tcp/443") - ) - .arg( - Arg::with_name("p2p-dial") - .long("p2p-dial") - .takes_value(true) - .multiple(true) - .help("P2P network dial addresses, e.g. /dns4/godwoken/tcp/443") - ), - ) + .subcommand(GenerateConfigCommand::command()) .subcommand( SubCommand::with_name("prepare-scripts") .about("Prepare scripts used by godwoken") @@ -300,6 +193,7 @@ async fn main() -> Result<()> { SubCommand::with_name("deposit-ckb") .about("Deposit CKB to godwoken") .arg(arg_ckb_rpc.clone()) + .arg(arg_indexer_rpc.clone()) .arg(arg_privkey_path.clone()) .arg(arg_deployment_results_path.clone()) .arg(arg_config_path.clone()) @@ -871,31 +765,13 @@ async fn main() -> Result<()> { let matches = app.clone().get_matches(); match matches.subcommand() { - Some(("deploy-scripts", m)) => { - let privkey_path = Path::new(m.value_of("privkey-path").unwrap()); - let ckb_rpc_url = m.value_of("ckb-rpc-url").unwrap(); - let input_path = Path::new(m.value_of("input-path").unwrap()); - let output_path = Path::new(m.value_of("output-path").unwrap()); - let build_script_result: BuildScriptsResult = { - let content = std::fs::read(input_path)?; - serde_json::from_slice(&content)? - }; - let result = - deploy_scripts::deploy_scripts(privkey_path, ckb_rpc_url, &build_script_result) - .await; - match result { - Ok(script_deployment) => { - output_json_file(&script_deployment, output_path); - } - Err(err) => { - log::error!("Deploy scripts error: {}", err); - std::process::exit(-1); - } - }; + Some((DEPLOY_SCRIPTS_COMMAND, m)) => { + DeployScriptsCommand::from_arg_matches(m)?.run().await?; } Some(("deploy-genesis", m)) => { let privkey_path = Path::new(m.value_of("privkey-path").unwrap()); let ckb_rpc_url = m.value_of("ckb-rpc-url").unwrap(); + let ckb_indexer_rpc_url = m.value_of("indexer-rpc-url"); let scripts_deployment_path = Path::new(m.value_of("scripts-deployment-path").unwrap()); let user_rollup_path = Path::new(m.value_of("user-rollup-config-path").unwrap()); let output_path = Path::new(m.value_of("output-path").unwrap()); @@ -917,6 +793,7 @@ async fn main() -> Result<()> { skip_config_check, privkey_path, ckb_rpc_url, + ckb_indexer_rpc_url, scripts_result: &script_results, user_rollup_config: &user_rollup_config, timestamp, @@ -927,79 +804,13 @@ async fn main() -> Result<()> { output_json_file(&rollup_deployment, output_path); } Err(err) => { - log::error!("Deploy genesis error: {}", err); + log::error!("Deploy genesis error: {:#}", err); std::process::exit(-1); } } } - Some(("generate-config", m)) => { - let ckb_url = m.value_of("ckb-rpc-url").unwrap().to_string(); - let indexer_url = m.value_of("indexer-rpc-url").map(Into::into); - let scripts_results_path = Path::new(m.value_of("scripts-deployment-path").unwrap()); - let genesis_path = Path::new(m.value_of("genesis-deployment-path").unwrap()); - let user_rollup_config_path = Path::new(m.value_of("user-rollup-config-path").unwrap()); - let privkey_path = Path::new(m.value_of("privkey-path").unwrap()); - let output_path = Path::new(m.value_of("output-path").unwrap()); - let scripts_config_path = - Path::new(m.value_of("scripts-deployment-config-path").unwrap()); - let server_url = m.value_of("rpc-server-url").unwrap().to_string(); - let omni_lock_config_path = Path::new(m.value_of("omni-lock-config-path").unwrap()); - let block_producer_address = hex::decode( - m.value_of("block-producer-address") - .unwrap() - .to_string() - .trim_start_matches("0x"), - )?; - let p2p_listen = m.value_of("p2p-listen").map(|l| l.to_string()); - let p2p_dial = m - .values_of("p2p-dial") - .into_iter() - .flatten() - .map(|v| v.to_string()) - .collect(); - - let rollup_result: RollupDeploymentResult = { - let content = std::fs::read(genesis_path)?; - serde_json::from_slice(&content)? - }; - let scripts_deployment: ScriptsDeploymentResult = { - let content = std::fs::read(scripts_results_path)?; - serde_json::from_slice(&content)? - }; - let build_scripts_result: BuildScriptsResult = { - let content = std::fs::read(scripts_config_path)?; - serde_json::from_slice(&content)? - }; - let user_rollup_config: UserRollupConfig = { - let content = std::fs::read(user_rollup_config_path)?; - serde_json::from_slice(&content)? - }; - let omni_lock_config: OmniLockConfig = { - let content = std::fs::read(omni_lock_config_path)?; - let json: serde_json::Value = serde_json::from_slice(&content)?; - serde_json::from_value(json["omni_lock"].clone())? - }; - - let args = GenerateNodeConfigArgs { - rollup_result: &rollup_result, - scripts_deployment: &scripts_deployment, - build_scripts_result: &build_scripts_result, - privkey_path, - ckb_url, - indexer_url, - server_url, - user_rollup_config: &user_rollup_config, - omni_lock_config: &omni_lock_config, - node_mode: gw_config::NodeMode::ReadOnly, - block_producer_address, - p2p_listen, - p2p_dial, - }; - - let config = generate_config::generate_node_config(args).await?; - let content = toml_edit::ser::to_string_pretty(&config)?; - std::fs::write(output_path, content).context("writing config file")?; - log::info!("Generate file {:?}", output_path); + Some((GENERATE_CONFIG_COMMAND, m)) => { + GenerateConfigCommand::from_arg_matches(m)?.run().await?; } Some(("prepare-scripts", m)) => { let mode = value_t!(m, "mode", prepare_scripts::ScriptsBuildMode).unwrap(); @@ -1050,7 +861,8 @@ async fn main() -> Result<()> { .await?; } Some(("deposit-ckb", m)) => { - let ckb_rpc_url = m.value_of("ckb-rpc-url").unwrap().to_string(); + let ckb_rpc_url = m.value_of("ckb-rpc-url").unwrap(); + let ckb_indexer_rpc_url = m.value_of("indexer-rpc-url"); let privkey_path = Path::new(m.value_of("privkey-path").unwrap()); let capacity = m.value_of("capacity").unwrap(); let fee = m.value_of("fee").unwrap(); @@ -1065,13 +877,14 @@ async fn main() -> Result<()> { config_path, capacity, fee, - ckb_rpc_url.as_str(), + ckb_rpc_url, + ckb_indexer_rpc_url, eth_address, godwoken_rpc_url, ) .await { - log::error!("Deposit CKB error: {}", err); + log::error!("Deposit CKB error: {:#}", err); std::process::exit(-1); }; } diff --git a/crates/tools/src/polyjuice.rs b/crates/tools/src/polyjuice.rs index a4a957904..74840b35b 100644 --- a/crates/tools/src/polyjuice.rs +++ b/crates/tools/src/polyjuice.rs @@ -1,10 +1,15 @@ +use std::path::Path; + use anyhow::{anyhow, Result}; use ckb_fixed_hash::H256; use ckb_jsonrpc_types::JsonBytes; use ckb_types::prelude::{Builder, Entity}; use gw_common::{builtins::ETH_REGISTRY_ACCOUNT_ID, registry_address::RegistryAddress}; -use gw_types::packed::{L2Transaction, RawL2Transaction}; -use std::path::Path; +use gw_types::{ + bytes::Bytes, + packed::{L2Transaction, RawL2Transaction}, + prelude::*, +}; use crate::{ account::{eth_sign, parse_account_from_str, privkey_to_l2_script_hash, read_privkey}, @@ -15,7 +20,6 @@ use crate::{ transaction::{read_config, wait_for_l2_tx}, }, }; -use gw_types::{bytes::Bytes as GwBytes, prelude::*}; const GW_LOG_POLYJUICE_SYSTEM: u8 = 0x2; @@ -31,7 +35,7 @@ pub async fn deploy( data: &str, value: u128, ) -> Result<()> { - let data = GwBytes::from(hex::decode(data.trim_start_matches("0x").as_bytes())?); + let data = Bytes::from(hex::decode(data.trim_start_matches("0x").as_bytes())?); let scripts_deployment_string = std::fs::read_to_string(scripts_deployment_path)?; let scripts_deployment: ScriptsDeploymentResult = @@ -74,7 +78,7 @@ pub async fn send_transaction( value: u128, to_address: &str, ) -> Result<()> { - let data = GwBytes::from(hex::decode(data.trim_start_matches("0x").as_bytes())?); + let data = Bytes::from(hex::decode(data.trim_start_matches("0x").as_bytes())?); let scripts_deployment_string = std::fs::read_to_string(scripts_deployment_path)?; let scripts_deployment: ScriptsDeploymentResult = @@ -116,7 +120,7 @@ pub async fn polyjuice_call( to_address: &str, from: &str, ) -> Result<()> { - let data = GwBytes::from(hex::decode(data.trim_start_matches("0x").as_bytes())?); + let data = Bytes::from(hex::decode(data.trim_start_matches("0x").as_bytes())?); let mut godwoken_rpc_client = GodwokenRpcClient::new(godwoken_rpc_url); @@ -174,7 +178,7 @@ async fn send( privkey: &H256, gas_limit: u64, gas_price: u128, - data: GwBytes, + data: Bytes, value: u128, rollup_type_hash: &H256, scripts_deployment: &ScriptsDeploymentResult, @@ -259,10 +263,10 @@ fn encode_polyjuice_args( gas_limit: u64, gas_price: u128, value: u128, - data: GwBytes, + data: Bytes, to_id: u32, creator_account_id: u32, -) -> GwBytes { +) -> Bytes { let mut args = vec![0u8; 52]; args[0..7].copy_from_slice(b"\xFF\xFF\xFFPOLY"); args[7] = if to_id == 0 || to_id == creator_account_id { @@ -277,5 +281,5 @@ fn encode_polyjuice_args( args[48..52].copy_from_slice(&data_length.to_le_bytes()); args.append(&mut data.to_vec()); - GwBytes::from(args) + Bytes::from(args) } diff --git a/crates/tools/src/prepare_scripts.rs b/crates/tools/src/prepare_scripts.rs index 33156ee10..23ff5f982 100644 --- a/crates/tools/src/prepare_scripts.rs +++ b/crates/tools/src/prepare_scripts.rs @@ -1,17 +1,19 @@ -use crate::{ - types::{BuildScriptsResult, Programs}, - utils, -}; -use anyhow::Result; -use clap::arg_enum; -use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, fs, path::{Path, PathBuf}, }; + +use anyhow::Result; +use clap::arg_enum; +use serde::{Deserialize, Serialize}; use url::Url; +use crate::{ + types::{BuildScriptsResult, Programs}, + utils, +}; + pub const SCRIPT_BUILD_DIR_PATH: &str = "scripts-build/"; pub const SCRIPTS_DIR_PATH: &str = "scripts/"; const GODWOKEN_SCRIPTS: &str = "godwoken-scripts"; diff --git a/crates/tools/src/report_accounts.rs b/crates/tools/src/report_accounts.rs index a98be02d2..20a216c99 100644 --- a/crates/tools/src/report_accounts.rs +++ b/crates/tools/src/report_accounts.rs @@ -1,8 +1,9 @@ +use std::path::Path; + use anyhow::Result; use ckb_jsonrpc_types::Serialize; use gw_common::builtins::CKB_SUDT_ACCOUNT_ID; use gw_types::U256; -use std::path::Path; use tokio::task::JoinHandle; use crate::godwoken_rpc::GodwokenRpcClient; diff --git a/crates/tools/src/setup.rs b/crates/tools/src/setup.rs index e5f0eec36..c091d7478 100644 --- a/crates/tools/src/setup.rs +++ b/crates/tools/src/setup.rs @@ -12,7 +12,7 @@ use rand::Rng; use crate::{ deploy_genesis::{deploy_rollup_cell, DeployRollupCellArgs}, deploy_scripts::deploy_scripts, - generate_config::{generate_node_config, GenerateNodeConfigArgs}, + generate_config::generate_node_config, prepare_scripts::{self, prepare_scripts, ScriptsBuildMode}, types::{SetupConfig, UserRollupConfig}, utils, @@ -67,7 +67,7 @@ pub async fn setup(args: SetupArgs<'_>) { build_scripts_config_path, privkey_path, nodes_count, - server_url, + server_url: _server_url, output_dir, setup_config_path, wallet_network, @@ -102,9 +102,13 @@ pub async fn setup(args: SetupArgs<'_>) { // deploy scripts let deploy_scripts_result = { let scripts_deploy_result = output_dir.join("scripts-result.json"); - let deploy_result = deploy_scripts(privkey_path, ckb_rpc_url, &build_scripts_result) - .await - .expect("deploy scripts"); + let deploy_result = deploy_scripts( + // TODO. + None.unwrap(), + &build_scripts_result, + ) + .await + .expect("deploy scripts"); let output_content = serde_json::to_string_pretty(&deploy_result).expect("serde json to string pretty"); fs::write(scripts_deploy_result, output_content.as_bytes()) @@ -135,11 +139,12 @@ pub async fn setup(args: SetupArgs<'_>) { }; // deploy rollup cell - let rollup_result = { + let _rollup_result = { let rollup_result_path = output_dir.join("rollup-result.json"); let args = DeployRollupCellArgs { privkey_path, ckb_rpc_url, + ckb_indexer_rpc_url: indexer_url, scripts_result: &deploy_scripts_result, user_rollup_config: &rollup_config, timestamp: None, @@ -156,33 +161,17 @@ pub async fn setup(args: SetupArgs<'_>) { // generate node config for (index, (node_name, _node_wallet)) in nodes.iter().enumerate() { - let privkey_path = output_dir.join(&node_name).join("pk"); - let output_file_path = output_dir.join(node_name).join("config.toml"); + let _privkey_path = output_dir.join(&node_name).join("pk"); + let _output_file_path = output_dir.join(node_name).join("config.toml"); // set the first node to fullnode - let node_mode = if index == 0 { + let _node_mode = if index == 0 { NodeMode::FullNode } else { NodeMode::ReadOnly }; - let args = GenerateNodeConfigArgs { - rollup_result: &rollup_result, - scripts_deployment: &deploy_scripts_result, - privkey_path: &privkey_path, - ckb_url: ckb_rpc_url.to_string(), - indexer_url: indexer_url.map(Into::into), - build_scripts_result: &build_scripts_result, - server_url: server_url.to_string(), - user_rollup_config: &rollup_config, - omni_lock_config: &setup_config.omni_lock_config, - node_mode, - block_producer_address: vec![0u8; 20], - p2p_listen: None, - p2p_dial: vec![], - }; - let config = generate_node_config(args).await.expect("generate_config"); - let output_content = - toml_edit::ser::to_string_pretty(&config).expect("serde toml to string pretty"); - fs::write(output_file_path, output_content.as_bytes()).unwrap(); + // TODO. + let args = None.unwrap(); + generate_node_config(args).await.expect("generate_config"); } log::info!("Finish"); diff --git a/crates/tools/src/stat/mod.rs b/crates/tools/src/stat/mod.rs index ad8ccef82..e8ca8b5dd 100644 --- a/crates/tools/src/stat/mod.rs +++ b/crates/tools/src/stat/mod.rs @@ -1,8 +1,12 @@ use anyhow::Result; use gw_rpc_client::indexer_client::CkbIndexerClient; -use gw_types::h256::*; -use gw_types::offchain::CompatibleFinalizedTimepoint; -use gw_types::{core::ScriptHashType, offchain::CustodianStat, packed::Script, prelude::*}; +use gw_types::{ + core::ScriptHashType, + h256::*, + offchain::{CompatibleFinalizedTimepoint, CustodianStat}, + packed::Script, + prelude::*, +}; /// Query custodian ckb from ckb-indexer pub async fn stat_custodian_cells( diff --git a/crates/tools/src/sudt/account.rs b/crates/tools/src/sudt/account.rs index 8efeadc9e..630c14710 100644 --- a/crates/tools/src/sudt/account.rs +++ b/crates/tools/src/sudt/account.rs @@ -6,6 +6,7 @@ use gw_config::{BackendType, Config}; use gw_types::{ core::ScriptHashType, packed::{CreateAccount, Fee, L2Transaction, MetaContractArgs, RawL2Transaction, Script}, + prelude::Pack as GwPack, }; use crate::{ diff --git a/crates/tools/src/sudt/transfer.rs b/crates/tools/src/sudt/transfer.rs index a5dc6b7bd..f601b8948 100644 --- a/crates/tools/src/sudt/transfer.rs +++ b/crates/tools/src/sudt/transfer.rs @@ -1,17 +1,27 @@ -use crate::account::{eth_sign, privkey_to_l2_script_hash, read_privkey}; -use crate::godwoken_rpc::GodwokenRpcClient; -use crate::types::ScriptsDeploymentResult; -use crate::utils::message::generate_eip712_message_to_sign; -use crate::utils::transaction::{read_config, wait_for_l2_tx}; +use std::path::Path; + use anyhow::Result; use ckb_jsonrpc_types::JsonBytes; -use ckb_types::bytes::Bytes; -use gw_common::builtins::ETH_REGISTRY_ACCOUNT_ID; -use gw_common::registry_address::RegistryAddress; -use gw_types::packed::{Fee, L2Transaction, RawL2Transaction, SUDTArgs, SUDTTransfer}; -use gw_types::prelude::*; -use gw_types::U256; -use std::path::Path; +use ckb_types::{ + bytes::Bytes, + prelude::{Builder as CKBBuilder, Entity as CKBEntity}, +}; +use gw_common::{builtins::ETH_REGISTRY_ACCOUNT_ID, registry_address::RegistryAddress}; +use gw_types::{ + packed::{Fee, L2Transaction, RawL2Transaction, SUDTArgs, SUDTTransfer}, + prelude::*, + U256, +}; + +use crate::{ + account::{eth_sign, privkey_to_l2_script_hash, read_privkey}, + godwoken_rpc::GodwokenRpcClient, + types::ScriptsDeploymentResult, + utils::{ + message::generate_eip712_message_to_sign, + transaction::{read_config, wait_for_l2_tx}, + }, +}; #[allow(clippy::too_many_arguments)] pub async fn transfer( diff --git a/crates/tools/src/types.rs b/crates/tools/src/types.rs index 852d2bac0..8577cc894 100644 --- a/crates/tools/src/types.rs +++ b/crates/tools/src/types.rs @@ -1,9 +1,9 @@ +use std::{collections::HashMap, path::PathBuf}; + use ckb_fixed_hash::{H160, H256}; use ckb_jsonrpc_types::{CellDep, Script}; use gw_config::GenesisConfig; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::PathBuf; #[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Default)] pub struct SetupConfig { @@ -49,6 +49,7 @@ pub struct UserRollupConfig { pub struct DeployItem { pub script_type_hash: H256, pub cell_dep: CellDep, + pub type_script: Script, } #[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Default)] diff --git a/crates/tools/src/update_cell.rs b/crates/tools/src/update_cell.rs index d3fac57f9..efd9c2378 100644 --- a/crates/tools/src/update_cell.rs +++ b/crates/tools/src/update_cell.rs @@ -2,12 +2,10 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, Result}; use ckb_jsonrpc_types::{Either, OutputsValidator}; -use ckb_types::{packed, prelude::Entity}; -use gw_config::WalletConfig; use gw_rpc_client::{ckb_client::CkbClient, indexer_client::CkbIndexerClient}; use gw_types::{ - offchain::{CellInfo, InputCellInfo}, - packed::{CellInput, OutPoint}, + offchain::CellInfo, + packed::{self, OutPoint}, prelude::*, }; use gw_utils::{ @@ -104,17 +102,12 @@ pub async fn update_cell>(args: UpdateCellArgs<'_, P>) -> Result< .tx_hash(tx_hash.pack()) .index(index.pack()) .build(); - let input = InputCellInfo { - input: CellInput::new_builder() - .previous_output(out_point.clone()) - .build(), - cell: CellInfo { - out_point, - output: existed_cell.clone(), - data: existed_cell_data.clone().into_bytes(), - }, + let input = CellInfo { + out_point, + output: existed_cell.clone(), + data: existed_cell_data.clone().into_bytes(), }; - tx_skeleton.inputs_mut().push(input); + tx_skeleton.inputs_mut().push(input.into()); tx_skeleton .outputs_mut() .push((new_cell, new_cell_data.into())); @@ -133,10 +126,7 @@ pub async fn update_cell>(args: UpdateCellArgs<'_, P>) -> Result< ) .await?; // sign - let wallet = Wallet::from_config(&WalletConfig { - privkey_path: pk_path, - lock: payment_lock.into(), - })?; + let wallet = Wallet::from_privkey_path(&pk_path)?; let tx = wallet.sign_tx_skeleton(tx_skeleton)?; let update_message = format!( "tx hash: {} cell index: 0 size: {}", diff --git a/crates/tools/src/utils/cli_args.rs b/crates/tools/src/utils/cli_args.rs index 07c900bc8..10f882c39 100644 --- a/crates/tools/src/utils/cli_args.rs +++ b/crates/tools/src/utils/cli_args.rs @@ -1,6 +1,31 @@ -use std::convert::TryInto; +use std::str::FromStr; use anyhow::{anyhow, Result}; +use ckb_fixed_hash::{H160, H256}; + +// Like H256/H160 but FromStr allows 0x prefixed values too. + +#[derive(Default)] +pub struct H256Arg(pub H256); + +#[derive(Default)] +pub struct H160Arg(pub H160); + +impl FromStr for H160Arg { + type Err = anyhow::Error; + fn from_str(value: &str) -> Result { + let h = value.strip_prefix("0x").unwrap_or(value).parse()?; + Ok(Self(h)) + } +} + +impl FromStr for H256Arg { + type Err = anyhow::Error; + fn from_str(value: &str) -> Result { + let h = value.strip_prefix("0x").unwrap_or(value).parse()?; + Ok(Self(h)) + } +} pub fn to_h256(input: &str) -> Result<[u8; 32]> { let input = hex::decode(input.trim_start_matches("0x"))?; diff --git a/crates/tools/src/utils/deploy.rs b/crates/tools/src/utils/deploy.rs new file mode 100644 index 000000000..19b8616e7 --- /dev/null +++ b/crates/tools/src/utils/deploy.rs @@ -0,0 +1,122 @@ +use std::{collections::HashSet, path::PathBuf}; + +use anyhow::{ensure, Result}; +use clap::Args; +use gw_rpc_client::{ckb_client::CkbClient, indexer_client::CkbIndexerClient}; +use gw_types::{bytes::Bytes, packed, prelude::*}; +use gw_utils::{ + fee::{collect_payment_cells, fill_tx_fee_with_local}, + genesis_info::CKBGenesisInfo, + local_cells::LocalCellsManager, + transaction_skeleton::TransactionSkeleton, + type_id::type_id_type_script, + wallet::Wallet, +}; + +#[derive(Args)] +pub struct DeployContextArgs { + #[clap(long, default_value = "http://127.0.0.1:8114")] + pub ckb_rpc: String, + #[clap(long)] + pub ckb_indexer_rpc: Option, + /// The private key file path + #[clap(short = 'k', long)] + pub privkey_path: PathBuf, +} + +impl DeployContextArgs { + pub async fn build(&self) -> Result { + let ckb_client = CkbClient::with_url(&self.ckb_rpc)?; + let ckb_indexer_client = if let Some(ref u) = self.ckb_indexer_rpc { + CkbIndexerClient::with_url(u)? + } else { + CkbIndexerClient::from(ckb_client.clone()) + }; + let wallet = Wallet::from_privkey_path(&self.privkey_path)?; + let genesis = CKBGenesisInfo::get(&ckb_client).await?; + + Ok(DeployContext { + ckb_client, + ckb_indexer_client, + wallet, + genesis, + }) + } +} + +pub struct DeployContext { + pub ckb_client: CkbClient, + pub ckb_indexer_client: CkbIndexerClient, + pub wallet: Wallet, + pub genesis: CKBGenesisInfo, +} + +impl DeployContext { + /// Deploy type id cell. + /// + /// Does not wait for the transaction. + pub async fn deploy_type_id_cell( + &self, + lock: packed::Script, + data: Bytes, + local_cells: &LocalCellsManager, + ) -> Result<(packed::Transaction, packed::OutPoint, packed::Script)> { + let payment_cells = collect_payment_cells( + &self.ckb_indexer_client, + self.wallet.lock_script().clone(), + 1, + &HashSet::new(), + local_cells, + ) + .await?; + + ensure!(!payment_cells.is_empty(), "no payment cell"); + + let mut tx = TransactionSkeleton::new([0u8; 32]); + tx.inputs_mut() + .extend(payment_cells.into_iter().map(Into::into)); + + let type_script = type_id_type_script(tx.inputs()[0].input.as_reader(), 0); + tx.add_output(lock.clone(), Some(type_script.clone()), data)?; + + let tx = self.deploy(tx, local_cells).await?; + let hash = tx.hash(); + + Ok(( + tx, + packed::OutPoint::new_builder().tx_hash(hash.pack()).build(), + type_script, + )) + } + + /// Add sighash dep, balance, sign and send (but don't wait). + pub async fn deploy( + &self, + mut tx: TransactionSkeleton, + local_cells: &LocalCellsManager, + ) -> Result { + // Sighash dep. + tx.cell_deps_mut().push(self.genesis.sighash_dep()); + + fill_tx_fee_with_local( + &mut tx, + &self.ckb_indexer_client, + self.wallet.lock_script().clone(), + local_cells, + 1000, + ) + .await?; + + let tx: packed::Transaction = self.wallet.sign_tx_skeleton(tx)?; + let ckb_tx = ckb_types::packed::Transaction::new_unchecked(tx.as_bytes()); + + self.ckb_client + .send_transaction( + ckb_tx.into(), + Some(ckb_jsonrpc_types::OutputsValidator::Passthrough), + ) + .await?; + + Ok(tx) + } +} diff --git a/crates/tools/src/utils/mod.rs b/crates/tools/src/utils/mod.rs index 30b3edf44..1e7a983d9 100644 --- a/crates/tools/src/utils/mod.rs +++ b/crates/tools/src/utils/mod.rs @@ -1,4 +1,5 @@ pub mod cli_args; +pub mod deploy; pub mod message; pub mod sdk; pub mod transaction; diff --git a/crates/tools/src/utils/sdk/constants.rs b/crates/tools/src/utils/sdk/constants.rs index 0ed38ce32..5f44f7ab5 100644 --- a/crates/tools/src/utils/sdk/constants.rs +++ b/crates/tools/src/utils/sdk/constants.rs @@ -1,4 +1,4 @@ -use ckb_types::{core::EpochNumberWithFraction, h256, H256}; +use ckb_types::{h256, H256}; pub const PREFIX_MAINNET: &str = "ckb"; pub const PREFIX_TESTNET: &str = "ckt"; @@ -8,36 +8,12 @@ pub const NETWORK_TESTNET: &str = "ckb_testnet"; pub const NETWORK_STAGING: &str = "ckb_staging"; pub const NETWORK_DEV: &str = "ckb_dev"; -pub const SECP_SIGNATURE_SIZE: usize = 65; - -// Since relative mask -pub const LOCK_TYPE_FLAG: u64 = 1 << 63; -pub const METRIC_TYPE_FLAG_MASK: u64 = 0x6000_0000_0000_0000; -pub const VALUE_MASK: u64 = 0x00ff_ffff_ffff_ffff; -pub const REMAIN_FLAGS_BITS: u64 = 0x1f00_0000_0000_0000; - -// Special cells in genesis transactions: (transaction-index, output-index) -pub const SIGHASH_OUTPUT_LOC: (usize, usize) = (0, 1); -pub const MULTISIG_OUTPUT_LOC: (usize, usize) = (0, 4); -pub const DAO_OUTPUT_LOC: (usize, usize) = (0, 2); -pub const SIGHASH_GROUP_OUTPUT_LOC: (usize, usize) = (1, 0); -pub const MULTISIG_GROUP_OUTPUT_LOC: (usize, usize) = (1, 1); - pub const ONE_CKB: u64 = 100_000_000; -pub const MIN_SECP_CELL_CAPACITY: u64 = 61 * ONE_CKB; -// mainnet,testnet cellbase maturity -pub const CELLBASE_MATURITY: EpochNumberWithFraction = - EpochNumberWithFraction::new_unchecked(4, 0, 1); - -/// "TYPE_ID" in hex (copied from ckb-chain-spec) -pub const TYPE_ID_CODE_HASH: H256 = h256!("0x545950455f4944"); pub const SIGHASH_TYPE_HASH: H256 = h256!("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"); pub const MULTISIG_TYPE_HASH: H256 = h256!("0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8"); -pub const DAO_TYPE_HASH: H256 = - h256!("0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"); /// anyone can pay script mainnet code hash, see: /// @@ -46,33 +22,3 @@ pub const ACP_TYPE_HASH_LINA: H256 = /// anyone can pay script testnet code hash pub const ACP_TYPE_HASH_AGGRON: H256 = h256!("0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356"); - -/// cheque withdraw since value -pub const CHEQUE_CELL_SINCE: u64 = 0xA000000000000006; - -#[cfg(test)] -mod test { - use super::*; - use ckb_types::{ - core::Capacity, - packed::{CellOutput, Script}, - prelude::*, - H160, - }; - - #[test] - fn test_min_capacity() { - let min_secp_cell_capacity = CellOutput::new_builder() - .lock( - Script::new_builder() - .args(H160::default().as_bytes().pack()) - .build(), - ) - .build() - .occupied_capacity(Capacity::zero()) - .unwrap() - .as_u64(); - - assert_eq!(min_secp_cell_capacity, MIN_SECP_CELL_CAPACITY); - } -} diff --git a/crates/tools/src/utils/sdk/mod.rs b/crates/tools/src/utils/sdk/mod.rs index 5e38b2bae..3c75b345f 100644 --- a/crates/tools/src/utils/sdk/mod.rs +++ b/crates/tools/src/utils/sdk/mod.rs @@ -1,13 +1,6 @@ /// The `ckb-sdk` crate contains too many dependencies. /// So we move some source code from https://github.com/nervosnetwork/ckb-sdk-rust pub mod constants; -#[cfg(test)] -pub mod test_utils; -pub mod traits; -pub mod tx_fee; -pub mod types; -pub mod unlock; -pub mod util; +mod types; -pub use ckb_crypto::secp::SECP256K1; -pub use types::{Address, AddressPayload, HumanCapacity, NetworkType, ScriptId}; +pub use types::{Address, AddressPayload, HumanCapacity, NetworkType}; diff --git a/crates/tools/src/utils/sdk/test_utils.rs b/crates/tools/src/utils/sdk/test_utils.rs deleted file mode 100644 index 72ff739a1..000000000 --- a/crates/tools/src/utils/sdk/test_utils.rs +++ /dev/null @@ -1,480 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; - -use anyhow::anyhow; -use ckb_jsonrpc_types::Serialize; -use rand::{thread_rng, Rng}; -use thiserror::Error; - -use crate::utils::sdk::{ - constants::{ - MULTISIG_GROUP_OUTPUT_LOC, MULTISIG_TYPE_HASH, ONE_CKB, SIGHASH_GROUP_OUTPUT_LOC, - SIGHASH_TYPE_HASH, - }, - traits::{ - CellDepResolver, DefaultCellDepResolver, HeaderDepResolver, TransactionDependencyError, - TransactionDependencyProvider, - }, - tx_fee::tx_fee, - ScriptId, -}; -use ckb_hash::blake2b_256; -use ckb_mock_tx_types::{ - MockCellDep, MockInfo, MockInput, MockResourceLoader, MockTransaction, Resource, -}; -use ckb_script::TransactionScriptsVerifier; -use ckb_types::{ - bytes::Bytes, - core::{ - cell::resolve_transaction, BlockView, Capacity, Cycle, DepType, FeeRate, HeaderView, - ScriptHashType, TransactionView, - }, - packed::{Byte32, CellDep, CellInput, CellOutput, OutPoint, OutPointVec, Script, Transaction}, - prelude::*, - H256, -}; - -/// Test utils errors -#[derive(Error, Debug)] -pub enum Error { - #[error("no enough capacity for holding a cell: occupied={occupied}, capacity={capacity}")] - NoEnoughCapacityForCell { occupied: u64, capacity: u64 }, - #[error("transaction fee not enough: {0}")] - NoEnoughFee(String), - #[error("verify script error: {0}")] - VerifyScript(String), - #[error("other error: {0}")] - Other(String), -} - -/// The test context for CKB Rust SDK -#[derive(Clone, Default)] -pub struct Context { - pub inputs: Vec, - pub cell_deps: Vec, - pub header_deps: Vec, - - /// cell dep data hashes - pub dep_data_hashes: Vec, - /// cell dep type script hashes - pub dep_type_hashes: Vec>, - /// For resolve dep group cell dep - pub cell_dep_map: HashMap, -} - -pub struct LiveCellsContext { - pub inputs: Vec, - pub header_deps: Vec, - pub used_inputs: HashSet, -} - -impl Context { - /// When the contract is a lock script will combine the deployed out point - /// with secp256k1_data and map the script id to dep_group cell_dep. The - /// contracts can only be referenced by data hash and with - /// hash_type="data1". - pub fn new(block: &BlockView, contracts: Vec<(&[u8], bool)>) -> Context { - let block_number: u64 = block.number(); - assert_eq!(block_number, 0); - let cell_dep_resolver = DefaultCellDepResolver::from_genesis(block).expect("genesis info"); - let block_hash = block.hash(); - let mut ctx = Context::default(); - for (cell_dep, (tx_idx, output_idx)) in [ - ( - cell_dep_resolver.sighash_dep().unwrap().clone().0, - SIGHASH_GROUP_OUTPUT_LOC, - ), - ( - cell_dep_resolver.multisig_dep().unwrap().clone().0, - MULTISIG_GROUP_OUTPUT_LOC, - ), - ] { - let tx = block.transaction(tx_idx).expect("get tx"); - let (output, data) = tx.output_with_data(output_idx).expect("get output+data"); - ctx.add_cell_dep(cell_dep, output, data, Some(block_hash.clone())); - } - for (code_hash, cell_dep) in [ - ( - SIGHASH_TYPE_HASH, - cell_dep_resolver.sighash_dep().unwrap().0.clone(), - ), - ( - MULTISIG_TYPE_HASH, - cell_dep_resolver.multisig_dep().unwrap().0.clone(), - ), - ] { - ctx.add_cell_dep_map(ScriptId::new_type(code_hash), cell_dep); - } - for tx in block.transactions().iter() { - for (idx, (output, data)) in tx - .outputs() - .into_iter() - .zip(tx.outputs_data().into_iter()) - .enumerate() - { - let cell_dep = CellDep::new_builder() - .out_point(OutPoint::new(tx.hash(), idx as u32)) - .dep_type(DepType::Code.into()) - .build(); - ctx.add_cell_dep(cell_dep, output, data.raw_data(), Some(block_hash.clone())); - } - } - - if !contracts.is_empty() { - let secp_data_out_point = OutPoint::new(block.transaction(0).unwrap().hash(), 3); - for (bin, is_lock) in contracts { - let data_hash = H256::from(blake2b_256(bin)); - let out_point = ctx.deploy_cell(Bytes::from(bin.to_vec())); - if is_lock { - let out_points: OutPointVec = - vec![secp_data_out_point.clone(), out_point].pack(); - let group_out_point = ctx.deploy_cell(out_points.as_bytes()); - let cell_dep = CellDep::new_builder() - .out_point(group_out_point) - .dep_type(DepType::DepGroup.into()) - .build(); - let script_id = ScriptId::new_data1(data_hash); - ctx.add_cell_dep_map(script_id, cell_dep); - } - } - } - ctx.add_header(block.header()); - ctx - } - - /// Adds a live cell to the set. - /// - /// If the set did not have this input present, old live cell is returned. - /// - /// If the set did have this input present, None is returned. - pub fn add_live_cell( - &mut self, - input: CellInput, - output: CellOutput, - data: Bytes, - header: Option, - ) -> Option<(CellOutput, Bytes, Option)> { - for mock_input in &mut self.inputs { - if mock_input.input == input { - let old_output = mock_input.output.clone(); - let old_data = mock_input.data.clone(); - let old_header = mock_input.header.clone(); - mock_input.output = output; - mock_input.data = data; - mock_input.header = header; - return Some((old_output, old_data, old_header)); - } - } - self.inputs.push(MockInput { - input, - output, - data, - header, - }); - None - } - - /// Add a live cell with empty data - pub fn add_simple_live_cell( - &mut self, - out_point: OutPoint, - lock_script: Script, - capacity: Option, - ) -> Option<(CellOutput, Bytes, Option)> { - let input = CellInput::new(out_point, 0); - let capacity = capacity.unwrap_or_else(|| { - let lock_size = 33 + lock_script.args().raw_data().len(); - Capacity::bytes((8 + lock_size) * ONE_CKB as usize) - .unwrap() - .as_u64() - }); - let output = CellOutput::new_builder() - .capacity(capacity.pack()) - .lock(lock_script) - .build(); - self.add_live_cell(input, output, Bytes::default(), None) - } - - /// Deploy a cell - /// return the out-point of the cell - pub fn deploy_cell(&mut self, data: Bytes) -> OutPoint { - let out_point = random_out_point(); - let cell_dep = CellDep::new_builder() - .out_point(out_point.clone()) - .dep_type(DepType::Code.into()) - .build(); - let output = CellOutput::default(); - self.add_cell_dep(cell_dep, output, data, None); - out_point - } - - pub fn add_cell_dep( - &mut self, - cell_dep: CellDep, - output: CellOutput, - data: Bytes, - header: Option, - ) -> Option<(CellOutput, Bytes, Option)> { - let data_hash = H256::from(blake2b_256(data.as_ref())); - let script_hash_opt = output - .type_() - .to_opt() - .map(|script| H256::from(blake2b_256(script.as_slice()))); - for (idx, mock_cell_dep) in self.cell_deps.iter_mut().enumerate() { - if mock_cell_dep.cell_dep == cell_dep { - let old_output = mock_cell_dep.output.clone(); - let old_data = mock_cell_dep.data.clone(); - let old_header = mock_cell_dep.header.clone(); - mock_cell_dep.output = output; - mock_cell_dep.data = data; - mock_cell_dep.header = header; - self.dep_data_hashes[idx] = data_hash; - self.dep_type_hashes[idx] = script_hash_opt; - return Some((old_output, old_data, old_header)); - } - } - self.cell_deps.push(MockCellDep { - cell_dep, - output, - data, - header, - }); - self.dep_data_hashes.push(data_hash); - self.dep_type_hashes.push(script_hash_opt); - None - } - - pub fn add_cell_dep_map(&mut self, script_id: ScriptId, cell_dep: CellDep) -> Option { - self.cell_dep_map.insert(script_id, cell_dep) - } - - pub fn add_header(&mut self, header: HeaderView) { - self.header_deps.push(header); - } - - pub fn get_live_cell(&self, out_point: &OutPoint) -> Option<(CellOutput, Bytes)> { - if let Some(result) = self.get_input(out_point) { - return Some(result); - } - for mock_cell_dep in &self.cell_deps { - if out_point == &mock_cell_dep.cell_dep.out_point() { - return Some((mock_cell_dep.output.clone(), mock_cell_dep.data.clone())); - } - } - None - } - pub fn get_input(&self, out_point: &OutPoint) -> Option<(CellOutput, Bytes)> { - for mock_input in &self.inputs { - if out_point == &mock_input.input.previous_output() { - return Some((mock_input.output.clone(), mock_input.data.clone())); - } - } - None - } - - pub fn to_mock_tx(&self, tx: Transaction) -> MockTransaction { - let mock_info = MockInfo { - inputs: self.inputs.clone(), - cell_deps: self.cell_deps.clone(), - header_deps: self.header_deps.clone(), - }; - MockTransaction { mock_info, tx } - } - - pub fn to_live_cells_context(&self) -> LiveCellsContext { - LiveCellsContext { - inputs: self.inputs.clone(), - header_deps: self.header_deps.clone(), - used_inputs: Default::default(), - } - } - - /// Check if the transaction fee is greater than fee rate - pub fn verify_tx_fee(&self, tx: &TransactionView, fee_rate: u64) -> Result<(), Error> { - let min_fee = FeeRate::from_u64(fee_rate) - .fee(tx.data().as_reader().serialized_size_in_block()) - .as_u64(); - let fee = tx_fee(tx.clone(), self, self).map_err(|err| Error::Other(err.to_string()))?; - if fee < min_fee { - return Err(Error::NoEnoughFee(format!( - "min-fee: {}, actual-fee: {}", - min_fee, fee - ))); - } - Ok(()) - } - - /// Run all scripts in the transaction in ckb-vm - pub fn verify_scripts(&self, tx: TransactionView) -> Result { - let mock_tx = self.to_mock_tx(tx.data()); - let resource = Resource::from_both(&mock_tx, DummyLoader).map_err(Error::VerifyScript)?; - let rtx = resolve_transaction(tx, &mut HashSet::new(), &resource, &resource) - .map_err(|err| Error::VerifyScript(format!("Resolve transaction error: {:?}", err)))?; - - let mut verifier = TransactionScriptsVerifier::new(&rtx, &resource); - verifier.set_debug_printer(|script_hash, message| { - println!("script: {:x}, debug: {}", script_hash, message); - }); - verifier - .verify(u64::max_value()) - .map_err(|err| Error::VerifyScript(format!("Verify script error: {:?}", err))) - } - - /// Verify: - /// * the transaction fee is greater than fee rate - /// * run the transaction in ckb-vm - pub fn verify(&self, tx: TransactionView, fee_rate: u64) -> Result { - self.verify_tx_fee(&tx, fee_rate)?; - self.verify_scripts(tx) - } -} - -impl TransactionDependencyProvider for Context { - // For verify certain cell belong to certain transaction - fn get_transaction( - &self, - _tx_hash: &Byte32, - ) -> Result { - Err(TransactionDependencyError::Other(anyhow!( - "context get_transaction" - ))) - } - // For get the output information of inputs or cell_deps, those cell should be live cell - fn get_cell(&self, out_point: &OutPoint) -> Result { - self.get_live_cell(out_point) - .map(|(output, _)| output) - .ok_or_else(|| TransactionDependencyError::NotFound("cell not found".to_string())) - } - // For get the output data information of inputs or cell_deps - fn get_cell_data(&self, out_point: &OutPoint) -> Result { - self.get_live_cell(out_point) - .map(|(_, data)| data) - .ok_or_else(|| TransactionDependencyError::NotFound("cell data not found".to_string())) - } - // For get the header information of header_deps - fn get_header(&self, _block_hash: &Byte32) -> Result { - Err(TransactionDependencyError::NotFound( - "header not found".to_string(), - )) - } -} - -impl HeaderDepResolver for Context { - fn resolve_by_tx(&self, tx_hash: &Byte32) -> Result, anyhow::Error> { - let mut header_opt = None; - for item in &self.inputs { - if item.input.previous_output().tx_hash() == *tx_hash { - header_opt = item.header.clone(); - } - } - if header_opt.is_none() { - for item in &self.cell_deps { - if item.cell_dep.out_point().tx_hash() == *tx_hash { - header_opt = item.header.clone(); - } - } - } - if let Some(hash) = header_opt { - for mock_header in &self.header_deps { - if hash == mock_header.hash() { - return Ok(Some(mock_header.clone())); - } - } - } - Ok(None) - } - fn resolve_by_number(&self, number: u64) -> Result, anyhow::Error> { - for mock_header in &self.header_deps { - if number == mock_header.number() { - return Ok(Some(mock_header.clone())); - } - } - Ok(None) - } -} - -impl CellDepResolver for Context { - fn resolve(&self, script: &Script) -> Option { - let code_hash: H256 = script.code_hash().unpack(); - let hash_type = script.hash_type(); - let script_id = ScriptId::new( - code_hash.clone(), - ScriptHashType::try_from(hash_type).unwrap(), - ); - if let Some(cell_dep) = self.cell_dep_map.get(&script_id) { - return Some(cell_dep.clone()); - } - if hash_type == ScriptHashType::Type.into() { - for (idx, hash_opt) in self.dep_type_hashes.iter().enumerate() { - if hash_opt.as_ref() == Some(&code_hash) { - return Some(self.cell_deps[idx].cell_dep.clone()); - } - } - } else { - for (idx, hash) in self.dep_data_hashes.iter().enumerate() { - if *hash == code_hash { - return Some(self.cell_deps[idx].cell_dep.clone()); - } - } - } - None - } -} - -struct DummyLoader; -impl MockResourceLoader for DummyLoader { - fn get_header(&mut self, hash: H256) -> Result, String> { - Err(format!("Can not call header getter, hash={:?}", hash)) - } - fn get_live_cell( - &mut self, - out_point: OutPoint, - ) -> Result)>, String> { - Err(format!( - "Can not call live cell getter, out_point={:?}", - out_point - )) - } -} - -pub fn random_out_point() -> OutPoint { - let mut rng = thread_rng(); - let tx_hash = { - let mut buf = [0u8; 32]; - rng.fill(&mut buf); - buf.pack() - }; - OutPoint::new(tx_hash, 0) -} - -#[derive(serde::Serialize)] -pub struct MockRpcResult { - id: u64, - jsonrpc: String, - result: T, -} - -impl MockRpcResult { - pub fn new(result: T) -> Self { - Self { - id: 42, - jsonrpc: "2.0".to_string(), - result, - } - } - - pub fn to_json(&self) -> String { - serde_json::to_string(&self).unwrap() - } -} - -#[cfg(test)] -mod anyhow_tests { - use anyhow::anyhow; - #[test] - fn test_error() { - let error = super::Error::VerifyScript("VerifyScript".to_string()); - let error = anyhow!(error); - assert_eq!("verify script error: VerifyScript", error.to_string()) - } -} diff --git a/crates/tools/src/utils/sdk/traits/default_impls.rs b/crates/tools/src/utils/sdk/traits/default_impls.rs deleted file mode 100644 index b4888c321..000000000 --- a/crates/tools/src/utils/sdk/traits/default_impls.rs +++ /dev/null @@ -1,278 +0,0 @@ -use std::collections::HashMap; - -use ckb_crypto::secp::Pubkey; -use thiserror::Error; - -use ckb_hash::blake2b_256; -use ckb_types::{ - bytes::Bytes, - core::{BlockView, DepType, TransactionView}, - packed::{CellDep, CellOutput, OutPoint, Script}, - prelude::*, - H160, -}; - -use super::OffchainCellDepResolver; -use crate::utils::sdk::traits::{CellDepResolver, Signer, SignerError}; -use crate::utils::sdk::types::ScriptId; -use crate::utils::sdk::util::{serialize_signature, zeroize_privkey}; -use crate::utils::sdk::{ - constants::{ - DAO_OUTPUT_LOC, DAO_TYPE_HASH, MULTISIG_GROUP_OUTPUT_LOC, MULTISIG_OUTPUT_LOC, - MULTISIG_TYPE_HASH, SIGHASH_GROUP_OUTPUT_LOC, SIGHASH_OUTPUT_LOC, SIGHASH_TYPE_HASH, - }, - util::keccak160, -}; -use ckb_crypto::secp::SECP256K1; -use ckb_resource::{ - CODE_HASH_DAO, CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL, - CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL, -}; - -/// Parse Genesis Info errors -#[derive(Error, Debug)] -pub enum ParseGenesisInfoError { - #[error("invalid block number, expected: 0, got: `{0}`")] - InvalidBlockNumber(u64), - #[error("data not found: `{0}`")] - DataHashNotFound(String), - #[error("type not found: `{0}`")] - TypeHashNotFound(String), -} - -/// A cell_dep resolver use genesis info resolve system scripts and can register more cell_dep info. -#[derive(Clone)] -pub struct DefaultCellDepResolver { - offchain: OffchainCellDepResolver, -} -impl DefaultCellDepResolver { - pub fn from_genesis( - genesis_block: &BlockView, - ) -> Result { - let header = genesis_block.header(); - if header.number() != 0 { - return Err(ParseGenesisInfoError::InvalidBlockNumber(header.number())); - } - let mut sighash_type_hash = None; - let mut multisig_type_hash = None; - let mut dao_type_hash = None; - let out_points = genesis_block - .transactions() - .iter() - .enumerate() - .map(|(tx_index, tx)| { - tx.outputs() - .into_iter() - .zip(tx.outputs_data().into_iter()) - .enumerate() - .map(|(index, (output, data))| { - if tx_index == SIGHASH_OUTPUT_LOC.0 && index == SIGHASH_OUTPUT_LOC.1 { - sighash_type_hash = output - .type_() - .to_opt() - .map(|script| script.calc_script_hash()); - let data_hash = CellOutput::calc_data_hash(&data.raw_data()); - if data_hash != CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL.pack() { - log::error!( - "System sighash script code hash error! found: {}, expected: {}", - data_hash, - CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL, - ); - } - } - if tx_index == MULTISIG_OUTPUT_LOC.0 && index == MULTISIG_OUTPUT_LOC.1 { - multisig_type_hash = output - .type_() - .to_opt() - .map(|script| script.calc_script_hash()); - let data_hash = CellOutput::calc_data_hash(&data.raw_data()); - if data_hash != CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL.pack() { - log::error!( - "System multisig script code hash error! found: {}, expected: {}", - data_hash, - CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL, - ); - } - } - if tx_index == DAO_OUTPUT_LOC.0 && index == DAO_OUTPUT_LOC.1 { - dao_type_hash = output - .type_() - .to_opt() - .map(|script| script.calc_script_hash()); - let data_hash = CellOutput::calc_data_hash(&data.raw_data()); - if data_hash != CODE_HASH_DAO.pack() { - log::error!( - "System dao script code hash error! found: {}, expected: {}", - data_hash, - CODE_HASH_DAO, - ); - } - } - OutPoint::new(tx.hash(), index as u32) - }) - .collect::>() - }) - .collect::>(); - - let sighash_type_hash = sighash_type_hash - .ok_or_else(|| "No type hash(sighash) found in txs[0][1]".to_owned()) - .map_err(ParseGenesisInfoError::TypeHashNotFound)?; - let multisig_type_hash = multisig_type_hash - .ok_or_else(|| "No type hash(multisig) found in txs[0][4]".to_owned()) - .map_err(ParseGenesisInfoError::TypeHashNotFound)?; - let dao_type_hash = dao_type_hash - .ok_or_else(|| "No type hash(dao) found in txs[0][2]".to_owned()) - .map_err(ParseGenesisInfoError::TypeHashNotFound)?; - - let sighash_dep = CellDep::new_builder() - .out_point(out_points[SIGHASH_GROUP_OUTPUT_LOC.0][SIGHASH_GROUP_OUTPUT_LOC.1].clone()) - .dep_type(DepType::DepGroup.into()) - .build(); - let multisig_dep = CellDep::new_builder() - .out_point(out_points[MULTISIG_GROUP_OUTPUT_LOC.0][MULTISIG_GROUP_OUTPUT_LOC.1].clone()) - .dep_type(DepType::DepGroup.into()) - .build(); - let dao_dep = CellDep::new_builder() - .out_point(out_points[DAO_OUTPUT_LOC.0][DAO_OUTPUT_LOC.1].clone()) - .build(); - - let mut items = HashMap::default(); - items.insert( - ScriptId::new_type(sighash_type_hash.unpack()), - (sighash_dep, "Secp256k1 blake160 sighash all".to_string()), - ); - items.insert( - ScriptId::new_type(multisig_type_hash.unpack()), - (multisig_dep, "Secp256k1 blake160 multisig all".to_string()), - ); - items.insert( - ScriptId::new_type(dao_type_hash.unpack()), - (dao_dep, "Nervos DAO".to_string()), - ); - let offchain = OffchainCellDepResolver { items }; - Ok(DefaultCellDepResolver { offchain }) - } - pub fn insert( - &mut self, - script_id: ScriptId, - cell_dep: CellDep, - name: String, - ) -> Option<(CellDep, String)> { - self.offchain.items.insert(script_id, (cell_dep, name)) - } - pub fn remove(&mut self, script_id: &ScriptId) -> Option<(CellDep, String)> { - self.offchain.items.remove(script_id) - } - pub fn contains(&self, script_id: &ScriptId) -> bool { - self.offchain.items.contains_key(script_id) - } - pub fn get(&self, script_id: &ScriptId) -> Option<&(CellDep, String)> { - self.offchain.items.get(script_id) - } - pub fn sighash_dep(&self) -> Option<&(CellDep, String)> { - self.get(&ScriptId::new_type(SIGHASH_TYPE_HASH)) - } - pub fn multisig_dep(&self) -> Option<&(CellDep, String)> { - self.get(&ScriptId::new_type(MULTISIG_TYPE_HASH)) - } - pub fn dao_dep(&self) -> Option<&(CellDep, String)> { - self.get(&ScriptId::new_type(DAO_TYPE_HASH)) - } -} - -impl CellDepResolver for DefaultCellDepResolver { - fn resolve(&self, script: &Script) -> Option { - self.offchain.resolve(script) - } -} - -/// A signer use secp256k1 raw key, the id is `blake160(pubkey)`. -#[derive(Default, Clone)] -pub struct SecpCkbRawKeySigner { - keys: HashMap, -} - -impl SecpCkbRawKeySigner { - pub fn new(keys: HashMap) -> SecpCkbRawKeySigner { - SecpCkbRawKeySigner { keys } - } - pub fn new_with_secret_keys(keys: Vec) -> SecpCkbRawKeySigner { - let mut signer = SecpCkbRawKeySigner::default(); - for key in keys { - signer.add_secret_key(key); - } - signer - } - pub fn add_secret_key(&mut self, key: secp256k1::SecretKey) { - let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &key); - let hash160 = H160::from_slice(&blake2b_256(&pubkey.serialize()[..])[0..20]) - .expect("Generate hash(H160) from pubkey failed"); - self.keys.insert(hash160, key); - } - - /// Create SecpkRawKeySigner from secret keys for ethereum algorithm. - pub fn new_with_ethereum_secret_keys(keys: Vec) -> SecpCkbRawKeySigner { - let mut signer = SecpCkbRawKeySigner::default(); - for key in keys { - signer.add_ethereum_secret_key(key); - } - signer - } - /// Add a ethereum secret key - pub fn add_ethereum_secret_key(&mut self, key: secp256k1::SecretKey) { - let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &key); - let hash160 = keccak160(Pubkey::from(pubkey).as_ref()); - self.keys.insert(hash160, key); - } -} - -impl Signer for SecpCkbRawKeySigner { - fn match_id(&self, id: &[u8]) -> bool { - id.len() == 20 && self.keys.contains_key(&H160::from_slice(id).unwrap()) - } - - fn sign( - &self, - id: &[u8], - message: &[u8], - recoverable: bool, - _tx: &TransactionView, - ) -> Result { - if !self.match_id(id) { - return Err(SignerError::IdNotFound); - } - if message.len() != 32 { - return Err(SignerError::InvalidMessage(format!( - "expected length: 32, got: {}", - message.len() - ))); - } - let msg = secp256k1::Message::from_slice(message).expect("Convert to message failed"); - let key = self.keys.get(&H160::from_slice(id).unwrap()).unwrap(); - if recoverable { - let sig = SECP256K1.sign_ecdsa_recoverable(&msg, key); - Ok(Bytes::from(serialize_signature(&sig).to_vec())) - } else { - let sig = SECP256K1.sign_ecdsa(&msg, key); - Ok(Bytes::from(sig.serialize_compact().to_vec())) - } - } -} - -impl Drop for SecpCkbRawKeySigner { - fn drop(&mut self) { - for (_, mut secret_key) in self.keys.drain() { - zeroize_privkey(&mut secret_key); - } - } -} -#[cfg(test)] -mod anyhow_tests { - use anyhow::anyhow; - #[test] - fn test_parse_genesis_info_error() { - let error = super::ParseGenesisInfoError::DataHashNotFound("DataHashNotFound".to_string()); - let error = anyhow!(error); - assert_eq!("data not found: `DataHashNotFound`", error.to_string()); - } -} diff --git a/crates/tools/src/utils/sdk/traits/dummy_impls.rs b/crates/tools/src/utils/sdk/traits/dummy_impls.rs deleted file mode 100644 index 8b1378917..000000000 --- a/crates/tools/src/utils/sdk/traits/dummy_impls.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/tools/src/utils/sdk/traits/mod.rs b/crates/tools/src/utils/sdk/traits/mod.rs deleted file mode 100644 index e2418b31f..000000000 --- a/crates/tools/src/utils/sdk/traits/mod.rs +++ /dev/null @@ -1,429 +0,0 @@ -//! The traits defined here is intent to describe the requirements of current -//! library code and only implemented the trait in upper level code. - -pub mod default_impls; -pub mod dummy_impls; -pub mod offchain_impls; - -pub use default_impls::{DefaultCellDepResolver, SecpCkbRawKeySigner}; -pub use offchain_impls::{ - OffchainCellDepResolver, OffchainHeaderDepResolver, OffchainTransactionDependencyProvider, -}; - -use thiserror::Error; - -use ckb_hash::blake2b_256; -use ckb_traits::{BlockEpoch, CellDataProvider, EpochProvider, HeaderProvider}; -use ckb_types::{ - bytes::Bytes, - core::{ - cell::{CellMetaBuilder, CellProvider, CellStatus, HeaderChecker}, - error::OutPointError, - EpochExt, HeaderView, TransactionView, - }, - packed::{Byte32, CellDep, CellOutput, OutPoint, Script}, - prelude::*, -}; - -use crate::utils::sdk::util::is_mature; - -/// Signer errors -#[derive(Error, Debug)] -pub enum SignerError { - #[error("the id is not found in the signer")] - IdNotFound, - - #[error("invalid message, reason: `{0}`")] - InvalidMessage(String), - - #[error("invalid transaction, reason: `{0}`")] - InvalidTransaction(String), - - // maybe hardware wallet error or io error - #[error(transparent)] - Other(#[from] anyhow::Error), -} - -/// A signer abstraction, support signer type: -/// * secp256k1 ckb signer -/// * secp256k1 eth signer -/// * RSA signer -/// * Hardware wallet signer -pub trait Signer { - /// typecial id are blake160(pubkey) and keccak256(pubkey)[12..20] - fn match_id(&self, id: &[u8]) -> bool; - - /// `message` type is variable length, because different algorithm have - /// different length of message: - /// * secp256k1 => 256bits - /// * RSA => 512bits (when key size is 1024bits) - fn sign( - &self, - id: &[u8], - message: &[u8], - recoverable: bool, - tx: &TransactionView, - ) -> Result; -} - -/// Transaction dependency provider errors -#[derive(Error, Debug)] -pub enum TransactionDependencyError { - #[error("the resource is not found in the provider: `{0}`")] - NotFound(String), - - #[error(transparent)] - Other(#[from] anyhow::Error), -} - -/// Provider dependency information of a transaction: -/// * inputs -/// * cell_deps -/// * header_deps -pub trait TransactionDependencyProvider { - /// For verify certain cell belong to certain transaction - fn get_transaction( - &self, - tx_hash: &Byte32, - ) -> Result; - /// For get the output information of inputs or cell_deps, those cell should be live cell - fn get_cell(&self, out_point: &OutPoint) -> Result; - /// For get the output data information of inputs or cell_deps - fn get_cell_data(&self, out_point: &OutPoint) -> Result; - /// For get the header information of header_deps - fn get_header(&self, block_hash: &Byte32) -> Result; -} - -// Implement CellDataProvider trait is currently for `DaoCalculator` -impl CellDataProvider for &dyn TransactionDependencyProvider { - fn get_cell_data(&self, out_point: &OutPoint) -> Option { - TransactionDependencyProvider::get_cell_data(*self, out_point).ok() - } - fn get_cell_data_hash(&self, out_point: &OutPoint) -> Option { - TransactionDependencyProvider::get_cell_data(*self, out_point) - .ok() - .map(|data| blake2b_256(data.as_ref()).pack()) - } -} -// Implement CellDataProvider trait is currently for `DaoCalculator` -impl EpochProvider for &dyn TransactionDependencyProvider { - fn get_epoch_ext(&self, _block_header: &HeaderView) -> Option { - None - } - fn get_block_epoch(&self, _block_header: &HeaderView) -> Option { - None - } -} -// Implement CellDataProvider trait is currently for `DaoCalculator` -impl HeaderProvider for &dyn TransactionDependencyProvider { - fn get_header(&self, hash: &Byte32) -> Option { - TransactionDependencyProvider::get_header(*self, hash).ok() - } -} -impl HeaderChecker for &dyn TransactionDependencyProvider { - fn check_valid(&self, block_hash: &Byte32) -> Result<(), OutPointError> { - TransactionDependencyProvider::get_header(*self, block_hash) - .map(|_| ()) - .map_err(|_| OutPointError::InvalidHeader(block_hash.clone())) - } -} -impl CellProvider for &dyn TransactionDependencyProvider { - fn cell(&self, out_point: &OutPoint, _eager_load: bool) -> CellStatus { - match self.get_transaction(&out_point.tx_hash()) { - Ok(tx) => tx - .outputs() - .get(out_point.index().unpack()) - .map(|cell| { - let data = tx - .outputs_data() - .get(out_point.index().unpack()) - .expect("output data"); - - let cell_meta = CellMetaBuilder::from_cell_output(cell, data.unpack()) - .out_point(out_point.to_owned()) - .build(); - - CellStatus::live_cell(cell_meta) - }) - .unwrap_or(CellStatus::Unknown), - Err(_err) => CellStatus::Unknown, - } - } -} - -/// Cell collector errors -#[derive(Error, Debug)] -pub enum CellCollectorError { - #[error(transparent)] - Internal(anyhow::Error), - - #[error(transparent)] - Other(anyhow::Error), -} - -#[derive(Debug, Clone)] -pub struct LiveCell { - pub output: CellOutput, - pub output_data: Bytes, - pub out_point: OutPoint, - pub block_number: u64, - pub tx_index: u32, -} - -/// The value range option: `start <= value < end` -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct ValueRangeOption { - pub start: u64, - pub end: u64, -} -impl ValueRangeOption { - pub fn new(start: u64, end: u64) -> ValueRangeOption { - ValueRangeOption { start, end } - } - pub fn new_exact(value: u64) -> ValueRangeOption { - ValueRangeOption { - start: value, - end: value + 1, - } - } - pub fn new_min(start: u64) -> ValueRangeOption { - ValueRangeOption { - start, - end: u64::max_value(), - } - } - pub fn match_value(&self, value: u64) -> bool { - self.start <= value && value < self.end - } -} - -/// The primary serach script type -/// * if primary script type is `lock` then secondary script type is `type` -/// * if primary script type is `type` then secondary script type is `lock` -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum PrimaryScriptType { - Lock, - Type, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum MaturityOption { - Mature, - Immature, - Both, -} -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum QueryOrder { - Desc, - Asc, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct CellQueryOptions { - pub primary_script: Script, - pub primary_type: PrimaryScriptType, - pub with_data: Option, - - // Options for SearchKeyFilter - pub secondary_script: Option