diff --git a/core/src/client/client.rs b/core/src/client/client.rs
index ba1538effe..9c3fb6824c 100644
--- a/core/src/client/client.rs
+++ b/core/src/client/client.rs
@@ -20,7 +20,7 @@ use std::sync::{Arc, Weak};
use std::time::Instant;
use cio::IoChannel;
-use ckey::{Address, PlatformAddress, Public};
+use ckey::{Address, NetworkId, PlatformAddress, Public};
use cmerkle::Result as TrieResult;
use cnetwork::NodeId;
use cstate::{
@@ -892,7 +892,31 @@ impl BlockProducer for Client {
}
}
-impl MiningBlockChainClient for Client {}
+impl MiningBlockChainClient for Client {
+ fn get_malicious_users(&self) -> Vec
{
+ self.importer.miner.get_malicious_users()
+ }
+
+ fn release_malicious_users(&self, prisoner_vec: Vec) {
+ self.importer.miner.release_malicious_users(prisoner_vec)
+ }
+
+ fn imprison_malicious_users(&self, prisoner_vec: Vec) {
+ self.importer.miner.imprison_malicious_users(prisoner_vec)
+ }
+
+ fn get_immune_users(&self) -> Vec {
+ self.importer.miner.get_immune_users()
+ }
+
+ fn register_immune_users(&self, immune_user_vec: Vec) {
+ self.importer.miner.register_immune_users(immune_user_vec)
+ }
+
+ fn get_network_id(&self) -> NetworkId {
+ self.common_params(BlockId::Latest).unwrap().network_id()
+ }
+}
impl ChainTimeInfo for Client {
fn transaction_block_age(&self, tracker: &H256, parent_block_number: BlockNumber) -> Option {
diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs
index 33594dcf4a..d80f5b0301 100644
--- a/core/src/client/mod.rs
+++ b/core/src/client/mod.rs
@@ -32,7 +32,7 @@ pub use self::test_client::TestBlockChainClient;
use std::ops::Range;
use std::sync::Arc;
-use ckey::{Address, PlatformAddress, Public};
+use ckey::{Address, NetworkId, PlatformAddress, Public};
use cmerkle::Result as TrieResult;
use cnetwork::NodeId;
use cstate::{AssetScheme, FindActionHandler, OwnedAsset, StateResult, Text, TopLevelState, TopStateView};
@@ -271,7 +271,25 @@ pub trait BlockProducer {
}
/// Extended client interface used for mining
-pub trait MiningBlockChainClient: BlockChainClient + BlockProducer + FindActionHandler {}
+pub trait MiningBlockChainClient: BlockChainClient + BlockProducer + FindActionHandler {
+ /// Returns malicious users who sent failing transactions.
+ fn get_malicious_users(&self) -> Vec;
+
+ /// Release designated users from the malicious user list.
+ fn release_malicious_users(&self, prisoner_vec: Vec);
+
+ /// Append designated users to the malicious user list.
+ fn imprison_malicious_users(&self, prisoner_vec: Vec);
+
+ /// Returns users immune from getting banned.
+ fn get_immune_users(&self) -> Vec;
+
+ /// Append designated users to the immune user list.
+ fn register_immune_users(&self, immune_user_vec: Vec);
+
+ /// Returns network id.
+ fn get_network_id(&self) -> NetworkId;
+}
/// Provides methods to access database.
pub trait DatabaseClient {
diff --git a/core/src/client/test_client.rs b/core/src/client/test_client.rs
index f823758c79..af2791e459 100644
--- a/core/src/client/test_client.rs
+++ b/core/src/client/test_client.rs
@@ -362,7 +362,31 @@ impl BlockProducer for TestBlockChainClient {
}
}
-impl MiningBlockChainClient for TestBlockChainClient {}
+impl MiningBlockChainClient for TestBlockChainClient {
+ fn get_malicious_users(&self) -> Vec {
+ self.miner.get_malicious_users()
+ }
+
+ fn release_malicious_users(&self, prisoner_vec: Vec) {
+ self.miner.release_malicious_users(prisoner_vec)
+ }
+
+ fn imprison_malicious_users(&self, prisoner_vec: Vec) {
+ self.miner.imprison_malicious_users(prisoner_vec)
+ }
+
+ fn get_immune_users(&self) -> Vec {
+ self.miner.get_immune_users()
+ }
+
+ fn register_immune_users(&self, immune_user_vec: Vec) {
+ self.miner.register_immune_users(immune_user_vec)
+ }
+
+ fn get_network_id(&self) -> NetworkId {
+ NetworkId::default()
+ }
+}
impl AccountData for TestBlockChainClient {
fn seq(&self, address: &Address, id: BlockId) -> Option {
diff --git a/core/src/miner/mem_pool.rs b/core/src/miner/mem_pool.rs
index d95627f55a..65e126388a 100644
--- a/core/src/miner/mem_pool.rs
+++ b/core/src/miner/mem_pool.rs
@@ -958,6 +958,11 @@ impl MemPool {
self.current.queue.iter().any(|tx| tx.origin.is_local())
}
+ /// Returns Some(true) if the given transaction is local and None for not found.
+ pub fn is_local_transaction(&self, tx_hash: H256) -> Option {
+ self.by_hash.get(&tx_hash).and_then(|found_item| Some(found_item.origin.is_local()))
+ }
+
/// Checks the given timelock with the current time/timestamp.
fn should_wait_timelock(timelock: &TxTimelock, best_block_number: BlockNumber, best_block_timestamp: u64) -> bool {
if let Some(block_number) = timelock.block {
diff --git a/core/src/miner/miner.rs b/core/src/miner/miner.rs
index 43f1e00dbf..09a8b5ee63 100644
--- a/core/src/miner/miner.rs
+++ b/core/src/miner/miner.rs
@@ -16,6 +16,7 @@
use std::collections::HashSet;
use std::iter::once;
+use std::iter::FromIterator;
use std::ops::Range;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@@ -23,7 +24,7 @@ use std::time::{Duration, Instant};
use ckey::{public_to_address, Address, Password, PlatformAddress, Public};
use cstate::{FindActionHandler, TopLevelState};
-use ctypes::errors::HistoryError;
+use ctypes::errors::{HistoryError, RuntimeError};
use ctypes::transaction::{Action, IncompleteTransaction, Timelock};
use ctypes::{BlockNumber, Header};
use cvm::ChainTimeInfo;
@@ -127,6 +128,8 @@ pub struct Miner {
accounts: Option>,
notifiers: RwLock>>,
+ malicious_users: RwLock>,
+ immune_users: RwLock>,
}
impl Miner {
@@ -184,6 +187,8 @@ impl Miner {
sealing_enabled: AtomicBool::new(true),
accounts,
notifiers: RwLock::new(notifiers),
+ malicious_users: RwLock::new(HashSet::new()),
+ immune_users: RwLock::new(HashSet::new()),
}
}
@@ -269,6 +274,27 @@ impl Miner {
.into_iter()
.map(|tx| {
let hash = tx.hash();
+ // FIXME: Refactoring is needed. recover_public is calling in verify_transaction_unordered.
+ let signer_public = tx.recover_public()?;
+ let signer_address = public_to_address(&signer_public);
+ if default_origin.is_local() {
+ self.immune_users.write().insert(signer_address);
+ }
+
+ let origin = self
+ .accounts
+ .as_ref()
+ .and_then(|accounts| match accounts.has_public(&signer_public) {
+ Ok(true) => Some(TxOrigin::Local),
+ Ok(false) => None,
+ Err(_) => None,
+ })
+ .unwrap_or(default_origin);
+
+ if self.malicious_users.read().contains(&signer_address) {
+ // FIXME: just to skip, think about another way.
+ return Ok(())
+ }
if client.transaction_block(&TransactionId::Hash(hash)).is_some() {
cdebug!(MINER, "Rejected transaction {:?}: already in the blockchain", hash);
return Err(HistoryError::TransactionAlreadyImported.into())
@@ -276,7 +302,8 @@ impl Miner {
if !self.is_allowed_transaction(&tx.action) {
cdebug!(MINER, "Rejected transaction {:?}: {:?} is not allowed transaction", hash, tx.action);
}
- match tx
+ let immune_users = self.immune_users.read();
+ let tx = tx
.verify_basic()
.map_err(From::from)
.and_then(|_| {
@@ -284,33 +311,34 @@ impl Miner {
self.engine.verify_transaction_with_params(&tx, &common_params)
})
.and_then(|_| CodeChainMachine::verify_transaction_seal(tx, &fake_header))
- {
- Err(e) => {
+ .map_err(|e| {
+ match e {
+ Error::Syntax(_) if !origin.is_local() && !immune_users.contains(&signer_address) => {
+ self.malicious_users.write().insert(signer_address);
+ }
+ _ => {}
+ }
cdebug!(MINER, "Rejected transaction {:?} with invalid signature: {:?}", hash, e);
- Err(e)
+ e
+ })?;
+
+ // This check goes here because verify_transaction takes SignedTransaction parameter
+ self.engine.machine().verify_transaction(&tx, &fake_header, client, false).map_err(|e| {
+ match e {
+ Error::Syntax(_) if !origin.is_local() && !immune_users.contains(&signer_address) => {
+ self.malicious_users.write().insert(signer_address);
+ }
+ _ => {}
}
- Ok(tx) => {
- // This check goes here because verify_transaction takes SignedTransaction parameter
- self.engine.machine().verify_transaction(&tx, &fake_header, client, false)?;
-
- let origin = self
- .accounts
- .as_ref()
- .and_then(|accounts| match accounts.has_public(&tx.signer_public()) {
- Ok(true) => Some(TxOrigin::Local),
- Ok(false) => None,
- Err(_) => None,
- })
- .unwrap_or(default_origin);
+ e
+ })?;
- let timelock = self.calculate_timelock(&tx, client)?;
- let tx_hash = tx.hash();
+ let timelock = self.calculate_timelock(&tx, client)?;
+ let tx_hash = tx.hash();
- to_insert.push(MemPoolInput::new(tx, origin, timelock));
- tx_hashes.push(tx_hash);
- Ok(())
- }
- }
+ to_insert.push(MemPoolInput::new(tx, origin, timelock));
+ tx_hashes.push(tx_hash);
+ Ok(())
})
.collect();
@@ -499,15 +527,22 @@ impl Miner {
let mut tx_count: usize = 0;
let tx_total = transactions.len();
- let mut invald_tx_users = HashSet::new();
+ let mut invalid_tx_users = HashSet::new();
+
+ let immune_users = self.immune_users.read();
for tx in transactions {
let signer_public = tx.signer_public();
- if invald_tx_users.contains(&signer_public) {
+ let signer_address = public_to_address(&signer_public);
+ if self.malicious_users.read().contains(&signer_address) {
+ invalid_transactions.push(tx.hash());
+ continue
+ }
+ if invalid_tx_users.contains(&signer_public) {
// The previous transaction has failed
continue
}
if !self.is_allowed_transaction(&tx.action) {
- invald_tx_users.insert(signer_public);
+ invalid_tx_users.insert(signer_public);
invalid_transactions.push(tx.hash());
continue
}
@@ -524,7 +559,22 @@ impl Miner {
// already have transaction - ignore
Err(Error::History(HistoryError::TransactionAlreadyImported)) => {}
Err(e) => {
- invald_tx_users.insert(signer_public);
+ match e {
+ Error::Runtime(RuntimeError::AssetSupplyOverflow)
+ | Error::Runtime(RuntimeError::InvalidScript) => {
+ if !self
+ .mem_pool
+ .read()
+ .is_local_transaction(hash)
+ .expect("The tx is clearly fetched from the mempool")
+ && !immune_users.contains(&signer_address)
+ {
+ self.malicious_users.write().insert(signer_address);
+ }
+ }
+ _ => {}
+ }
+ invalid_tx_users.insert(signer_public);
invalid_transactions.push(hash);
cinfo!(
MINER,
@@ -1074,6 +1124,35 @@ impl MinerService for Miner {
cdebug!(MINER, "Stop sealing");
self.sealing_enabled.store(false, Ordering::Relaxed);
}
+
+ fn get_malicious_users(&self) -> Vec {
+ Vec::from_iter(self.malicious_users.read().iter().map(Clone::clone))
+ }
+
+ fn release_malicious_users(&self, prisoner_vec: Vec) {
+ let mut malicious_users = self.malicious_users.write();
+ for address in prisoner_vec {
+ malicious_users.remove(&address);
+ }
+ }
+
+ fn imprison_malicious_users(&self, prisoner_vec: Vec) {
+ let mut malicious_users = self.malicious_users.write();
+ for address in prisoner_vec {
+ malicious_users.insert(address);
+ }
+ }
+
+ fn get_immune_users(&self) -> Vec {
+ Vec::from_iter(self.immune_users.read().iter().map(Clone::clone))
+ }
+
+ fn register_immune_users(&self, immune_user_vec: Vec) {
+ let mut immune_users = self.immune_users.write();
+ for address in immune_user_vec {
+ immune_users.insert(address);
+ }
+ }
}
fn get_next_seq(transactions: impl IntoIterator- , addresses: &[Address]) -> Option {
diff --git a/core/src/miner/mod.rs b/core/src/miner/mod.rs
index 63f599efe7..1922977013 100644
--- a/core/src/miner/mod.rs
+++ b/core/src/miner/mod.rs
@@ -150,6 +150,21 @@ pub trait MinerService: Send + Sync {
/// Stop sealing.
fn stop_sealing(&self);
+
+ /// Get malicious users
+ fn get_malicious_users(&self) -> Vec;
+
+ /// Release target malicious users from malicious user set.
+ fn release_malicious_users(&self, prisoner_vec: Vec);
+
+ /// Imprison target malicious users to malicious user set.
+ fn imprison_malicious_users(&self, prisoner_vec: Vec);
+
+ /// Get ban-immune users.
+ fn get_immune_users(&self) -> Vec;
+
+ /// Register users to ban-immune users.
+ fn register_immune_users(&self, immune_user_vec: Vec);
}
/// Mining status
diff --git a/rpc/src/v1/impls/mempool.rs b/rpc/src/v1/impls/mempool.rs
index d534c6fabb..e61f9d6928 100644
--- a/rpc/src/v1/impls/mempool.rs
+++ b/rpc/src/v1/impls/mempool.rs
@@ -16,8 +16,9 @@
use std::sync::Arc;
-use ccore::{BlockChainClient, SignedTransaction};
+use ccore::{BlockChainClient, MiningBlockChainClient, SignedTransaction};
use cjson::bytes::Bytes;
+use ckey::{Address, PlatformAddress};
use primitives::H256;
use rlp::UntrustedRlp;
@@ -41,7 +42,7 @@ impl MempoolClient {
impl Mempool for MempoolClient
where
- C: BlockChainClient + 'static,
+ C: BlockChainClient + MiningBlockChainClient + 'static,
{
fn send_signed_transaction(&self, raw: Bytes) -> Result {
UntrustedRlp::new(&raw.into_vec())
@@ -78,4 +79,37 @@ where
fn get_pending_transactions_count(&self, from: Option, to: Option) -> Result {
Ok(self.client.count_pending_transactions(from.unwrap_or(0)..to.unwrap_or(::std::u64::MAX)))
}
+
+ fn get_banned_accounts(&self) -> Result> {
+ let malicious_user_vec = self.client.get_malicious_users();
+ let network_id = self.client.get_network_id();
+ Ok(malicious_user_vec.into_iter().map(|address| PlatformAddress::new_v1(network_id, address)).collect())
+ }
+
+ fn unban_accounts(&self, prisoner_list: Vec) -> Result<()> {
+ let prisoner_vec: Vec = prisoner_list.into_iter().map(PlatformAddress::into_address).collect();
+
+ self.client.release_malicious_users(prisoner_vec);
+ Ok(())
+ }
+
+ fn ban_accounts(&self, prisoner_list: Vec) -> Result<()> {
+ let prisoner_vec: Vec = prisoner_list.into_iter().map(PlatformAddress::into_address).collect();
+
+ self.client.imprison_malicious_users(prisoner_vec);
+ Ok(())
+ }
+
+ fn get_immune_accounts(&self) -> Result> {
+ let immune_user_vec = self.client.get_immune_users();
+ let network_id = self.client.get_network_id();
+ Ok(immune_user_vec.into_iter().map(|address| PlatformAddress::new_v1(network_id, address)).collect())
+ }
+
+ fn register_immune_accounts(&self, immune_user_list: Vec) -> Result<()> {
+ let immune_user_vec: Vec = immune_user_list.into_iter().map(PlatformAddress::into_address).collect();
+
+ self.client.register_immune_users(immune_user_vec);
+ Ok(())
+ }
}
diff --git a/rpc/src/v1/traits/mempool.rs b/rpc/src/v1/traits/mempool.rs
index ef482ad89a..571b9fa919 100644
--- a/rpc/src/v1/traits/mempool.rs
+++ b/rpc/src/v1/traits/mempool.rs
@@ -17,6 +17,7 @@
use cjson::bytes::Bytes;
use primitives::H256;
+use ckey::PlatformAddress;
use jsonrpc_core::Result;
use super::super::types::PendingTransactions;
@@ -42,5 +43,20 @@ build_rpc_trait! {
/// Gets the count of transactions in the current mem pool.
# [rpc(name = "mempool_getPendingTransactionsCount")]
fn get_pending_transactions_count(&self, Option, Option) -> Result;
+
+ #[rpc(name = "mempool_getBannedAccounts")]
+ fn get_banned_accounts(&self) -> Result>;
+
+ #[rpc(name = "mempool_unbanAccounts")]
+ fn unban_accounts(&self, Vec) -> Result<()>;
+
+ #[rpc(name = "mempool_banAccounts")]
+ fn ban_accounts(&self, Vec) -> Result<()>;
+
+ #[rpc(name = "mempool_getImmuneAccounts")]
+ fn get_immune_accounts(&self) -> Result>;
+
+ #[rpc(name = "mempool_registerImmuneAccounts")]
+ fn register_immune_accounts(&self, Vec) -> Result<()>;
}
}
diff --git a/spec/JSON-RPC.md b/spec/JSON-RPC.md
index 8127e45591..61e2375710 100644
--- a/spec/JSON-RPC.md
+++ b/spec/JSON-RPC.md
@@ -323,6 +323,11 @@ When `Transaction` is included in any response, there will be an additional fiel
* [mempool_getTransactionResultsByTracker](#mempool_getTransactionResultsByTracker)
* [mempool_getPendingTransactions](#mempool_getpendingtransactions)
* [mempool_getPendingTransactionsCount](#mempool_getpendingtransactionscount)
+ * [mempool_getBannedAccounts](#mempool_getbannedaccounts)
+ * [mempool_unbanAccounts](#mempool_unbanaccounts)
+ * [mempool_banAccounts](#mempool_banaccounts)
+ * [mempool_registerImmuneAccounts](#mempool_registerimmuneaccounts)
+ * [mempool_getRegisteredImmuneAccounts](#mempool_getregisteredimmuneaccounts)
***
* [engine_getCoinbase](#engine_getcoinbase)
* [engine_getBlockReward](#engine_getblockreward)
@@ -1803,6 +1808,160 @@ Returns a count of the transactions that have insertion_timestamps within the gi
[Back to **List of methods**](#list-of-methods)
+## mempool_banAccounts
+Register accounts to the mempool's banned account list. The mempool would not import the transactions from the users on the list.
+
+### Params
+ 1. prisoner_list: `PlatformAccount[]`
+
+### Returns
+`null`
+
+Errors: `Invalid params`
+
+### Request Example
+```
+curl \
+ -H 'Content-Type: application/json' \
+ -d '{"jsonrpc": "2.0", "method": "mempool_getBannedAccounts", "params": [], "id": null}' \
+ localhost:8080
+```
+
+### Response Example
+```
+{
+ "jsonrpc": "2.0",
+ "result": null,
+ "id": null
+}
+```
+
+[Back to **List of methods**](#list-of-methods)
+
+## mempool_unbanAccounts
+Release accounts from the mempool's banned account list.
+
+### Params
+ 1. trusty_list: `PlatformAccount[]`
+
+### Returns
+`null`
+
+Errors: `Invalid params`
+
+### Request Example
+```
+curl \
+ -H 'Content-Type: application/json' \
+ -d '{"jsonrpc": "2.0", "method": "mempool_unbanAccounts", "params": [["tccq9h7vnl68frvqapzv3tujrxtxtwqdnxw6yamrrgd"]], "id": null}' \
+ localhost:8080
+```
+
+### Response Example
+```
+{
+ "jsonrpc": "2.0",
+ "result": null,
+ "id": null
+}
+```
+
+[Back to **List of methods**](#list-of-methods)
+
+## mempool_getBannedAccounts
+Returns accounts banned for propagating transactions which cause syntax errors or runtime errors.
+
+### Params
+No parameters
+
+### Returns
+`PlatformAddress[]`
+
+Error: `Invalid params`
+
+### Request Example
+```
+curl \
+ -H 'Content-Type: application/json' \
+ -d '{"jsonrpc": "2.0", "method": "mempool_getBannedAccounts", "params": [], "id": null}' \
+ localhost:8080
+```
+
+### Response Example
+```
+{
+ "jsonrpc": "2.0",
+ "result": [
+ "tccq9h7vnl68frvqapzv3tujrxtxtwqdnxw6yamrrgd"
+ ],
+ "id": null
+}
+```
+
+[Back to **List of methods**](#list-of-methods)
+
+## mempool_registerImmuneAccounts
+Register accounts immune from getting banned. The trasactions from these accounts would never be rejected for the reason they are malicious.
+
+### Params
+ 1. immune_user_list: `PlatformAccount[]`
+
+### Returns
+`null`
+
+Error: `Invalid params`
+
+### Request Example
+```
+curl \
+ -H 'Content-Type: application/json' \
+ -d '{"jsonrpc": "2.0", "method": "mempool_registerImmuneAccounts", "params": [["tccq9h7vnl68frvqapzv3tujrxtxtwqdnxw6yamrrgd"]], "id": null}' \
+ localhost:8080
+```
+
+### Response Example
+```
+{
+ "jsonrpc": "2.0",
+ "result": null,
+ "id": null
+}
+```
+
+[Back to **List of methods**](#list-of-methods)
+
+## mempool_getRegisteredImmuneAccounts
+Gets immune accounts registered by `mempool_registerImmuneAccounts`.
+
+### Params
+No parameters
+
+### Returns
+`PlatformAccount[]`
+
+Error: `Invalid params`
+
+### Request Example
+```
+curl \
+ -H 'Content-Type: application/json' \
+ -d '{"jsonrpc": "2.0", "method": "mempool_getImmuneAccounts", "params": [], "id": null}' \
+ localhost:8080
+```
+
+### Response Example
+```
+{
+ "jsonrpc": "2.0",
+ "result": [
+ "tccq9h7vnl68frvqapzv3tujrxtxtwqdnxw6yamrrgd"
+ ],
+ "id": null
+}
+```
+
+[Back to **List of methods**](#list-of-methods)
+
## engine_getCoinbase
Gets coinbase's account id.