Skip to content

Commit

Permalink
Merge pull request #304 from TheWaWaR/add-mock-tx-dump-sub-command
Browse files Browse the repository at this point in the history
feat: Add a sub-command to mock-tx for dump standalone mock transaction
  • Loading branch information
TheWaWaR authored Jul 27, 2020
2 parents 6deb6d1 + bfd1518 commit 6622443
Showing 1 changed file with 173 additions and 2 deletions.
175 changes: 173 additions & 2 deletions src/subcommands/mock_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ use std::fs;
use std::io::{Read, Write};
use std::path::PathBuf;

use ckb_jsonrpc_types as json_types;
use ckb_sdk::{
GenesisInfo, HttpRpcClient, MockCellDep, MockInfo, MockInput, MockResourceLoader,
MockTransaction, MockTransactionHelper, ReprMockTransaction,
MockTransaction, MockTransactionHelper, ReprMockCellDep, ReprMockInfo, ReprMockInput,
ReprMockTransaction,
};
use ckb_types::{
bytes::Bytes,
core::{
capacity_bytes, Capacity, HeaderBuilder, HeaderView, ScriptHashType, TransactionBuilder,
},
h256,
packed::{CellDep, CellInput, CellOutput, OutPoint, Script},
packed::{self, CellDep, CellInput, CellOutput, OutPoint, Script},
prelude::*,
H160, H256,
};
Expand Down Expand Up @@ -72,6 +74,30 @@ impl<'a> MockTxSubCommand<'a> {
.clone()
.about("Completed mock transaction data file (format: json)"),
),
App::new("dump")
.about("Dump all on-chain data(inputs/cell_deps/header_deps) into mock_info")
.arg(
Arg::with_name("tx-hash")
.long("tx-hash")
.takes_value(true)
.validator(|input| FixedHashParser::<H256>::default().validate(input))
.required_unless("tx-file")
.conflicts_with("tx-file")
.about("The hash of transaction which is on the chain"),
)
.arg(
arg_tx_file
.clone()
.required_unless("tx-hash")
.conflicts_with("tx-hash")
.about("CKB transaction data file (format: json)"),
)
.arg(
arg_output_file
.clone()
.required(true)
.about("Dumped mock transaction data file (format: json)"),
),
App::new("verify")
.about("Verify a mock transaction in local")
.arg(arg_tx_file.clone()),
Expand Down Expand Up @@ -225,6 +251,151 @@ impl<'a> CliSubCommand for MockTxSubCommand<'a> {
Ok(Output::new_output(resp))
}
}
("dump", Some(m)) => {
let output_path: PathBuf =
FilePathParser::new(false).from_matches(m, "output-file")?;
let tx_hash_opt: Option<H256> =
FixedHashParser::<H256>::default().from_matches_opt(m, "tx-hash", false)?;
let tx_file_opt: Option<PathBuf> =
FilePathParser::new(true).from_matches_opt(m, "tx-file", false)?;

let src_tx: json_types::Transaction = if let Some(path) = tx_file_opt {
let mut content = String::new();
let mut file = fs::File::open(path).map_err(|err| err.to_string())?;
file.read_to_string(&mut content)
.map_err(|err| err.to_string())?;
serde_json::from_str(content.as_str()).map_err(|err| err.to_string())?
} else if let Some(tx_hash) = tx_hash_opt {
self.rpc_client
.get_transaction(tx_hash.clone())?
.map(|tx_with_status| {
packed::Transaction::from(tx_with_status.transaction.inner)
})
.ok_or_else(|| format!("Transaction not found on chain: {:x}", tx_hash))?
.into()
} else {
return Err(String::from("<tx-hash> or <tx-file> is required"));
};
fn load_output_and_data(
rpc_client: &mut HttpRpcClient,
out_point: json_types::OutPoint,
) -> Result<(json_types::CellOutput, json_types::JsonBytes, H256), String>
{
let tx_hash = out_point.tx_hash;
let index = out_point.index.value() as usize;
let (tx, block_hash) = rpc_client
.get_transaction(tx_hash.clone())?
.filter(|tx_with_status| tx_with_status.tx_status.block_hash.is_some())
.map(|tx_with_status| {
let tx = json_types::Transaction::from(packed::Transaction::from(
tx_with_status.transaction.inner,
));
let block_hash = tx_with_status
.tx_status
.block_hash
.expect("block_hash exists");
(tx, block_hash)
})
.ok_or_else(|| {
format!("transaction not exists or not mined: {:x}", tx_hash)
})?;
let output = tx.outputs.get(index).cloned().ok_or_else(|| {
format!(
"can not found output tx-hash={:x}, index={}",
tx_hash, index
)
})?;
let data = tx.outputs_data.get(index).cloned().ok_or_else(|| {
format!("can not found data tx-hash={:x}, index={}", tx_hash, index)
})?;
Ok((output, data, block_hash))
}
let mock_inputs = src_tx
.inputs
.iter()
.map(|input| {
let (output, data, block_hash) =
load_output_and_data(self.rpc_client, input.previous_output.clone())?;
Ok(ReprMockInput {
input: input.clone(),
output,
data,
block_hash: Some(block_hash),
})
})
.collect::<Result<Vec<_>, String>>()?;
let mock_cell_deps = src_tx
.cell_deps
.iter()
.flat_map(|cell_dep| {
let (output, data, block_hash) =
match load_output_and_data(self.rpc_client, cell_dep.out_point.clone())
{
Ok((output, data, block_hash)) => (output, data, block_hash),
Err(err) => return vec![Err(err)],
};
let mut cell_deps = if cell_dep.dep_type == json_types::DepType::DepGroup {
let out_points = match packed::OutPointVec::from_slice(data.as_bytes())
{
Ok(out_points) => out_points,
Err(err) => return vec![Err(err.to_string())],
};
out_points
.into_iter()
.map(json_types::OutPoint::from)
.map(|out_point| {
let (output, data, block_hash) =
load_output_and_data(self.rpc_client, out_point.clone())?;
Ok(ReprMockCellDep {
cell_dep: json_types::CellDep {
out_point,
dep_type: json_types::DepType::Code,
},
output,
data,
block_hash: Some(block_hash),
})
})
.collect::<Vec<_>>()
} else {
Vec::new()
};
cell_deps.push(Ok(ReprMockCellDep {
cell_dep: cell_dep.clone(),
output,
data,
block_hash: Some(block_hash),
}));
cell_deps
})
.collect::<Result<Vec<_>, String>>()?;
let mock_header_deps = src_tx
.header_deps
.iter()
.map(|block_hash| {
self.rpc_client
.get_header(block_hash.clone())?
.map(HeaderView::from)
.map(json_types::HeaderView::from)
.ok_or_else(|| format!("header not exists: {:x}", block_hash))
})
.collect::<Result<Vec<_>, String>>()?;
let repr_tx = ReprMockTransaction {
mock_info: ReprMockInfo {
inputs: mock_inputs,
cell_deps: mock_cell_deps,
header_deps: mock_header_deps,
},
tx: src_tx,
};
let content =
serde_json::to_string_pretty(&repr_tx).map_err(|err| err.to_string())?;
let mut out_file = fs::File::create(output_path).map_err(|err| err.to_string())?;
out_file
.write_all(content.as_bytes())
.map_err(|err| err.to_string())?;
Ok(Output::new_success())
}
("verify", Some(m)) => {
let (mock_tx, cycle) = complete_tx(m, false, true)?;
let tx_hash: H256 = mock_tx.core_transaction().hash().unpack();
Expand Down

0 comments on commit 6622443

Please sign in to comment.