From c260076771e82e13e93ea41defb4dc52c8b3cc6e Mon Sep 17 00:00:00 2001 From: Seulgi Kim Date: Wed, 12 Jun 2019 14:01:44 +0900 Subject: [PATCH] Add chain_getPossibleAuthors --- core/src/client/client.rs | 14 +++- core/src/client/mod.rs | 2 + core/src/client/test_client.rs | 5 ++ core/src/consensus/blake_pow/mod.rs | 7 +- core/src/consensus/cuckoo/mod.rs | 7 +- core/src/consensus/mod.rs | 2 + core/src/consensus/null_engine/mod.rs | 7 +- core/src/consensus/simple_poa/mod.rs | 7 ++ core/src/consensus/solo/mod.rs | 7 +- core/src/consensus/tendermint/engine.rs | 21 ++++++ core/src/lib.rs | 1 + rpc/src/v1/impls/chain.rs | 4 ++ rpc/src/v1/traits/chain.rs | 4 ++ test/src/e2e.long/tendermint.test.ts | 93 ++++++++++++++++++++++++- test/src/e2e/chain.test.ts | 14 ++++ 15 files changed, 189 insertions(+), 6 deletions(-) diff --git a/core/src/client/client.rs b/core/src/client/client.rs index a8913d557a..8aa8507662 100644 --- a/core/src/client/client.rs +++ b/core/src/client/client.rs @@ -46,7 +46,7 @@ use super::{ use crate::block::{ClosedBlock, IsBlock, OpenBlock, SealedBlock}; use crate::blockchain::{BlockChain, BlockProvider, BodyProvider, HeaderProvider, InvoiceProvider, TransactionAddress}; use crate::client::{ConsensusClient, TermInfo}; -use crate::consensus::CodeChainEngine; +use crate::consensus::{CodeChainEngine, EngineError}; use crate::encoded; use crate::error::{BlockImportError, Error, ImportError, SchemeError}; use crate::miner::{Miner, MinerService}; @@ -518,6 +518,18 @@ impl EngineInfo for Client { fn recommended_confirmation(&self) -> u32 { self.engine().recommended_confirmation() } + + fn possible_authors(&self, block_number: Option) -> Result>, EngineError> { + let network_id = self.common_params(BlockId::Latest).unwrap().network_id(); + if block_number == Some(0) { + let genesis_author = self.block_header(&0.into()).expect("genesis block").author(); + return Ok(Some(vec![PlatformAddress::new_v1(network_id, genesis_author)])) + } + let addresses = self.engine().possible_authors(block_number)?; + Ok(addresses.map(|addresses| { + addresses.into_iter().map(|address| PlatformAddress::new_v1(network_id, address)).collect() + })) + } } impl EngineClient for Client { diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 17f0e88d3e..480d555aa8 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -44,6 +44,7 @@ use primitives::{Bytes, H160, H256, U256}; use crate::block::{ClosedBlock, OpenBlock, SealedBlock}; use crate::blockchain_info::BlockChainInfo; +use crate::consensus::EngineError; use crate::encoded; use crate::error::BlockImportError; use crate::transaction::{LocalizedTransaction, PendingSignedTransactions}; @@ -90,6 +91,7 @@ pub trait EngineInfo: Send + Sync { fn block_reward(&self, block_number: u64) -> u64; fn mining_reward(&self, block_number: u64) -> Option; fn recommended_confirmation(&self) -> u32; + fn possible_authors(&self, block_number: Option) -> Result>, EngineError>; } /// Client facilities used by internally sealing Engines. diff --git a/core/src/client/test_client.rs b/core/src/client/test_client.rs index e4cdeb6373..18dfa2c7ac 100644 --- a/core/src/client/test_client.rs +++ b/core/src/client/test_client.rs @@ -58,6 +58,7 @@ use crate::client::{ AccountData, BlockChainClient, BlockChainTrait, BlockProducer, BlockStatus, EngineInfo, ImportBlock, MiningBlockChainClient, StateInfo, StateOrBlock, TermInfo, }; +use crate::consensus::EngineError; use crate::db::{COL_STATE, NUM_COLUMNS}; use crate::encoded; use crate::error::BlockImportError; @@ -599,6 +600,10 @@ impl EngineInfo for TestBlockChainClient { fn recommended_confirmation(&self) -> u32 { unimplemented!() } + + fn possible_authors(&self, _block_number: Option) -> Result>, EngineError> { + unimplemented!() + } } impl ConsensusClient for TestBlockChainClient {} diff --git a/core/src/consensus/blake_pow/mod.rs b/core/src/consensus/blake_pow/mod.rs index 4677c8f7ca..0b9d850b19 100644 --- a/core/src/consensus/blake_pow/mod.rs +++ b/core/src/consensus/blake_pow/mod.rs @@ -19,6 +19,7 @@ mod params; use std::cmp::{max, min}; use ccrypto::blake256; +use ckey::Address; use ctypes::util::unexpected::{Mismatch, OutOfBounds}; use ctypes::{CommonParams, Header}; use primitives::U256; @@ -28,7 +29,7 @@ use self::params::BlakePoWParams; use super::ConsensusEngine; use crate::block::ExecutedBlock; use crate::codechain_machine::CodeChainMachine; -use crate::consensus::EngineType; +use crate::consensus::{EngineError, EngineType}; use crate::error::{BlockError, Error}; /// BlakePoW specific seal @@ -182,4 +183,8 @@ impl ConsensusEngine for BlakePoW { fn recommended_confirmation(&self) -> u32 { self.params.recommmended_confirmation } + + fn possible_authors(&self, _block_number: Option) -> Result>, EngineError> { + Ok(None) + } } diff --git a/core/src/consensus/cuckoo/mod.rs b/core/src/consensus/cuckoo/mod.rs index 5a7f55674e..3e7fec50db 100644 --- a/core/src/consensus/cuckoo/mod.rs +++ b/core/src/consensus/cuckoo/mod.rs @@ -19,6 +19,7 @@ mod params; use std::cmp::{max, min}; use ccrypto::blake256; +use ckey::Address; use ctypes::util::unexpected::{Mismatch, OutOfBounds}; use ctypes::{CommonParams, Header}; use cuckoo::Cuckoo as CuckooVerifier; @@ -29,7 +30,7 @@ use self::params::CuckooParams; use super::ConsensusEngine; use crate::block::ExecutedBlock; use crate::codechain_machine::CodeChainMachine; -use crate::consensus::EngineType; +use crate::consensus::{EngineError, EngineType}; use crate::error::{BlockError, Error}; /// Cuckoo specific seal @@ -192,6 +193,10 @@ impl ConsensusEngine for Cuckoo { fn recommended_confirmation(&self) -> u32 { self.params.recommmended_confirmation } + + fn possible_authors(&self, _block_number: Option) -> Result>, EngineError> { + Ok(None) + } } #[cfg(test)] diff --git a/core/src/consensus/mod.rs b/core/src/consensus/mod.rs index 41a4b62290..98b1975e4c 100644 --- a/core/src/consensus/mod.rs +++ b/core/src/consensus/mod.rs @@ -278,6 +278,8 @@ pub trait ConsensusEngine: Sync + Send { fn find_action_handler_for(&self, id: u64) -> Option<&ActionHandler> { self.action_handlers().iter().find(|handler| handler.handler_id() == id).map(AsRef::as_ref) } + + fn possible_authors(&self, block_number: Option) -> Result>, EngineError>; } /// Voting errors. diff --git a/core/src/consensus/null_engine/mod.rs b/core/src/consensus/null_engine/mod.rs index f804331cac..516521b3f8 100644 --- a/core/src/consensus/null_engine/mod.rs +++ b/core/src/consensus/null_engine/mod.rs @@ -16,13 +16,14 @@ mod params; +use ckey::Address; use ctypes::{CommonParams, Header}; use self::params::NullEngineParams; use super::ConsensusEngine; use crate::block::ExecutedBlock; use crate::codechain_machine::CodeChainMachine; -use crate::consensus::EngineType; +use crate::consensus::{EngineError, EngineType}; use crate::error::Error; /// An engine which does not provide any consensus mechanism and does not seal blocks. @@ -77,4 +78,8 @@ impl ConsensusEngine for NullEngine { fn recommended_confirmation(&self) -> u32 { 1 } + + fn possible_authors(&self, _block_number: Option) -> Result>, EngineError> { + Ok(None) + } } diff --git a/core/src/consensus/simple_poa/mod.rs b/core/src/consensus/simple_poa/mod.rs index 6e79a6f6bb..281506c6ae 100644 --- a/core/src/consensus/simple_poa/mod.rs +++ b/core/src/consensus/simple_poa/mod.rs @@ -48,6 +48,7 @@ impl SimplePoA { SimplePoA { machine, signer: Default::default(), + // If you want to change the type of validator set, please fix possible_authors first. validators: Box::new(RoundRobinValidator::new(params.validators)), block_reward: params.block_reward, } @@ -141,6 +142,12 @@ impl ConsensusEngine for SimplePoA { fn recommended_confirmation(&self) -> u32 { 1 } + + fn possible_authors(&self, _block_number: Option) -> Result>, EngineError> { + // TODO: It works because the round robin validator doesn't use the parent hash. + let parent = 0.into(); + Ok(Some(self.validators.addresses(&parent))) + } } #[cfg(test)] diff --git a/core/src/consensus/solo/mod.rs b/core/src/consensus/solo/mod.rs index 921e321384..a53290b81b 100644 --- a/core/src/consensus/solo/mod.rs +++ b/core/src/consensus/solo/mod.rs @@ -18,6 +18,7 @@ mod params; use std::sync::Arc; +use ckey::Address; use cstate::{ActionHandler, HitHandler}; use ctypes::{CommonParams, Header}; @@ -26,7 +27,7 @@ use super::stake; use super::{ConsensusEngine, Seal}; use crate::block::{ExecutedBlock, IsBlock}; use crate::codechain_machine::CodeChainMachine; -use crate::consensus::EngineType; +use crate::consensus::{EngineError, EngineType}; use crate::error::Error; /// A consensus engine which does not provide any consensus mechanism. @@ -136,6 +137,10 @@ impl ConsensusEngine for Solo { fn action_handlers(&self) -> &[Arc] { &self.action_handlers } + + fn possible_authors(&self, _block_number: Option) -> Result>, EngineError> { + Ok(None) + } } #[cfg(test)] diff --git a/core/src/consensus/tendermint/engine.rs b/core/src/consensus/tendermint/engine.rs index f16dac14cc..49711297d5 100644 --- a/core/src/consensus/tendermint/engine.rs +++ b/core/src/consensus/tendermint/engine.rs @@ -43,6 +43,7 @@ use crate::codechain_machine::CodeChainMachine; use crate::consensus::{EngineType, ValidatorSet}; use crate::error::Error; use crate::views::HeaderView; +use crate::BlockId; use consensus::tendermint::params::TimeGapParams; impl ConsensusEngine for Tendermint { @@ -292,6 +293,26 @@ impl ConsensusEngine for Tendermint { fn action_handlers(&self) -> &[Arc] { &self.action_handlers } + + fn possible_authors(&self, block_number: Option) -> Result>, EngineError> { + let client = self + .client + .read() + .as_ref() + .ok_or(EngineError::CannotOpenBlock)? + .upgrade() + .ok_or(EngineError::CannotOpenBlock)?; + let block_hash = match block_number { + None => { + client.block_header(&BlockId::Latest).expect("latest block must exist").hash() // the latest block + } + Some(block_number) => { + assert_ne!(0, block_number); + client.block_header(&(block_number - 1).into()).ok_or(EngineError::CannotOpenBlock)?.hash() // the parent of the given block number + } + }; + Ok(Some(self.validators.addresses(&block_hash))) + } } fn calculate_pending_rewards_of_the_previous_term( diff --git a/core/src/lib.rs b/core/src/lib.rs index c011500df2..4a635673de 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -56,6 +56,7 @@ extern crate util_error; #[macro_use] extern crate log; extern crate core; +extern crate hyper; mod account_provider; pub mod block; diff --git a/rpc/src/v1/impls/chain.rs b/rpc/src/v1/impls/chain.rs index 192a9bfb12..0a870eeb22 100644 --- a/rpc/src/v1/impls/chain.rs +++ b/rpc/src/v1/impls/chain.rs @@ -316,6 +316,10 @@ where } } + fn get_possible_authors(&self, block_number: Option) -> Result>> { + Ok(self.client.possible_authors(block_number).map_err(errors::core)?) + } + fn execute_transaction(&self, tx: UnsignedTransaction, sender: PlatformAddress) -> Result> { let sender_address = sender.try_address().map_err(errors::core)?; let action = Action::try_from(tx.action).map_err(errors::conversion)?; diff --git a/rpc/src/v1/traits/chain.rs b/rpc/src/v1/traits/chain.rs index b164bc1c8b..8c6f357260 100644 --- a/rpc/src/v1/traits/chain.rs +++ b/rpc/src/v1/traits/chain.rs @@ -145,6 +145,10 @@ build_rpc_trait! { #[rpc(name = "chain_getTermMetadata")] fn get_term_metadata(&self, Option) -> Result>; + /// Return the valid block authors + #[rpc(name = "chain_getPossibleAuthors")] + fn get_possible_authors(&self, Option) -> Result>>; + /// Execute Transactions # [rpc(name = "chain_executeTransaction")] fn execute_transaction(&self, UnsignedTransaction, PlatformAddress) -> Result>; diff --git a/test/src/e2e.long/tendermint.test.ts b/test/src/e2e.long/tendermint.test.ts index e8afec0570..05e7b18aed 100644 --- a/test/src/e2e.long/tendermint.test.ts +++ b/test/src/e2e.long/tendermint.test.ts @@ -14,7 +14,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { expect } from "chai"; +import * as chai from "chai"; +import * as chaiAsPromised from "chai-as-promised"; import "mocha"; import { validator0Address, @@ -25,6 +26,9 @@ import { import { PromiseExpect } from "../helper/promise"; import CodeChain from "../helper/spawn"; +chai.use(chaiAsPromised); +const expect = chai.expect; + describe("Tendermint ", function() { const promiseExpect = new PromiseExpect(); let nodes: CodeChain[]; @@ -55,6 +59,93 @@ describe("Tendermint ", function() { await Promise.all(nodes.map(node => node.start())); }); + describe("getPossibleAuthors", function() { + it("latest", async function() { + const validators = [ + "tccq94guhkrfndnehnca06dlkxcfuq0gdlamvw9ga4f", + "tccq8p9hr53lnxnhzcn0d065lux7etz22azaca786tt", + "tccq8fj6lxn9tchqdqqe93yaga6fzxh5rndzu8k2gdw", + "tccq9y6e0k6af9058qq4h4ffpt9xmat2vkeyue23j8y" + ]; + expect( + await nodes[0].sdk.rpc.sendRpcRequest( + "chain_getPossibleAuthors", + [null] + ) + ).deep.equal(validators); + expect( + await nodes[1].sdk.rpc.sendRpcRequest( + "chain_getPossibleAuthors", + [null] + ) + ).deep.equal(validators); + expect( + await nodes[2].sdk.rpc.sendRpcRequest( + "chain_getPossibleAuthors", + [null] + ) + ).deep.equal(validators); + expect( + await nodes[3].sdk.rpc.sendRpcRequest( + "chain_getPossibleAuthors", + [null] + ) + ).deep.equal(validators); + }); + + it("genesis", async function() { + const validators = ["tccq94guhkrfndnehnca06dlkxcfuq0gdlamvw9ga4f"]; + expect( + await nodes[0].sdk.rpc.sendRpcRequest( + "chain_getPossibleAuthors", + [0] + ) + ).deep.equal(validators); + expect( + await nodes[1].sdk.rpc.sendRpcRequest( + "chain_getPossibleAuthors", + [0] + ) + ).deep.equal(validators); + expect( + await nodes[2].sdk.rpc.sendRpcRequest( + "chain_getPossibleAuthors", + [0] + ) + ).deep.equal(validators); + expect( + await nodes[3].sdk.rpc.sendRpcRequest( + "chain_getPossibleAuthors", + [0] + ) + ).deep.equal(validators); + }); + + it("larger than the current block", async function() { + const currentBlock = await nodes[0].getBestBlockNumber(); + expect( + nodes[0].sdk.rpc.sendRpcRequest("chain_getPossibleAuthors", [ + currentBlock + 10 + ]) + ).be.rejectedWith("Engine"); + expect( + nodes[1].sdk.rpc.sendRpcRequest("chain_getPossibleAuthors", [ + currentBlock + 100 + ]) + ).be.rejectedWith("Engine"); + expect( + nodes[2].sdk.rpc.sendRpcRequest("chain_getPossibleAuthors", [ + currentBlock + 1000 + ]) + ).be.rejectedWith("Engine"); + expect( + nodes[3].sdk.rpc.sendRpcRequest("chain_getPossibleAuthors", [ + currentBlock + 10000 + ]) + ).be.rejectedWith("Engine"); + }); + }); + it("Block generation", async function() { const startHight = await nodes[0].getBestBlockNumber(); diff --git a/test/src/e2e/chain.test.ts b/test/src/e2e/chain.test.ts index 781f0dd98c..2defa6d2fd 100644 --- a/test/src/e2e/chain.test.ts +++ b/test/src/e2e/chain.test.ts @@ -52,6 +52,20 @@ describe("chain", function() { expect(await node.sdk.rpc.chain.getBestBlockNumber()).to.be.a("number"); }); + it("getPossibleAuthors", async function() { + expect( + await node.sdk.rpc.sendRpcRequest("chain_getPossibleAuthors", [ + null + ]) + ).be.null; + }); + + it("getPossibleAuthors of the genesis block", async function() { + expect( + await node.sdk.rpc.sendRpcRequest("chain_getPossibleAuthors", [0]) + ).deep.equal(["tccqyqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhhn9p3"]); + }); + it("getBestBlockId", async function() { const value = await node.sdk.rpc.sendRpcRequest( "chain_getBestBlockId",