From 077096aafbd77bc627f7e8b304076dea26c58fab Mon Sep 17 00:00:00 2001 From: Eason Date: Tue, 7 Nov 2023 16:34:28 +0800 Subject: [PATCH 1/2] fix: rlp decode of `SignedTransaction` with interoperation signature --- core/api/src/jsonrpc/impl/web3.rs | 105 ++-------------------------- core/api/src/jsonrpc/web3_types.rs | 2 +- core/consensus/benches/bench_wal.rs | 2 +- core/consensus/src/wal.rs | 2 +- core/storage/benches/bench.rs | 2 +- core/storage/src/tests/mod.rs | 2 +- protocol/src/codec/transaction.rs | 19 +++-- protocol/src/types/batch.rs | 2 +- protocol/src/types/mod.rs | 3 + protocol/src/types/transaction.rs | 42 ++++++----- 10 files changed, 51 insertions(+), 130 deletions(-) diff --git a/core/api/src/jsonrpc/impl/web3.rs b/core/api/src/jsonrpc/impl/web3.rs index 3e72e1b62..60d939d7c 100644 --- a/core/api/src/jsonrpc/impl/web3.rs +++ b/core/api/src/jsonrpc/impl/web3.rs @@ -1,26 +1,19 @@ use std::{sync::Arc, time::Duration}; -use ckb_types::core::cell::{CellProvider, CellStatus}; -use ckb_types::prelude::Entity; use jsonrpsee::core::RpcResult; use common_apm::metrics_rpc; -use protocol::traits::{APIAdapter, Context, Interoperation}; +use protocol::traits::{APIAdapter, Context}; use protocol::types::{ - Block, BlockNumber, Bytes, CellDepWithPubKey, EthAccountProof, Hash, Hasher, Header, Hex, - Proposal, Receipt, SignatureComponents, SignatureR, SignatureS, SignedTransaction, TxResp, - UnverifiedTransaction, BASE_FEE_PER_GAS, H160, H256, MAX_FEE_HISTORY, MAX_RPC_GAS_CAP, - MIN_TRANSACTION_GAS_LIMIT, U256, + Block, BlockNumber, Bytes, EthAccountProof, Hash, Header, Hex, Proposal, Receipt, + SignedTransaction, TxResp, UnverifiedTransaction, BASE_FEE_PER_GAS, H160, H256, + MAX_FEE_HISTORY, MAX_RPC_GAS_CAP, MIN_TRANSACTION_GAS_LIMIT, U256, }; use protocol::{ - async_trait, ckb_blake2b_256, codec::ProtocolCodec, lazy::PROTOCOL_VERSION, tokio::time::sleep, - ProtocolResult, MEMPOOL_REFRESH_TIMEOUT, + async_trait, codec::ProtocolCodec, lazy::PROTOCOL_VERSION, tokio::time::sleep, ProtocolResult, + MEMPOOL_REFRESH_TIMEOUT, }; -use core_executor::system_contract::DataProvider; -use core_interoperation::utils::is_dummy_out_point; -use core_interoperation::InteroperationImpl; - use crate::jsonrpc::web3_types::{ BlockCount, BlockId, FeeHistoryEmpty, FeeHistoryWithReward, FeeHistoryWithoutReward, RichTransactionOrHash, Web3Block, Web3CallRequest, Web3FeeHistory, Web3Filter, Web3Log, @@ -191,80 +184,6 @@ impl Web3RpcImpl { reward, )) } - - async fn extract_interoperation_tx_sender( - &self, - utx: &UnverifiedTransaction, - signature: &SignatureComponents, - ) -> RpcResult { - // Call CKB-VM mode - if signature.r[0] == 0 { - let r = rlp::decode::(&signature.r[1..]) - .map_err(|e| RpcError::DecodeInteroperationSigR(e.to_string()))?; - - return Ok(Hasher::digest(&r.pub_key).into()); - } - - // Verify by CKB-VM mode - let r = SignatureR::decode(&signature.r) - .map_err(|e| RpcError::DecodeInteroperationSigR(e.to_string()))?; - let s = SignatureS::decode(&signature.s) - .map_err(|e| RpcError::DecodeInteroperationSigS(e.to_string()))?; - let address_source = r.address_source(); - - let ckb_tx_view = - InteroperationImpl::dummy_transaction(r.clone(), s, Some(utx.signature_hash(true).0)); - let dummy_input = r.dummy_input(); - - let input = ckb_tx_view - .inputs() - .get(address_source.index as usize) - .ok_or(RpcError::InvalidAddressSource)?; - - log::debug!("[mempool]: verify interoperation tx sender \ntx view \n{:?}\ndummy input\n {:?}\naddress source\n{:?}\n", ckb_tx_view, dummy_input, address_source); - - // Dummy input mode - if is_dummy_out_point(&input.previous_output()) { - log::debug!("[mempool]: verify interoperation tx dummy input mode."); - - if let Some(cell) = dummy_input { - if address_source.type_ == 1 && cell.type_script.is_none() { - return Err(RpcError::InvalidAddressSource.into()); - } - - let script_hash = if address_source.type_ == 0 { - cell.lock_script_hash() - } else { - cell.type_script_hash().unwrap() - }; - - return Ok(Hasher::digest(script_hash).into()); - } - - return Err(RpcError::MissingDummyInputCell.into()); - } - - // Reality input mode - let root = self - .adapter - .get_image_cell_root(Context::new()) - .await - .map_err(|e| RpcError::Internal(e.to_string()))?; - match DataProvider::new(root).cell(&input.previous_output(), true) { - CellStatus::Live(cell) => { - let script_hash = if address_source.type_ == 0 { - ckb_blake2b_256(cell.cell_output.lock().as_slice()) - } else if let Some(type_script) = cell.cell_output.type_().to_opt() { - ckb_blake2b_256(type_script.as_slice()) - } else { - return Err(RpcError::InvalidAddressSource.into()); - }; - - Ok(Hasher::digest(script_hash).into()) - } - _ => Err(RpcError::CannotFindImageCell.into()), - } - } } #[async_trait] @@ -297,17 +216,7 @@ impl Web3RpcServer for Web3RpcImpl { utx.check_hash() .map_err(|e| RpcError::Internal(e.to_string()))?; - let interoperation_sender = if let Some(sig) = utx.signature.as_ref() { - if sig.is_eth_sig() { - None - } else { - Some(self.extract_interoperation_tx_sender(&utx, sig).await?) - } - } else { - return Err(RpcError::TransactionIsNotSigned.into()); - }; - - let stx = SignedTransaction::from_unverified(utx, interoperation_sender) + let stx = SignedTransaction::from_unverified(utx) .map_err(|e| RpcError::Internal(e.to_string()))?; let hash = stx.transaction.hash; diff --git a/core/api/src/jsonrpc/web3_types.rs b/core/api/src/jsonrpc/web3_types.rs index bae15140c..1fd3746e1 100644 --- a/core/api/src/jsonrpc/web3_types.rs +++ b/core/api/src/jsonrpc/web3_types.rs @@ -858,7 +858,7 @@ mod tests { // https://etherscan.io/getRawTx?tx=0x07c7388b03ab8403deeaefc551efbc632f8531f04dc9993a274dbba9bbb98cbf let tx = Hex::from_str("0x02f902f801728405f5e1008509898edcf78302ffb8943fc91a3afd70395cd496c647d5a6cc9d4b2b7fad8802c68af0bb140000b902843593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006480c64700000000000000000000000000000000000000000000000000000000000000020b080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000002c68af0bb1400000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000002c68af0bb1400000000000000000000000000000000000000000004a715ce36374beaa635218d9700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000c3681a720605bd6f8fe9a2fabff6a7cdecdc605dc080a0d253ee687ab2d9734a5073d64a0ba26bc3bc1cf4582005137bba05ef88616ea89e8ba79925267b17403fdf3ab47641b4aa52322dc385429cc92a7003c5d7c2").unwrap(); let tx = UnverifiedTransaction::decode(tx).unwrap(); - let tx = SignedTransaction::from_unverified(tx, None).unwrap(); + let tx = SignedTransaction::from_unverified(tx).unwrap(); let tx_json = serde_json::to_value(Web3Transaction::from(tx)).unwrap(); assert_eq!( diff --git a/core/consensus/benches/bench_wal.rs b/core/consensus/benches/bench_wal.rs index 33a5c0cfd..a9c17b9d9 100644 --- a/core/consensus/benches/bench_wal.rs +++ b/core/consensus/benches/bench_wal.rs @@ -53,7 +53,7 @@ fn mock_sign_tx() -> SignedTransaction { .to_bytes(); utx.signature = Some(signature.into()); - SignedTransaction::from_unverified(utx, None).unwrap() + SignedTransaction::from_unverified(utx).unwrap() } fn criterion_save_wal(c: &mut Criterion) { diff --git a/core/consensus/src/wal.rs b/core/consensus/src/wal.rs index 2d2d7d0f6..ed533d974 100644 --- a/core/consensus/src/wal.rs +++ b/core/consensus/src/wal.rs @@ -364,7 +364,7 @@ mod tests { .to_bytes(); utx.signature = Some(signature.into()); - SignedTransaction::from_unverified(utx, None).unwrap() + SignedTransaction::from_unverified(utx).unwrap() } pub fn mock_wal_txs(size: usize) -> Vec { diff --git a/core/storage/benches/bench.rs b/core/storage/benches/bench.rs index 6e7598863..acdf0ac35 100644 --- a/core/storage/benches/bench.rs +++ b/core/storage/benches/bench.rs @@ -143,7 +143,7 @@ fn mock_signed_tx() -> SignedTransaction { .to_bytes(); utx.signature = Some(signature.into()); - SignedTransaction::from_unverified(utx, None).unwrap() + SignedTransaction::from_unverified(utx).unwrap() } fn get_random_bytes(len: usize) -> Bytes { diff --git a/core/storage/src/tests/mod.rs b/core/storage/src/tests/mod.rs index 8f3c90bd2..407331724 100644 --- a/core/storage/src/tests/mod.rs +++ b/core/storage/src/tests/mod.rs @@ -42,7 +42,7 @@ fn mock_signed_tx() -> SignedTransaction { .to_bytes(); utx.signature = Some(signature.into()); - SignedTransaction::from_unverified(utx, None).unwrap() + SignedTransaction::from_unverified(utx).unwrap() } fn mock_receipt(hash: Hash) -> Receipt { diff --git a/protocol/src/codec/transaction.rs b/protocol/src/codec/transaction.rs index d70035919..5b5537d1b 100644 --- a/protocol/src/codec/transaction.rs +++ b/protocol/src/codec/transaction.rs @@ -340,19 +340,24 @@ impl Decodable for SignedTransaction { .as_ref() .ok_or(DecoderError::Custom("missing signature"))?; - let public = if sig.is_eth_sig() { - Public::from_slice( + let (public, sender_addr) = if sig.is_eth_sig() { + let public = Public::from_slice( &secp256k1_recover(utx.signature_hash(true).as_bytes(), sig.as_bytes().as_ref()) .map_err(|_| DecoderError::Custom("recover signature"))? .serialize_uncompressed()[1..65], - ) + ); + (public, public_to_address(&public)) } else { - Public::zero() + ( + Public::zero(), + sig.extract_interoperation_tx_sender() + .map_err(|_| DecoderError::Custom("Invalid interoperation sender"))?, + ) }; Ok(SignedTransaction { transaction: utx, - sender: public_to_address(&public), + sender: sender_addr, public: Some(public), }) } @@ -460,7 +465,7 @@ mod tests { fn test_signed_tx_codec() { let raw = hex_decode("02f8670582010582012c82012c825208945cf83df52a32165a7f392168ac009b168c9e89150180c001a0a68aeb0db4d84cf16da5a6918becefd254654854cfc23f0112ef78154ce84db89f4b0af1cbf12f5bfaec81c3d4d495717d720b574a05092f6b436c2ab255cd35").unwrap(); let utx = UnverifiedTransaction::decode(&Rlp::new(&raw)).unwrap(); - let origin = SignedTransaction::from_unverified(utx, None).unwrap(); + let origin = SignedTransaction::from_unverified(utx).unwrap(); let encode = origin.rlp_bytes().freeze().to_vec(); let decode: SignedTransaction = rlp::decode(&encode).unwrap(); assert_eq!(origin, decode); @@ -559,7 +564,7 @@ mod tests { let test_vector = |tx_data: &str, address: &'static str| { let utx = UnverifiedTransaction::decode(&Rlp::new(&hex_decode(tx_data).unwrap())).unwrap(); - let signed = SignedTransaction::from_unverified(utx.clone(), None).unwrap(); + let signed = SignedTransaction::from_unverified(utx.clone()).unwrap(); assert_eq!( signed.sender, H160::from_slice(&hex_decode(address).unwrap()) diff --git a/protocol/src/types/batch.rs b/protocol/src/types/batch.rs index 5f8ee6aeb..a4c93e0a0 100644 --- a/protocol/src/types/batch.rs +++ b/protocol/src/types/batch.rs @@ -82,7 +82,7 @@ mod tests { .to_bytes(); utx.signature = Some(signature.into()); - SignedTransaction::from_unverified(utx, None).unwrap() + SignedTransaction::from_unverified(utx).unwrap() } #[test] diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 9a73da22d..1b8fcbce1 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -84,6 +84,9 @@ pub enum TypesError { #[display(fmt = "InvalidBlockVersion {}", _0)] InvalidBlockVersion(u8), + + #[display(fmt = "Decode interoperation signature R error {:?}", _0)] + DecodeInteroperationSigR(rlp::DecoderError), } impl Error for TypesError {} diff --git a/protocol/src/types/transaction.rs b/protocol/src/types/transaction.rs index 3a8d5820f..a7de86c0a 100644 --- a/protocol/src/types/transaction.rs +++ b/protocol/src/types/transaction.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; use common_crypto::secp256k1_recover; use crate::types::{ - Bloom, Bytes, BytesMut, ExitReason, Hash, Hasher, Public, TxResp, TypesError, H160, H256, H520, - U256, + Bloom, Bytes, BytesMut, CellDepWithPubKey, ExitReason, Hash, Hasher, Public, TxResp, + TypesError, H160, H256, H520, U256, }; use crate::ProtocolResult; @@ -412,6 +412,18 @@ impl SignatureComponents { } } + pub(crate) fn extract_interoperation_tx_sender(&self) -> ProtocolResult { + // Only call CKB-VM mode is supported now + if self.r[0] == 0 { + let r = rlp::decode::(&self.r[1..]) + .map_err(TypesError::DecodeInteroperationSigR)?; + + return Ok(Hasher::digest(&r.pub_key).into()); + } + + Err(TypesError::InvalidSignatureRType.into()) + } + #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.r.len() + self.s.len() + 1 @@ -426,24 +438,19 @@ pub struct SignedTransaction { } impl SignedTransaction { - pub fn from_unverified( - utx: UnverifiedTransaction, - sender: Option, - ) -> ProtocolResult { + pub fn from_unverified(utx: UnverifiedTransaction) -> ProtocolResult { if utx.signature.is_none() { return Err(TypesError::Unsigned.into()); } let hash = utx.signature_hash(true); + let sig = utx.signature.as_ref().unwrap(); - if utx.signature.as_ref().unwrap().is_eth_sig() { + if sig.is_eth_sig() { let public = Public::from_slice( - &secp256k1_recover( - hash.as_bytes(), - utx.signature.as_ref().unwrap().as_bytes().as_ref(), - ) - .map_err(TypesError::Crypto)? - .serialize_uncompressed()[1..65], + &secp256k1_recover(hash.as_bytes(), sig.as_bytes().as_ref()) + .map_err(TypesError::Crypto)? + .serialize_uncompressed()[1..65], ); return Ok(SignedTransaction { @@ -453,14 +460,11 @@ impl SignedTransaction { }); } - if sender.is_none() { - return Err(TypesError::MissingInteroperationSender.into()); - } - + // Otherwise it is an interoperation transaction Ok(SignedTransaction { - transaction: utx.calc_hash(), - sender: sender.unwrap(), + sender: sig.extract_interoperation_tx_sender()?, public: Some(Public::zero()), + transaction: utx.calc_hash(), }) } From c91a6af5a53f06e8c877dd0640293636d4503986 Mon Sep 17 00:00:00 2001 From: Eason Date: Thu, 9 Nov 2023 10:09:20 +0800 Subject: [PATCH 2/2] refactor: remove the verify by ckb vm verification --- core/executor/src/precompiles/mod.rs | 1 - .../src/precompiles/verify_by_ckb_vm.rs | 140 -------- core/interoperation/src/lib.rs | 51 +-- core/interoperation/src/utils.rs | 82 ----- core/mempool/src/adapter/mod.rs | 55 +-- core/mempool/src/lib.rs | 10 +- protocol/src/codec/transaction.rs | 43 +-- protocol/src/traits/interoperation.rs | 102 +----- protocol/src/types/interoperation.rs | 316 +----------------- 9 files changed, 22 insertions(+), 778 deletions(-) delete mode 100644 core/executor/src/precompiles/verify_by_ckb_vm.rs delete mode 100644 core/interoperation/src/utils.rs diff --git a/core/executor/src/precompiles/mod.rs b/core/executor/src/precompiles/mod.rs index c593ca7d8..3e6fa09c3 100644 --- a/core/executor/src/precompiles/mod.rs +++ b/core/executor/src/precompiles/mod.rs @@ -16,7 +16,6 @@ mod sha256; #[cfg(test)] mod tests; -mod verify_by_ckb_vm; use std::collections::BTreeMap; diff --git a/core/executor/src/precompiles/verify_by_ckb_vm.rs b/core/executor/src/precompiles/verify_by_ckb_vm.rs deleted file mode 100644 index d2ccc31ca..000000000 --- a/core/executor/src/precompiles/verify_by_ckb_vm.rs +++ /dev/null @@ -1,140 +0,0 @@ -use ethers::contract::{EthAbiCodec, EthAbiType}; -use ethers::{abi::AbiDecode, core::types::Bytes as EthBytes}; -use evm::executor::stack::{PrecompileFailure, PrecompileOutput}; -use evm::{Context, ExitError, ExitSucceed}; - -use protocol::traits::Interoperation; -use protocol::types::{SignatureR, SignatureS, H160, H256}; - -use core_interoperation::{cycle_to_gas, gas_to_cycle, InteroperationImpl}; - -use crate::precompiles::{axon_precompile_address, call_ckb_vm::CellDep, PrecompileContract}; -use crate::system_contract::{image_cell::image_cell_abi::OutPoint, DataProvider}; -use crate::{err, CURRENT_HEADER_CELL_ROOT}; - -#[derive(Default, Clone)] -pub struct CkbVM; - -impl PrecompileContract for CkbVM { - const ADDRESS: H160 = axon_precompile_address(0x05); - const MIN_GAS: u64 = 500; - - fn exec_fn( - input: &[u8], - gas_limit: Option, - _context: &Context, - _is_static: bool, - ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { - let payload = parse_input(input)?; - - if let Some(gas) = gas_limit { - let res = InteroperationImpl::verify_by_ckb_vm( - Default::default(), - DataProvider::new(CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow())), - &InteroperationImpl::dummy_transaction( - SignatureR::new_by_ref( - payload.cell_deps(), - payload.header_deps(), - payload.inputs(), - Default::default(), - ), - SignatureS::new(payload.witnesses()), - None, - ), - None, - gas_to_cycle(gas), - ) - .map_err(|e| err!(_, e.to_string()))?; - - return Ok(( - PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: res.to_le_bytes().to_vec(), - }, - cycle_to_gas(res).max(Self::MIN_GAS), - )); - } - - err!() - } - - fn gas_cost(_input: &[u8]) -> u64 { - unreachable!() - } -} - -fn parse_input(input: &[u8]) -> Result { - ::decode(input).map_err(|_| err!(_, "decode input")) -} - -#[derive(EthAbiType, EthAbiCodec, Clone, Default, Debug, PartialEq, Eq)] -pub struct VerifyByCkbPayload { - pub cell_deps: Vec, - pub header_deps: Vec<[u8; 32]>, - pub inputs: Vec, - pub witnesses: Vec, -} - -impl VerifyByCkbPayload { - pub fn cell_deps(&self) -> Vec { - self.cell_deps - .iter() - .map(|c| protocol::types::CellDep { - tx_hash: c.out_point.tx_hash.into(), - index: c.out_point.index, - dep_type: c.dep_type, - }) - .collect() - } - - pub fn header_deps(&self) -> Vec { - self.header_deps.iter().map(Into::into).collect() - } - - pub fn inputs(&self) -> Vec { - self.inputs - .iter() - .map(|i| protocol::types::OutPoint { - tx_hash: i.tx_hash.into(), - index: i.index, - }) - .collect() - } - - pub fn witnesses(&self) -> Vec { - self.witnesses.clone().into_iter().map(Into::into).collect() - } -} - -#[derive(EthAbiType, EthAbiCodec, Clone, Default, Debug, PartialEq, Eq)] -pub struct WitnessArgs { - pub lock: EthBytes, - pub input_type: EthBytes, - pub output_type: EthBytes, -} - -impl From for protocol::types::Witness { - fn from(w: WitnessArgs) -> Self { - let lock = if w.lock.is_empty() { - None - } else { - Some(w.lock.0) - }; - let input_type = if w.input_type.is_empty() { - None - } else { - Some(w.input_type.0) - }; - let output_type = if w.output_type.is_empty() { - None - } else { - Some(w.output_type.0) - }; - - protocol::types::Witness { - lock, - input_type, - output_type, - } - } -} diff --git a/core/interoperation/src/lib.rs b/core/interoperation/src/lib.rs index fe3256327..826726821 100644 --- a/core/interoperation/src/lib.rs +++ b/core/interoperation/src/lib.rs @@ -1,29 +1,17 @@ -pub mod utils; +use std::error::Error; -use std::{error::Error, sync::Arc}; - -use ckb_chain_spec::consensus::Consensus; -use ckb_script::{TransactionScriptsVerifier, TxVerifyEnv}; use ckb_traits::CellDataProvider; -use ckb_types::core::{Cycle, HeaderBuilder, TransactionView}; -use ckb_types::{packed, prelude::Pack}; +use ckb_types::packed; use ckb_vm::machine::{asm::AsmCoreMachine, DefaultMachineBuilder, SupportMachine, VERSION1}; use ckb_vm::{Error as VMError, ISA_B, ISA_IMC, ISA_MOP}; -use protocol::traits::{CkbDataProvider, Context, Interoperation}; -use protocol::types::{Bytes, CellDep, CellWithData, OutPoint, VMResp}; +use protocol::traits::{Context, Interoperation}; +use protocol::types::{Bytes, CellDep, OutPoint, VMResp}; use protocol::{Display, ProtocolError, ProtocolErrorKind, ProtocolResult}; -use crate::utils::resolve_transaction; - const ISA: u8 = ISA_IMC | ISA_B | ISA_MOP; const GAS_TO_CYCLE_COEF: u64 = 6_000; -// The following information is from CKB block [10976708](https://explorer.nervos.org/block/10976708) -// which is CKB2023 disabled. -const CKB2023_DISABLED_NUMBER: u64 = 10_976_708; -const CKB2023_DISABLED_EPOCH: u64 = 0x53c007f0020c8; - pub const fn gas_to_cycle(gas: u64) -> u64 { gas * GAS_TO_CYCLE_COEF } @@ -76,37 +64,6 @@ impl Interoperation for InteroperationImpl { cycles: vm.machine.cycles(), }) } - - /// Todo: After CKB2023 is enabled, a hardfork is needed to support the new - /// VM version and syscalls. - fn verify_by_ckb_vm( - _ctx: Context, - data_loader: DL, - mocked_tx: &TransactionView, - dummy_input: Option, - max_cycles: u64, - ) -> ProtocolResult { - let rtx = Arc::new(resolve_transaction(&data_loader, mocked_tx, dummy_input)?); - log::debug!("[mempool]: Verify by ckb vm tx {:?}", rtx); - - // The consensus and tx_env arguments are used for judge if the VM version2 and - // syscalls3 are enabled. Due to only the hardfork field in consensus and the - // epoch field in tx_env is used, the provided arguments only need to fill these - // fields correctly. - let (ckb_spec, ckb2023_disabled_env) = ( - Arc::new(Consensus::default()), - Arc::new(TxVerifyEnv::new_commit( - &HeaderBuilder::default() - .number(CKB2023_DISABLED_NUMBER.pack()) - .epoch(CKB2023_DISABLED_EPOCH.pack()) - .build(), - )), - ); - - TransactionScriptsVerifier::new(rtx, data_loader, ckb_spec, ckb2023_disabled_env) - .verify(max_cycles) - .map_err(|e| InteroperationError::Ckb(e).into()) - } } #[derive(Debug, Display)] diff --git a/core/interoperation/src/utils.rs b/core/interoperation/src/utils.rs deleted file mode 100644 index 557f3c3a3..000000000 --- a/core/interoperation/src/utils.rs +++ /dev/null @@ -1,82 +0,0 @@ -use ckb_types::core::cell::{CellMeta, CellProvider, CellStatus, ResolvedTransaction}; -use ckb_types::core::{DepType, TransactionView}; -use ckb_types::{packed, prelude::*}; - -use protocol::{lazy::DUMMY_INPUT_OUT_POINT, types::CellWithData, ProtocolResult}; - -use crate::InteroperationError; - -pub fn resolve_transaction( - cell_loader: &CL, - tx: &TransactionView, - dummy_input: Option, -) -> ProtocolResult { - let resolve_cell = |out_point: &packed::OutPoint| -> ProtocolResult { - match cell_loader.cell(out_point, true) { - CellStatus::Live(meta) => Ok(meta), - _ => Err(InteroperationError::GetUnknownCell(out_point.into()).into()), - } - }; - - let (mut resolved_inputs, mut resolved_cell_deps, mut resolved_dep_groups) = ( - Vec::with_capacity(tx.inputs().len()), - Vec::with_capacity(tx.cell_deps().len()), - Vec::with_capacity(tx.cell_deps().len()), - ); - - for outpoint in tx.input_pts_iter() { - if is_dummy_out_point(&outpoint) { - if let Some(ref cell) = dummy_input { - resolved_inputs.push(cell.into()); - } else { - return Err(InteroperationError::InvalidDummyInput.into()); - } - } else { - resolved_inputs.push(resolve_cell(&outpoint)?); - } - } - - for cell_dep in tx.cell_deps_iter() { - if cell_dep.dep_type() == DepType::DepGroup.into() { - let dep_group = resolve_cell(&cell_dep.out_point())?; - let data = dep_group.mem_cell_data.as_ref().unwrap(); - let sub_out_points = - parse_dep_group_data(data).map_err(InteroperationError::InvalidDepGroup)?; - - for sub_out_point in sub_out_points.into_iter() { - resolved_cell_deps.push(resolve_cell(&sub_out_point)?); - } - resolved_dep_groups.push(dep_group); - } else { - resolved_cell_deps.push(resolve_cell(&cell_dep.out_point())?); - } - } - - Ok(ResolvedTransaction { - transaction: tx.clone(), - resolved_cell_deps, - resolved_inputs, - resolved_dep_groups, - }) -} - -pub fn parse_dep_group_data(slice: &[u8]) -> Result { - if slice.is_empty() { - Err("data is empty".to_owned()) - } else { - match packed::OutPointVec::from_slice(slice) { - Ok(v) => { - if v.is_empty() { - Err("dep group is empty".to_owned()) - } else { - Ok(v) - } - } - Err(err) => Err(err.to_string()), - } - } -} - -pub fn is_dummy_out_point(out_point: &packed::OutPoint) -> bool { - *out_point == *DUMMY_INPUT_OUT_POINT -} diff --git a/core/mempool/src/adapter/mod.rs b/core/mempool/src/adapter/mod.rs index f701e7466..26c871815 100644 --- a/core/mempool/src/adapter/mod.rs +++ b/core/mempool/src/adapter/mod.rs @@ -17,7 +17,7 @@ use protocol::traits::{ }; use protocol::types::{ recover_intact_pub_key, Backend, BatchSignedTxs, CellDepWithPubKey, Hash, MerkleRoot, - SignatureR, SignatureS, SignedTransaction, H160, U256, + SignedTransaction, H160, U256, }; use protocol::{ async_trait, codec::ProtocolCodec, tokio, trie, Display, ProtocolError, ProtocolErrorKind, @@ -35,8 +35,6 @@ use crate::adapter::message::{MsgPullTxs, END_GOSSIP_NEW_TXS, RPC_PULL_TXS}; use crate::context::TxContext; use crate::MemPoolError; -const MAX_VERIFY_CKB_VM_CYCLES: u64 = 50_000_000; - struct IntervalTxsBroadcaster; impl IntervalTxsBroadcaster { @@ -289,48 +287,17 @@ where let root = self.executor_backend(ctx).await?.get_image_cell_root(); - // Verify interoperation signature - match signature.r[0] { - 0u8 => { - // Call CKB-VM mode - let r = rlp::decode::(&signature.r[1..]) - .map_err(AdapterError::Rlp)?; - - InteroperationImpl::call_ckb_vm( - Default::default(), - &DataProvider::new(root), - r.cell_dep, - &[r.pub_key, signature.s], - u64::MAX, - ) - .map_err(|e| AdapterError::VerifySignature(e.to_string()))?; - } - _ => { - // Verify by mock transaction mode - let r = SignatureR::decode(&signature.r)?; - let s = SignatureS::decode(&signature.s)?; - - if r.inputs_len() != s.witnesses.len() { - return Err(AdapterError::VerifySignature( - "signature item mismatch".to_string(), - ) - .into()); - } + // Verify interoperation signature call CKB-VM mode + let r = rlp::decode::(&signature.r[1..]).map_err(AdapterError::Rlp)?; + InteroperationImpl::call_ckb_vm( + Default::default(), + &DataProvider::new(root), + r.cell_dep, + &[r.pub_key, signature.s], + u64::MAX, + ) + .map_err(|e| AdapterError::VerifySignature(e.to_string()))?; - InteroperationImpl::verify_by_ckb_vm( - Default::default(), - DataProvider::new(root), - &InteroperationImpl::dummy_transaction( - r.clone(), - s, - Some(stx.transaction.signature_hash(true).0), - ), - r.dummy_input(), - MAX_VERIFY_CKB_VM_CYCLES, - ) - .map_err(|e| AdapterError::VerifySignature(e.to_string()))?; - } - } Ok(()) } diff --git a/core/mempool/src/lib.rs b/core/mempool/src/lib.rs index b87386d8d..819056e9a 100644 --- a/core/mempool/src/lib.rs +++ b/core/mempool/src/lib.rs @@ -20,9 +20,7 @@ use futures::future::try_join_all; use common_apm::Instant; use protocol::traits::{Context, MemPool, MemPoolAdapter}; -use protocol::types::{ - AddressSource, BlockNumber, Hash, PackedTxHashes, SignedTransaction, H160, H256, U256, -}; +use protocol::types::{BlockNumber, Hash, PackedTxHashes, SignedTransaction, H160, H256, U256}; use protocol::{async_trait, tokio, Display, ProtocolError, ProtocolErrorKind, ProtocolResult}; use core_executor::is_call_system_script; @@ -451,12 +449,6 @@ pub enum MemPoolError { #[display(fmt = "Encode transaction to JSON failed")] EncodeJson, - #[display(fmt = "Invalid address source {:?}", _0)] - InvalidAddressSource(AddressSource), - - #[display(fmt = "Invalid dummy input")] - InvalidDummyInput, - #[display(fmt = "Invalid sender, expect: {:?}, get: {:?}", expect, actual)] InvalidSender { expect: H160, actual: H160 }, } diff --git a/protocol/src/codec/transaction.rs b/protocol/src/codec/transaction.rs index 5b5537d1b..9659b5a5d 100644 --- a/protocol/src/codec/transaction.rs +++ b/protocol/src/codec/transaction.rs @@ -370,10 +370,7 @@ mod tests { use common_crypto::secp256k1_recover; use crate::codec::hex_decode; - use crate::types::{ - AddressSource, Bytes, CKBTxMockByRefAndOneInput, CellDep, CellWithData, Public, Script, - SignatureR, SignatureS, Witness, H160, H256, U256, - }; + use crate::types::{Public, SignatureS, Witness, H160, U256}; #[test] fn test_legacy_decode() { @@ -616,44 +613,6 @@ mod tests { assert!(!utx.signature.as_ref().unwrap().is_eth_sig()); } - #[test] - fn test_signature_r_codec() { - let cell_dep = CellDep { - tx_hash: H256::from_slice(&[ - 243, 81, 120, 199, 161, 165, 164, 229, 177, 100, 21, 122, 165, 73, 164, 147, 206, - 188, 154, 48, 121, 182, 169, 237, 231, 174, 82, 7, 173, 179, 244, 212, - ]), - index: 0, - dep_type: 1, - }; - let cell_with_data = CellWithData { - type_script: None, - lock_script: Script { - code_hash: H256::from_slice(&[ - 210, 55, 97, 179, 100, 33, 7, 53, 193, 156, 96, 86, 29, 33, 63, 179, 190, 174, - 47, 214, 23, 39, 67, 113, 158, 255, 105, 32, 224, 32, 186, 172, - ]), - args: vec![ - 0, 1, 96, 145, 217, 61, 186, 177, 47, 22, 100, 15, 179, 160, 168, 241, 231, - 126, 3, 251, 197, 28, - ] - .into(), - hash_type: 1, - }, - data: Bytes::new(), - }; - let address_source = AddressSource { index: 0, type_: 0 }; - - let sig_r = SignatureR::ByRefAndOneInput(CKBTxMockByRefAndOneInput { - cell_deps: vec![cell_dep], - header_deps: vec![], - input_cell_with_data: cell_with_data, - out_point_addr_source: address_source, - }); - - assert_eq!(SignatureR::decode(&sig_r.encode()).unwrap(), sig_r); - } - #[test] fn test_signature_s_codec() { let witness = Witness { diff --git a/protocol/src/traits/interoperation.rs b/protocol/src/traits/interoperation.rs index 72b38d151..15409999f 100644 --- a/protocol/src/traits/interoperation.rs +++ b/protocol/src/traits/interoperation.rs @@ -1,9 +1,8 @@ use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; -use ckb_types::core::{cell::CellProvider, Cycle, TransactionView}; -use ckb_types::{packed, prelude::*}; +use ckb_types::core::cell::CellProvider; -use crate::types::{Bytes, CellDep, CellWithData, SignatureR, SignatureS, VMResp}; -use crate::{lazy::DUMMY_INPUT_OUT_POINT, traits::Context, ProtocolResult}; +use crate::types::{Bytes, CellDep, VMResp}; +use crate::{traits::Context, ProtocolResult}; pub const BYTE_SHANNONS: u64 = 100_000_000; pub const SIGNATURE_HASH_CELL_OCCUPIED_CAPACITY: u64 = signature_hash_cell_bytes() * BYTE_SHANNONS; @@ -37,99 +36,4 @@ pub trait Interoperation: Sync + Send { args: &[Bytes], max_cycles: u64, ) -> ProtocolResult; - - fn verify_by_ckb_vm( - ctx: Context, - data_loader: DL, - mocked_tx: &TransactionView, - dummy_input: Option, - max_cycles: u64, - ) -> ProtocolResult; - - /// The function construct the `TransactionView` payload required by - /// `verify_by_ckb_vm()`. - fn dummy_transaction( - r: SignatureR, - s: SignatureS, - signature_hash: Option<[u8; 32]>, - ) -> TransactionView { - let cell_deps = r.cell_deps(); - let header_deps = r.header_deps(); - let signature_hash = signature_hash.map(|hash| hash.to_vec()).unwrap_or_default(); - - let tx_builder = TransactionView::new_advanced_builder() - .cell_deps(cell_deps.iter().map(Into::into)) - .header_deps(header_deps.iter().map(|dep| dep.0.pack())) - .witnesses(s.witnesses.iter().map(|i| { - packed::WitnessArgsBuilder::default() - .input_type( - packed::BytesOptBuilder::default() - .set(i.input_type.clone().map(|inner| inner.pack())) - .build(), - ) - .output_type( - packed::BytesOptBuilder::default() - .set(i.output_type.clone().map(|inner| inner.pack())) - .build(), - ) - .lock( - packed::BytesOptBuilder::default() - .set(i.lock.clone().map(|inner| inner.pack())) - .build(), - ) - .build() - .as_bytes() - .pack() - })); - - if r.is_only_by_ref() { - return tx_builder - .inputs(r.out_points().iter().map(|i| { - packed::CellInput::new( - packed::OutPointBuilder::default() - .tx_hash(i.tx_hash.0.pack()) - .index(i.index.pack()) - .build(), - 0u64, - ) - })) - .output( - packed::CellOutputBuilder::default() - .capacity(SIGNATURE_HASH_CELL_OCCUPIED_CAPACITY.pack()) - .build(), - ) - .output_data(signature_hash.pack()) - .build(); - } - - let output_capacity = (r.dummy_input().unwrap().capacity() - BYTE_SHANNONS) - .max(SIGNATURE_HASH_CELL_OCCUPIED_CAPACITY); - - tx_builder - .input(packed::CellInput::new(DUMMY_INPUT_OUT_POINT.clone(), 0u64)) - .output( - packed::CellOutputBuilder::default() - .capacity(output_capacity.pack()) - .build(), - ) - .output_data(signature_hash.pack()) - .build() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ckb_types::core::Capacity; - - #[test] - fn test_const_signature_hash_cell_bytes() { - let data_capacity = Capacity::bytes(32).unwrap(); - let actual = packed::CellOutputBuilder::default() - .build() - .occupied_capacity(data_capacity) - .unwrap() - .as_u64(); - assert_eq!(SIGNATURE_HASH_CELL_OCCUPIED_CAPACITY, actual); - } } diff --git a/protocol/src/types/interoperation.rs b/protocol/src/types/interoperation.rs index 4c5f5c84c..8524f5fda 100644 --- a/protocol/src/types/interoperation.rs +++ b/protocol/src/types/interoperation.rs @@ -1,13 +1,9 @@ -use ckb_types::{core::cell::CellMeta, packed, prelude::*}; +use ckb_types::{packed, prelude::*}; use derive_more::Display; use rlp_derive::{RlpDecodable, RlpEncodable}; use serde::{Deserialize, Serialize}; -use crate::traits::{BYTE_SHANNONS, SIGNATURE_HASH_CELL_OCCUPIED_CAPACITY}; -use crate::types::{Bytes, TypesError, H256}; -use crate::{ckb_blake2b_256, codec::ProtocolCodec, lazy::DUMMY_INPUT_OUT_POINT, ProtocolResult}; - -const CAPACITY_BYTES_LEN: usize = 8; +use crate::types::{Bytes, H256}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct VMResp { @@ -15,143 +11,6 @@ pub struct VMResp { pub cycles: u64, } -/// The address mapping for calculate an Axon address by the input cell, which -/// is `keccak(input[index].content).into()`. The `type_` field means the type -/// of content to calculate hash with the following rules: -/// `0u8`: use lock script hash. -/// `1u8`: use type script hash. -/// So that the default value of `AddressSource` means using -/// `blake2b_256(input[0].lock().as_bytes())` as `keccak()` input. -#[derive( - RlpEncodable, RlpDecodable, Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq, -)] -pub struct AddressSource { - pub type_: u8, - pub index: u32, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SignatureR { - ByRef(CKBTxMockByRef), - ByRefAndOneInput(CKBTxMockByRefAndOneInput), -} - -impl SignatureR { - pub fn new_by_ref( - cell_deps: Vec, - header_deps: Vec, - out_points: Vec, - out_point_addr_source: AddressSource, - ) -> Self { - SignatureR::ByRef(CKBTxMockByRef { - cell_deps, - header_deps, - out_points, - out_point_addr_source, - }) - } - - pub fn decode(data: &[u8]) -> ProtocolResult { - if data.is_empty() { - return Err(TypesError::SignatureRIsEmpty.into()); - } - - let ret = match data[0] { - 1u8 => SignatureR::ByRef(CKBTxMockByRef::decode(&data[1..])?), - 2u8 => SignatureR::ByRefAndOneInput(CKBTxMockByRefAndOneInput::decode(&data[1..])?), - _ => return Err(TypesError::InvalidSignatureRType.into()), - }; - - if ret.address_source().type_ > 1 { - return Err(TypesError::InvalidAddressSourceType.into()); - } - - Ok(ret) - } - - pub fn inputs_len(&self) -> usize { - match self { - SignatureR::ByRef(i) => i.out_points.len(), - SignatureR::ByRefAndOneInput(_) => 1usize, - } - } - - pub fn address_source(&self) -> AddressSource { - match self { - SignatureR::ByRef(i) => i.out_point_addr_source, - SignatureR::ByRefAndOneInput(i) => i.out_point_addr_source, - } - } - - pub fn cell_deps(&self) -> &[CellDep] { - match self { - SignatureR::ByRef(i) => &i.cell_deps, - SignatureR::ByRefAndOneInput(i) => &i.cell_deps, - } - } - - pub fn header_deps(&self) -> &[H256] { - match self { - SignatureR::ByRef(i) => &i.header_deps, - SignatureR::ByRefAndOneInput(i) => &i.header_deps, - } - } - - pub(crate) fn out_points(&self) -> &[OutPoint] { - match self { - SignatureR::ByRef(i) => &i.out_points, - _ => unreachable!(), - } - } - - pub fn dummy_input(&self) -> Option { - match self { - SignatureR::ByRefAndOneInput(i) => Some(i.input_cell_with_data.clone()), - SignatureR::ByRef(_) => None, - } - } - - pub fn is_only_by_ref(&self) -> bool { - match self { - SignatureR::ByRef(_) => true, - SignatureR::ByRefAndOneInput(_) => false, - } - } - - #[cfg(test)] - pub(crate) fn encode(&self) -> Bytes { - match self { - SignatureR::ByRef(r) => { - let mut ret = vec![1]; - ret.extend_from_slice(&rlp::encode(r)); - ret - } - SignatureR::ByRefAndOneInput(r) => { - let mut ret = vec![2]; - ret.extend_from_slice(&rlp::encode(r)); - ret - } - } - .into() - } -} - -#[derive(RlpEncodable, RlpDecodable, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] -pub struct CKBTxMockByRef { - pub cell_deps: Vec, - pub header_deps: Vec, - pub out_points: Vec, - pub out_point_addr_source: AddressSource, -} - -#[derive(RlpEncodable, RlpDecodable, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] -pub struct CKBTxMockByRefAndOneInput { - pub cell_deps: Vec, - pub header_deps: Vec, - pub input_cell_with_data: CellWithData, - pub out_point_addr_source: AddressSource, -} - #[derive(RlpEncodable, RlpDecodable, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct CellWithData { pub type_script: Option