diff --git a/Cargo.lock b/Cargo.lock index e93e759535..8bd32b144b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,6 +344,7 @@ dependencies = [ "rlp", "rlp_compress", "rlp_derive", + "rustc-hex 1.0.0", "snap", "table", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index 833fca2259..923c7fc651 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -30,6 +30,7 @@ num-rational = "0.2.1" parking_lot = "0.6.0" primitives = { git = "https://github.com/CodeChain-io/rust-codechain-primitives.git", version = "0.4" } rand = "0.6.1" +rustc-hex = "1.0" rlp = { git = "https://github.com/CodeChain-io/rlp.git", version = "0.4" } rlp_compress = { git = "https://github.com/CodeChain-io/rlp.git", version = "0.2" } rlp_derive = { git = "https://github.com/CodeChain-io/rlp.git", version = "0.2" } diff --git a/core/src/consensus/light_client/verification.rs b/core/src/consensus/light_client/verification.rs index fa2840db4a..b3ccd99a83 100644 --- a/core/src/consensus/light_client/verification.rs +++ b/core/src/consensus/light_client/verification.rs @@ -26,18 +26,31 @@ pub fn verify_signature(block_hash: H256, vset: &CompactValidatorSet, seal: &Sea let seal_view = TendermintSealView::new(&seal.raw).signatures(); if seal_view.is_err() { + cdebug!(LIGHT_CLIENT, "verify_signature: seal is not a valis tendermint seal"); return false } let seal_dec = seal_view.unwrap(); for (index, sign) in &seal_dec { if *index >= n { + cdebug!( + LIGHT_CLIENT, + "verify_signature: index {} is equal or greater than validator set size {}", + index, + n + ); return false } match verify_schnorr(&vset[*index].public_key, &sign, &block_hash) { Ok(true) => (), - Ok(false) => return false, - _ => return false, + Ok(false) => { + cdebug!(LIGHT_CLIENT, "verify_signature: verify_schnorr verified that signature is not correct"); + return false + } + _ => { + cdebug!(LIGHT_CLIENT, "verify_signature: verify_schnorr failed"); + return false + } }; } true @@ -61,15 +74,36 @@ pub fn verify_quorum(vset: &CompactValidatorSet, seal: &Seal) -> bool { pub fn verify_header(client_state: &ClientState, proposal: &UpdateHeader) -> bool { if client_state.number + 1 != proposal.number { + ctrace!( + IBC, + "verify_header: The number in the header does not match. expted: {} given: {}", + client_state.number + 1, + proposal.number + ); return false } if client_state.next_validator_set_hash != proposal.validator_set.hash() { + ctrace!( + IBC, + "verify_header: Next validator set hash does not match. expected: {} given: {}", + client_state.next_validator_set_hash, + proposal.validator_set.hash() + ); return false } + + // FIXME: We should remove this if statement when the static validator is removed. + let bypass_verification = std::env::var("BYPASS_VERIFICATION_IN_STATIC_VALIDATOR"); + if bypass_verification.is_ok() { + return true + } + if !verify_signature(proposal.hash, &proposal.validator_set, &proposal.seal) { + ctrace!(IBC, "verify_header: Signature verification of seal failed"); return false } if !verify_quorum(&proposal.validator_set, &proposal.seal) { + ctrace!(IBC, "verify_header: Qurom not met"); return false } true diff --git a/core/src/ibc/client_02/manager.rs b/core/src/ibc/client_02/manager.rs index babff3e898..0638305d26 100644 --- a/core/src/ibc/client_02/manager.rs +++ b/core/src/ibc/client_02/manager.rs @@ -57,6 +57,7 @@ impl<'a> Manager<'a> { pub fn update(&mut self, id: IdentifierSlice, header: Bytes) -> Result<(), String> { let header_dec: Header = rlp::decode(&header).map_err(|_| "Failed to decode IBC header")?; + cdebug!(IBC, "Decoded header in update: {:?}", header_dec); let client_state = self.query(id)?; let (new_client_state, new_consensus_state) = super::client::check_validity_and_update_state(&client_state, &header_dec)?; diff --git a/core/src/ibc/transaction_handler/mod.rs b/core/src/ibc/transaction_handler/mod.rs index 2489838b64..446477800a 100644 --- a/core/src/ibc/transaction_handler/mod.rs +++ b/core/src/ibc/transaction_handler/mod.rs @@ -27,6 +27,7 @@ use ibc::client_02 as ibc_client; use ibc::connection_03 as ibc_connection; use ibc::context as ibc_context; use rlp::{Decodable, Rlp}; +use rustc_hex::ToHex; pub fn execute( bytes: &[u8], @@ -58,8 +59,9 @@ pub fn execute( id, header, } => { + cdebug!(IBC, "Update client {} {}", id, header.to_hex()); let mut client_manager = ibc_client::Manager::new(&mut context); - client_manager.update(&id, header).map_err(|err| RuntimeError::IBC(format!("CreateClient: {:?}", err)))?; + client_manager.update(&id, header).map_err(|err| RuntimeError::IBC(format!("UpdateClient: {:?}", err)))?; Ok(()) } Datagram::ConnOpenInit { diff --git a/core/src/miner/miner.rs b/core/src/miner/miner.rs index 2f7f2df0a3..770dcc4da8 100644 --- a/core/src/miner/miner.rs +++ b/core/src/miner/miner.rs @@ -328,6 +328,14 @@ impl Miner { }; assert!(self.engine.seals_internally(), "If a signer is not prepared, prepare_block should not be called"); + + // FIXME this sleeping is for the experimental branch. This should not be included in the master branch. + let wait = std::env::var("WAIT_1_SEC_BEFORE_CREATING_A_BLOCK"); + if wait.is_ok() { + cinfo!(MINER, "Wait 1 second before generating a block"); + std::thread::sleep(std::time::Duration::from_secs(1)); + } + let seal = self.engine.generate_seal(None, &parent_header.decode()); if let Some(seal_bytes) = seal.seal_fields() { open_block.seal(self.engine.borrow(), seal_bytes).expect("Sealing always success"); diff --git a/ibc.ts/chainA/chainA.schem.json b/ibc.ts/chainA/chainA.schem.json index 33bcaac6bb..930507087e 100644 --- a/ibc.ts/chainA/chainA.schem.json +++ b/ibc.ts/chainA/chainA.schem.json @@ -6,7 +6,7 @@ "validators": [ "0xc7db8f558a87a45d3b4d2e7422abb48a27e6eeee00a0f884ca244d1d783d6ead2ca445e9ac8f08b311d78b4b4b69d48a9daf0c98d2a19e87e482f8060138ba88" ], - "timeoutPropose": 3000, + "timeoutPropose": 10000, "timeoutProposeDelta": 500, "timeoutPrevote": 1000, "timeoutPrevoteDelta": 500, diff --git a/ibc.ts/chainB/chainB.schem.json b/ibc.ts/chainB/chainB.schem.json index 6c70132af3..da9ae5b140 100644 --- a/ibc.ts/chainB/chainB.schem.json +++ b/ibc.ts/chainB/chainB.schem.json @@ -6,7 +6,7 @@ "validators": [ "0x521d103d95b5b684f9be089ccc5cffd6b4ff2997ce535a7053e44d811b84f38abf2f35ebfeb5458e86a0b7d7293e71bcb3a59eddeb8b63801c2c4a8081fc67ab" ], - "timeoutPropose": 3000, + "timeoutPropose": 10000, "timeoutProposeDelta": 500, "timeoutPrevote": 1000, "timeoutPrevoteDelta": 500, diff --git a/ibc.ts/src/common/chain.ts b/ibc.ts/src/common/chain.ts index a830bc9ec5..e2354ba36c 100644 --- a/ibc.ts/src/common/chain.ts +++ b/ibc.ts/src/common/chain.ts @@ -54,17 +54,34 @@ export class Chain { } public async submitDatagram(datagram: Datagram): Promise { - const ibcAction = new IBC(this.sdk.networkId, datagram.rlpBytes()); + await this.submitDatagrams([datagram]); + } + public async submitDatagrams(datagrams: Datagram[]): Promise { + const txHashes = []; const seq = await this.sdk.rpc.chain.getSeq(this.faucetAddress); - const signedTx = await this.sdk.key.signTransaction(ibcAction, { - account: this.faucetAddress, - fee: 100, - seq - }); - const txHash = await this.sdk.rpc.chain.sendSignedTransaction(signedTx); - await waitForTx(this.sdk, txHash); + for (let i = 0; i < datagrams.length; i += 1) { + const datagram = datagrams[i]; + const ibcAction = new IBC(this.sdk.networkId, datagram.rlpBytes()); + + const signedTx = await this.sdk.key.signTransaction(ibcAction, { + account: this.faucetAddress, + fee: 100, + seq: seq + i + }); + + debug(`Send tx with seq ${seq + i}`); + const txHash = await this.sdk.rpc.chain.sendSignedTransaction( + signedTx + ); + txHashes.push(txHash); + } + + for (const txHash of txHashes) { + debug(`Wait for tx ${txHash}`); + await waitForTx(this.sdk, txHash); + } } public async latestHeight(): Promise { diff --git a/ibc.ts/src/relayer/index.ts b/ibc.ts/src/relayer/index.ts index 6442e3af85..85b3180dbf 100644 --- a/ibc.ts/src/relayer/index.ts +++ b/ibc.ts/src/relayer/index.ts @@ -5,6 +5,7 @@ import { delay } from "../common/util"; import { getConfig } from "../common/config"; import { PlatformAddress } from "codechain-primitives/lib"; import { UpdateClientDatagram } from "../common/datagram/updateClient"; +import { strict as assert } from "assert"; require("dotenv").config(); @@ -61,13 +62,9 @@ async function relayFromTo({ counterpartyChain }); - for (const localDiagram of localDatagrams) { - await chain.submitDatagram(localDiagram); - } + await chain.submitDatagrams(localDatagrams); - for (const counterpartyDatagram of counterpartyDatagrams) { - await counterpartyChain.submitDatagram(counterpartyDatagram); - } + await counterpartyChain.submitDatagrams(counterpartyDatagrams); } async function pendingDatagrams({ @@ -78,7 +75,7 @@ async function pendingDatagrams({ counterpartyChain: Chain; }): Promise<{ localDatagrams: Datagram[]; counterpartyDatagrams: Datagram[] }> { const height = await chain.latestHeight(); - const counterpartyChainHeight = await chain.latestHeight(); + const counterpartyChainHeight = await counterpartyChain.latestHeight(); let localDatagrams: Datagram[] = []; let counterpartyDatagrams: Datagram[] = []; @@ -127,6 +124,7 @@ async function updateLightClient({ const header = (await counterpartyChain.queryIBCHeader( currentBlockNumber + 1 ))!; + assert.notEqual(header, null, "Composed header should not be null"); datagrams.push( new UpdateClientDatagram({ id: chain.counterpartyIdentifiers.client, diff --git a/ibc.ts/src/scenario/runChains.ts b/ibc.ts/src/scenario/runChains.ts index cf5cde5e31..51f04003e8 100644 --- a/ibc.ts/src/scenario/runChains.ts +++ b/ibc.ts/src/scenario/runChains.ts @@ -53,6 +53,11 @@ async function runChainA() { "../../target/debug/foundry", ["-c", "./chainA.schem.json", "--config", "./chainA.config.toml"], { + env: { + ...process.env, + WAIT_1_SEC_BEFORE_CREATING_A_BLOCK: "true", + BYPASS_VERIFICATION_IN_STATIC_VALIDATOR: "true" + }, cwd: "./chainA" } ); @@ -64,6 +69,11 @@ async function runChainB() { "../../target/debug/foundry", ["-c", "./chainB.schem.json", "--config", "./chainB.config.toml"], { + env: { + ...process.env, + WAIT_1_SEC_BEFORE_CREATING_A_BLOCK: "true", + BYPASS_VERIFICATION_IN_STATIC_VALIDATOR: "true" + }, cwd: "./chainB" } ); @@ -91,6 +101,11 @@ async function checkChainAAndBAreRunning() { debug("Send ping to B"); await sdkB.rpc.node.ping(); + debug("Delete pending Txs in A"); + await sdkA.rpc.sendRpcRequest("mempool_deleteAllPendingTransactions", []); + debug("Delete pending Txs in B"); + await sdkB.rpc.sendRpcRequest("mempool_deleteAllPendingTransactions", []); + await sendPayTx({ sdk: sdkA, from: "accqym7qmn5yj29cdl405xlmx6awd3f3yz07g7vq2c9", @@ -121,7 +136,7 @@ async function sendPayTx({ account: from, passphrase: "", fee: 1000, - seq: 0 + seq: await sdk.rpc.chain.getSeq(from) }); debug(`Send payTx to ${chainName}`); const txhash = await sdk.rpc.chain.sendSignedTransaction(signedPay); diff --git a/util/logger/src/macros.rs b/util/logger/src/macros.rs index 9340e212de..a99a04747e 100644 --- a/util/logger/src/macros.rs +++ b/util/logger/src/macros.rs @@ -97,6 +97,12 @@ macro_rules! log_target { (TX) => { "tx" }; + (IBC) => { + "ibc" + }; + (LIGHT_CLIENT) => { + "light_client" + }; } #[macro_export]