Skip to content

Commit

Permalink
Optimize ckb-mock-tx-types
Browse files Browse the repository at this point in the history
  • Loading branch information
mohanson committed Jul 15, 2024
1 parent 9fe71ff commit 25eef61
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 25 deletions.
3 changes: 1 addition & 2 deletions ckb-debugger/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::misc::DummyResourceLoader;
use ckb_chain_spec::consensus::ConsensusBuilder;
use ckb_mock_tx_types::{MockTransaction, ReprMockTransaction, Resource};
use ckb_script::{ScriptGroupType, TransactionScriptsVerifier, TxVerifyEnv};
Expand All @@ -20,7 +19,7 @@ pub fn run(
script_hash: &Byte32,
max_cycle: Cycle,
) -> Result<Cycle, Box<dyn std::error::Error>> {
let resource = Resource::from_both(mock_tx, DummyResourceLoader {})?;
let resource = Resource::from_mock_tx(mock_tx)?;
let resolve_transaction =
resolve_transaction(mock_tx.core_transaction(), &mut HashSet::new(), &resource, &resource)?;
let hardforks = HardForks { ckb2021: CKB2021::new_dev_default(), ckb2023: CKB2023::new_dev_default() };
Expand Down
7 changes: 3 additions & 4 deletions ckb-debugger/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ use ckb_chain_spec::consensus::ConsensusBuilder;
#[cfg(target_family = "unix")]
use ckb_debugger::Stdio;
use ckb_debugger::{
get_script_hash_by_index, pre_check, DummyResourceLoader, ElfDumper, FileOperation, FileStream,
HumanReadableCycles, MachineAnalyzer, MachineAssign, MachineOverlap, MachineProfile, MachineStepLog, Random,
TimeNow,
get_script_hash_by_index, pre_check, ElfDumper, FileOperation, FileStream, HumanReadableCycles, MachineAnalyzer,
MachineAssign, MachineOverlap, MachineProfile, MachineStepLog, Random, TimeNow,
};
use ckb_debugger::{Embed, GdbStubHandler, GdbStubHandlerEventLoop};
use ckb_mock_tx_types::{MockCellDep, MockInfo, MockInput, MockTransaction, ReprMockTransaction, Resource};
Expand Down Expand Up @@ -319,7 +318,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
"2" => ScriptVersion::V2,
_ => panic!("Wrong script version"),
};
let verifier_resource = Resource::from_both(&verifier_mock_tx, DummyResourceLoader {})?;
let verifier_resource = Resource::from_mock_tx(&verifier_mock_tx)?;
let verifier_resolve_transaction = resolve_transaction(
verifier_mock_tx.core_transaction(),
&mut HashSet::new(),
Expand Down
50 changes: 31 additions & 19 deletions ckb-mock-tx-types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/// This Rust code provides a framework for working with mock transactions in a blockchain context, specifically for
/// the CKB (Nervos) blockchain. It defines various structs and methods for handling mock cell dependencies, inputs,
/// transactions, and resources. The code also includes serialization and deserialization capabilities for these mock
/// types, making it easier to work with mock data in a structured and consistent manner.
use ckb_jsonrpc_types as json_types;
use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider};
use ckb_types::{
Expand All @@ -14,6 +18,7 @@ use ckb_types::{
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Represents a cell dependency with its associated data and optional header.
#[derive(Clone, Default)]
pub struct MockCellDep {
pub cell_dep: CellDep,
Expand All @@ -22,6 +27,7 @@ pub struct MockCellDep {
pub header: Option<Byte32>,
}

/// Represents a transaction input with its associated data and optional header.
#[derive(Clone, Default)]
pub struct MockInput {
pub input: CellInput,
Expand All @@ -30,6 +36,7 @@ pub struct MockInput {
pub header: Option<Byte32>,
}

/// Aggregates multiple MockInput and MockCellDep instances, along with header dependencies and extensions.
#[derive(Clone, Default)]
pub struct MockInfo {
pub inputs: Vec<MockInput>,
Expand All @@ -38,14 +45,15 @@ pub struct MockInfo {
pub extensions: Vec<(Byte32, Bytes)>,
}

/// A wrapper transaction with mock inputs and deps
/// A wrapper transaction with mock inputs and deps.
#[derive(Clone, Default)]
pub struct MockTransaction {
pub mock_info: MockInfo,
pub tx: Transaction,
}

impl MockTransaction {
/// Retrieve the input cell data for a given cell input.
pub fn get_input_cell<F: FnMut(OutPoint) -> Result<Option<(CellOutput, Bytes, Option<Byte32>)>, String>>(
&self,
input: &CellInput,
Expand All @@ -59,6 +67,7 @@ impl MockTransaction {
live_cell_getter(input.previous_output())
}

/// Retrieve the cell dependency data for a given out point.
pub fn get_dep_cell<F: FnMut(OutPoint) -> Result<Option<(CellOutput, Bytes, Option<Byte32>)>, String>>(
&self,
out_point: &OutPoint,
Expand All @@ -72,6 +81,7 @@ impl MockTransaction {
live_cell_getter(out_point.clone())
}

/// Retrieve the header for a given block hash.
pub fn get_header<F: FnMut(H256) -> Result<Option<HeaderView>, String>>(
&self,
block_hash: &H256,
Expand All @@ -85,17 +95,19 @@ impl MockTransaction {
header_getter(block_hash.clone())
}

/// Generate the core transaction
/// Generate the core transaction.
pub fn core_transaction(&self) -> TransactionView {
self.tx.clone().into_view()
}
}

/// The trait defines methods for loading headers and live cells.
pub trait MockResourceLoader {
fn get_header(&mut self, hash: H256) -> Result<Option<HeaderView>, String>;
fn get_live_cell(&mut self, out_point: OutPoint) -> Result<Option<(CellOutput, Bytes, Option<Byte32>)>, String>;
}

/// The struct holds the necessary cells, headers, and extensions for validating a transaction.
#[derive(Clone)]
pub struct Resource {
required_cells: HashMap<OutPoint, CellMeta>,
Expand All @@ -104,25 +116,19 @@ pub struct Resource {
}

impl Resource {
/// Create a resource from a mock transaction.
pub fn from_mock_tx(mock_tx: &MockTransaction) -> Result<Resource, String> {
Self::from_both(mock_tx, DummyResourceLoader {})
Self::from_both(mock_tx, &mut DummyResourceLoader {})
}

/// Create a resource from both mock transaction and a resource loader.
#[allow(clippy::mutable_key_type)]
pub fn from_both<L: MockResourceLoader>(mock_tx: &MockTransaction, loader: L) -> Result<Resource, String> {
Self::from_loader_and_block_extensions(mock_tx, loader, HashMap::default())
}

#[allow(clippy::mutable_key_type)]
pub fn from_loader_and_block_extensions<L: MockResourceLoader>(
mock_tx: &MockTransaction,
mut loader: L,
block_extensions: HashMap<Byte32, packed::Bytes>,
) -> Result<Resource, String> {
pub fn from_both<L: MockResourceLoader>(mock_tx: &MockTransaction, loader: &mut L) -> Result<Resource, String> {
let tx = mock_tx.core_transaction();
let mut required_cells = HashMap::default();
let mut required_headers = HashMap::default();

// Process each input to gather required cells.
for input in tx.inputs().into_iter() {
let (output, data, header) = mock_tx
.get_input_cell(&input, |out_point| loader.get_live_cell(out_point))?
Expand All @@ -134,11 +140,12 @@ impl Resource {
required_cells.insert(input.previous_output(), cell_meta);
}

// Process each cell dependency to gather required cells.
for cell_dep in tx.cell_deps().into_iter() {
let (output, data, header) = mock_tx
.get_dep_cell(&cell_dep.out_point(), |out_point| loader.get_live_cell(out_point))?
.ok_or_else(|| format!("Can not get CellOutput by dep={}", cell_dep))?;
// Handle dep group
// Handle dep group.
if cell_dep.dep_type() == DepType::DepGroup.into() {
for sub_out_point in OutPointVec::from_slice(&data)
.map_err(|err| format!("Parse dep group data error: {}", err))?
Expand All @@ -162,22 +169,24 @@ impl Resource {
required_cells.insert(cell_dep.out_point(), cell_meta);
}

// Process each header dependency to gather required headers.
for block_hash in tx.header_deps().into_iter() {
let header = mock_tx
.get_header(&block_hash.unpack(), |block_hash| loader.get_header(block_hash))?
.ok_or_else(|| format!("Can not get header: {:x}", block_hash))?;
required_headers.insert(block_hash, header);
}

let mut extensions: HashMap<Byte32, packed::Bytes> =
// Merge block extensions.
let block_extensions: HashMap<Byte32, packed::Bytes> =
mock_tx.mock_info.extensions.iter().map(|(hash, data)| (hash.clone(), data.pack())).collect();
extensions.extend(block_extensions);

Ok(Resource { required_cells, required_headers, block_extensions: extensions })
Ok(Resource { required_cells, required_headers, block_extensions })
}

/// Build transaction info from a given header.
fn build_transaction_info(header: Option<Byte32>) -> TransactionInfo {
// Only block hash might be used by script syscalls
// Only block hash might be used by script syscalls.
TransactionInfo::new(0, EpochNumberWithFraction::new(0, 0, 1800), header.unwrap_or_default(), 0)
}
}
Expand Down Expand Up @@ -244,6 +253,8 @@ pub struct ReprMockInfo {
pub extensions: Vec<(H256, json_types::JsonBytes)>,
}

/// The structs and their implementations provide serialization and deserialization capabilities for mock cell
/// dependencies, inputs, and transactions.
#[derive(Clone, Serialize, Deserialize)]
pub struct ReprMockTransaction {
pub mock_info: ReprMockInfo,
Expand Down Expand Up @@ -328,7 +339,7 @@ impl From<ReprMockInfo> for MockInfo {
.header_deps
.into_iter()
.map(|json_header| {
// Keep the user given hash
// Keep the user given hash.
let hash = json_header.hash.pack();
HeaderView::from(json_header).fake_hash(hash)
})
Expand All @@ -350,6 +361,7 @@ impl From<ReprMockTransaction> for MockTransaction {
}
}

/// Dummy resource loader used for testing purposes.
pub struct DummyResourceLoader {}

impl MockResourceLoader for DummyResourceLoader {
Expand Down

0 comments on commit 25eef61

Please sign in to comment.