Skip to content

Commit

Permalink
fix: rlp decode of SignedTransction with interoperation signature (#…
Browse files Browse the repository at this point in the history
…1533)

This PR:
1. Fix the interoperation transaction sender recovery:
  **Original**
a. When the transaction is from API, the sender address is recovery by
the interoperation logic
b. When the transaction if from network and insert into mempool
directly, the sender address is recovery by rlp decode. However, the rlp
decode logic is different from the interoperation.
 **Current**
        The rlp decode logic is same as interoperation logic.
2. Remove the useless `verify_by_ckb_vm` verification code due to the
schedule.
3. Only call CKB-VM mode is supported now

### What is the impact of this PR?

No Breaking Change

**PR relation**:
- Fix #1514
  • Loading branch information
Flouse authored Nov 15, 2023
2 parents ea9ae7e + c91a6af commit 835cb16
Show file tree
Hide file tree
Showing 18 changed files with 73 additions and 908 deletions.
105 changes: 7 additions & 98 deletions core/api/src/jsonrpc/impl/web3.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -191,80 +184,6 @@ impl<Adapter: APIAdapter> Web3RpcImpl<Adapter> {
reward,
))
}

async fn extract_interoperation_tx_sender(
&self,
utx: &UnverifiedTransaction,
signature: &SignatureComponents,
) -> RpcResult<H160> {
// Call CKB-VM mode
if signature.r[0] == 0 {
let r = rlp::decode::<CellDepWithPubKey>(&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]
Expand Down Expand Up @@ -297,17 +216,7 @@ impl<Adapter: APIAdapter + 'static> Web3RpcServer for Web3RpcImpl<Adapter> {
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;

Expand Down
2 changes: 1 addition & 1 deletion core/api/src/jsonrpc/web3_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down
2 changes: 1 addition & 1 deletion core/consensus/benches/bench_wal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion core/consensus/src/wal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<SignedTransaction> {
Expand Down
1 change: 0 additions & 1 deletion core/executor/src/precompiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ mod sha256;

#[cfg(test)]
mod tests;
mod verify_by_ckb_vm;

use std::collections::BTreeMap;

Expand Down
140 changes: 0 additions & 140 deletions core/executor/src/precompiles/verify_by_ckb_vm.rs

This file was deleted.

51 changes: 4 additions & 47 deletions core/interoperation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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
}
Expand Down Expand Up @@ -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<DL: CkbDataProvider + Sync + Send + 'static>(
_ctx: Context,
data_loader: DL,
mocked_tx: &TransactionView,
dummy_input: Option<CellWithData>,
max_cycles: u64,
) -> ProtocolResult<Cycle> {
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)]
Expand Down
Loading

0 comments on commit 835cb16

Please sign in to comment.