diff --git a/ethcore/native_contracts/build.rs b/ethcore/native_contracts/build.rs
index e7985b388..979535057 100644
--- a/ethcore/native_contracts/build.rs
+++ b/ethcore/native_contracts/build.rs
@@ -29,6 +29,7 @@ const SECRETSTORE_ACL_STORAGE_ABI: &'static str = include_str!("res/secretstore_
const VALIDATOR_SET_ABI: &'static str = include_str!("res/validator_set.json");
const VALIDATOR_REPORT_ABI: &'static str = include_str!("res/validator_report.json");
const PEER_SET_ABI: &'static str = include_str!("res/peer_set.json");
+const TX_ACL_ABI: &'static str = include_str!("res/tx_acl.json");
const TEST_VALIDATOR_SET_ABI: &'static str = include_str!("res/test_validator_set.json");
@@ -55,6 +56,7 @@ fn main() {
build_file("ValidatorSet", VALIDATOR_SET_ABI, "validator_set.rs");
build_file("ValidatorReport", VALIDATOR_REPORT_ABI, "validator_report.rs");
build_file("PeerSet", PEER_SET_ABI, "peer_set.rs");
+ build_file("TransactAcl", TX_ACL_ABI, "tx_acl.rs");
build_test_contracts();
}
diff --git a/ethcore/native_contracts/generator/src/lib.rs b/ethcore/native_contracts/generator/src/lib.rs
index 996ee4969..8de7555d2 100644
--- a/ethcore/native_contracts/generator/src/lib.rs
+++ b/ethcore/native_contracts/generator/src/lib.rs
@@ -302,7 +302,8 @@ fn detokenize(name: &str, output_type: ParamType) -> String {
}
ParamType::Uint(width) => {
let read_uint = match width {
- 8 | 16 | 32 | 64 => format!("bigint::prelude::U256(u).low_u64() as u{}", width),
+ 8 => "u[31] as u8".into(),
+ 16 | 32 | 64 => format!("BigEndian::read_u{}(&u[{}..])", width, 32 - (width / 8)),
_ => format!("bigint::prelude::U{}::from(&u[..])", width),
};
diff --git a/ethcore/native_contracts/res/tx_acl.json b/ethcore/native_contracts/res/tx_acl.json
new file mode 100644
index 000000000..cff9956de
--- /dev/null
+++ b/ethcore/native_contracts/res/tx_acl.json
@@ -0,0 +1 @@
+[{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"allowedTxTypes","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]
diff --git a/ethcore/native_contracts/src/lib.rs b/ethcore/native_contracts/src/lib.rs
index 733dea80e..c37a13504 100644
--- a/ethcore/native_contracts/src/lib.rs
+++ b/ethcore/native_contracts/src/lib.rs
@@ -31,6 +31,7 @@ mod secretstore_acl_storage;
mod validator_set;
mod validator_report;
mod peer_set;
+mod tx_acl;
pub mod test_contracts;
@@ -42,3 +43,4 @@ pub use self::secretstore_acl_storage::SecretStoreAclStorage;
pub use self::validator_set::ValidatorSet;
pub use self::validator_report::ValidatorReport;
pub use self::peer_set::PeerSet;
+pub use self::tx_acl::TransactAcl;
diff --git a/ethcore/native_contracts/src/tx_acl.rs b/ethcore/native_contracts/src/tx_acl.rs
new file mode 100644
index 000000000..1ab4c8e5d
--- /dev/null
+++ b/ethcore/native_contracts/src/tx_acl.rs
@@ -0,0 +1,21 @@
+// Copyright 2015-2017 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+#![allow(unused_mut, unused_variables, unused_imports)]
+
+//! Transact permissions contract.
+
+include!(concat!(env!("OUT_DIR"), "/tx_acl.rs"));
diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs
index e749bb0ae..448188676 100644
--- a/ethcore/src/client/client.rs
+++ b/ethcore/src/client/client.rs
@@ -1446,6 +1446,10 @@ impl BlockChainClient for Client {
self.state_at(id).and_then(|s| s.code(address).ok()).map(|c| c.map(|c| (&*c).clone()))
}
+ fn code_hash(&self, address: &Address, id: BlockId) -> Option {
+ self.state_at(id).and_then(|s| s.code_hash(address).ok())
+ }
+
fn balance(&self, address: &Address, id: BlockId) -> Option {
self.state_at(id).and_then(|s| s.balance(address).ok())
}
diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs
index da98175d6..0cd9fedc8 100644
--- a/ethcore/src/client/test_client.rs
+++ b/ethcore/src/client/test_client.rs
@@ -456,6 +456,13 @@ impl BlockChainClient for TestBlockChainClient {
}
}
+ fn code_hash(&self, address: &Address, id: BlockId) -> Option {
+ match id {
+ BlockId::Latest | BlockId::Pending => self.code.read().get(address).map(|c| keccak(&c)),
+ _ => None,
+ }
+ }
+
fn balance(&self, address: &Address, id: BlockId) -> Option {
match id {
BlockId::Latest | BlockId::Pending => Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero)),
diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs
index 5d049039b..45736e2c5 100644
--- a/ethcore/src/client/traits.rs
+++ b/ethcore/src/client/traits.rs
@@ -98,6 +98,9 @@ pub trait BlockChainClient : Sync + Send {
.expect("code will return Some if given BlockId::Latest; qed")
}
+ /// Get address code hash at given block's state.
+ fn code_hash(&self, address: &Address, id: BlockId) -> Option;
+
/// Get address balance at the given block's state.
///
/// May not return None if given BlockId::Latest.
diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs
index ae27a0940..8b0554e5a 100644
--- a/ethcore/src/error.rs
+++ b/ethcore/src/error.rs
@@ -83,6 +83,8 @@ pub enum TransactionError {
CodeBanned,
/// Invalid chain ID given.
InvalidChainId,
+ /// Not enough permissions given by permission contract.
+ NotAllowed,
}
impl fmt::Display for TransactionError {
@@ -107,6 +109,7 @@ impl fmt::Display for TransactionError {
RecipientBanned => "Recipient is temporarily banned.".into(),
CodeBanned => "Contract code is temporarily banned.".into(),
InvalidChainId => "Transaction of this chain ID is not allowed on this chain.".into(),
+ NotAllowed => "Sender does not have permissions to execute this type of transction".into(),
};
f.write_fmt(format_args!("Transaction error ({})", msg))
diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs
index 0b3cab6ef..b11700e09 100644
--- a/ethcore/src/ethereum/ethash.rs
+++ b/ethcore/src/ethereum/ethash.rs
@@ -17,7 +17,7 @@
use std::path::Path;
use std::cmp;
use std::collections::{BTreeMap, HashMap};
-use std::sync::Arc;
+use std::sync::{Arc, Weak};
use hash::{KECCAK_EMPTY_LIST_RLP};
use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager};
use bigint::prelude::U256;
@@ -32,13 +32,15 @@ use trace::{Tracer, ExecutiveTracer, RewardType};
use header::{Header, BlockNumber};
use state::CleanupMode;
use spec::CommonParams;
-use transaction::UnverifiedTransaction;
+use transaction::{UnverifiedTransaction, SignedTransaction};
use engines::{self, Engine};
use evm::Schedule;
use ethjson;
use rlp::{self, UntrustedRlp};
use vm::LastHashes;
use semantic_version::SemanticVersion;
+use tx_filter::{TransactionFilter};
+use client::{Client, BlockChainClient};
/// Parity tries to round block.gas_limit to multiple of this constant
pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]);
@@ -144,6 +146,7 @@ pub struct Ethash {
ethash_params: EthashParams,
builtins: BTreeMap,
pow: EthashManager,
+ tx_filter: Option,
}
impl Ethash {
@@ -155,6 +158,7 @@ impl Ethash {
builtins: BTreeMap,
) -> Arc {
Arc::new(Ethash {
+ tx_filter: TransactionFilter::from_params(¶ms),
params,
ethash_params,
builtins,
@@ -440,6 +444,14 @@ impl Engine for Arc {
Ok(())
}
+ fn verify_transaction(&self, t: UnverifiedTransaction, header: &Header) -> Result {
+ let signed = SignedTransaction::new(t)?;
+ if !self.tx_filter.as_ref().map_or(true, |filter| filter.transaction_allowed(header.parent_hash(), &signed)) {
+ return Err(From::from(TransactionError::NotAllowed));
+ }
+ Ok(signed)
+ }
+
fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engines::ConstructedVerifier<'a> {
engines::ConstructedVerifier::Trusted(Box::new(self.clone()))
}
@@ -447,6 +459,13 @@ impl Engine for Arc {
fn snapshot_components(&self) -> Option> {
Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS)))
}
+
+ fn register_client(&self, client: Weak) {
+ if let Some(ref filter) = self.tx_filter {
+ filter.register_client(client as Weak);
+ }
+ }
+
}
// Try to round gas_limit a bit so that:
diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs
index 382887543..2f2329d64 100644
--- a/ethcore/src/lib.rs
+++ b/ethcore/src/lib.rs
@@ -173,6 +173,7 @@ mod executive;
mod externalities;
mod blockchain;
mod factory;
+mod tx_filter;
#[cfg(test)]
mod tests;
diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs
index 39da8cba7..727b08584 100644
--- a/ethcore/src/spec/spec.rs
+++ b/ethcore/src/spec/spec.rs
@@ -105,6 +105,8 @@ pub struct CommonParams {
pub registrar: Address,
/// Node permission managing contract address.
pub node_permission_contract: Option,
+ /// Transaction permission managing contract address.
+ pub transaction_permission_contract: Option,
}
impl CommonParams {
@@ -177,6 +179,7 @@ impl From for CommonParams {
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
registrar: p.registrar.map_or_else(Address::new, Into::into),
node_permission_contract: p.node_permission_contract.map(Into::into),
+ transaction_permission_contract: p.transaction_permission_contract.map(Into::into),
}
}
}
diff --git a/ethcore/src/tx_filter.rs b/ethcore/src/tx_filter.rs
new file mode 100644
index 000000000..f39b92451
--- /dev/null
+++ b/ethcore/src/tx_filter.rs
@@ -0,0 +1,233 @@
+// Copyright 2015-2017 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+//! Smart contract based transaction filter.
+
+use std::sync::Weak;
+use std::collections::HashMap;
+use std::collections::hash_map::Entry;
+use native_contracts::TransactAcl as Contract;
+use client::{BlockChainClient, BlockId, ChainNotify};
+use util::{Address, H256, Bytes};
+use parking_lot::{Mutex, RwLock};
+use futures::{self, Future};
+use spec::CommonParams;
+use transaction::{Action, SignedTransaction};
+use hash::KECCAK_EMPTY;
+
+const MAX_CACHE_SIZE: usize = 4096;
+
+mod tx_permissions {
+ pub const _ALL: u32 = 0xffffffff;
+ pub const NONE: u32 = 0x0;
+ pub const BASIC: u32 = 0b00000001;
+ pub const CALL: u32 = 0b00000010;
+ pub const CREATE: u32 = 0b00000100;
+ pub const _PRIVATE: u32 = 0b00001000;
+}
+
+/// Connection filter that uses a contract to manage permissions.
+pub struct TransactionFilter {
+ contract: Mutex