diff --git a/Cargo.lock b/Cargo.lock
index d217d4e565..cfc34fb2ac 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -199,6 +199,7 @@ dependencies = [
"codechain-network 0.1.0",
"codechain-reactor 0.1.0",
"codechain-rpc 0.1.0",
+ "codechain-state 0.1.0",
"codechain-stratum 1.11.0",
"codechain-sync 0.1.0",
"codechain-types 0.1.0",
diff --git a/Cargo.toml b/Cargo.toml
index a602defdba..d780ddca90 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,7 @@ codechain-merkle = { path = "util/merkle" }
codechain-network = { path = "network" }
codechain-reactor = { path = "util/reactor" }
codechain-rpc = { path = "rpc" }
+codechain-state = { path = "state" }
codechain-sync = { path = "sync" }
codechain-types = { path = "types" }
codechain-vm = { path = "vm" }
diff --git a/codechain/config/chain_type.rs b/codechain/config/chain_type.rs
index ec10613446..d7f8c9f2f7 100644
--- a/codechain/config/chain_type.rs
+++ b/codechain/config/chain_type.rs
@@ -15,9 +15,11 @@
// along with this program. If not, see .
use std::str::FromStr;
+use std::sync::Arc;
use std::{fmt, fs};
use ccore::Spec;
+use cstate::ActionHandler;
#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "snake_case")]
@@ -66,17 +68,17 @@ impl fmt::Display for ChainType {
}
impl ChainType {
- pub fn spec<'a>(&self) -> Result {
+ pub fn spec<'a>(&self, handlers: Vec>) -> Result {
match self {
- ChainType::Solo => Ok(Spec::new_test_solo()),
- ChainType::SoloAuthority => Ok(Spec::new_test_solo_authority()),
- ChainType::Tendermint => Ok(Spec::new_test_tendermint()),
- ChainType::Cuckoo => Ok(Spec::new_test_cuckoo()),
- ChainType::BlakePoW => Ok(Spec::new_test_blake_pow()),
+ ChainType::Solo => Ok(Spec::new_test_solo(handlers)),
+ ChainType::SoloAuthority => Ok(Spec::new_test_solo_authority(handlers)),
+ ChainType::Tendermint => Ok(Spec::new_test_tendermint(handlers)),
+ ChainType::Cuckoo => Ok(Spec::new_test_cuckoo(handlers)),
+ ChainType::BlakePoW => Ok(Spec::new_test_blake_pow(handlers)),
ChainType::Custom(filename) => {
let file = fs::File::open(filename)
.map_err(|e| format!("Could not load specification file at {}: {}", filename, e))?;
- Spec::load(file)
+ Spec::load(file, handlers)
}
}
}
diff --git a/codechain/main.rs b/codechain/main.rs
index f48f94d6b1..5d76db641d 100644
--- a/codechain/main.rs
+++ b/codechain/main.rs
@@ -35,6 +35,7 @@ extern crate codechain_logger as clogger;
extern crate codechain_network as cnetwork;
extern crate codechain_reactor as creactor;
extern crate codechain_rpc as crpc;
+extern crate codechain_state as cstate;
extern crate codechain_sync as csync;
extern crate codechain_types as ctypes;
extern crate ctrlc;
@@ -69,6 +70,7 @@ use clap::ArgMatches;
use clogger::LoggerConfig;
use cnetwork::{NetworkConfig, NetworkControl, NetworkControlError, NetworkService, SocketAddr};
use creactor::EventLoop;
+use cstate::{ActionHandler, HitHandler};
use csync::{BlockSyncExtension, ParcelSyncExtension, SnapshotService};
use ctrlc::CtrlC;
use fdlimit::raise_fd_limit;
@@ -263,7 +265,10 @@ fn run_node(matches: ArgMatches) -> Result<(), String> {
let _event_loop = EventLoop::spawn();
let config = load_config(&matches)?;
- let spec = config.operating.chain.spec()?;
+
+ // Add handlers here to accept additional custom actions
+ let custom_action_handlers: Vec> = vec![Arc::new(HitHandler::new())];
+ let spec = config.operating.chain.spec(custom_action_handlers)?;
let instance_id = config.operating.instance_id.unwrap_or(
SystemTime::now()
diff --git a/core/src/block.rs b/core/src/block.rs
index 68ffc3060e..57fb586ed7 100644
--- a/core/src/block.rs
+++ b/core/src/block.rs
@@ -453,7 +453,7 @@ mod tests {
#[test]
fn open_block() {
- let spec = Spec::new_test();
+ let spec = Spec::new_test(Vec::new());
let genesis_header = spec.genesis_header();
let db = spec.ensure_genesis_state(get_temp_state_db(), &Default::default()).unwrap();
let b = OpenBlock::new(&*spec.engine, Default::default(), db, &genesis_header, Address::zero(), vec![], false)
diff --git a/core/src/client/client.rs b/core/src/client/client.rs
index 4bdbcd23c7..127d1a66e8 100644
--- a/core/src/client/client.rs
+++ b/core/src/client/client.rs
@@ -22,7 +22,10 @@ use std::time::Instant;
use cio::IoChannel;
use ckey::{Address, Public};
use cnetwork::NodeId;
-use cstate::{Asset, AssetAddress, AssetScheme, AssetSchemeAddress, StateDB, TopLevelState, TopStateInfo};
+use cstate::{
+ ActionHandler, Asset, AssetAddress, AssetScheme, AssetSchemeAddress, StateDB, TopBackend, TopLevelState,
+ TopStateInfo,
+};
use ctypes::invoice::ParcelInvoice;
use ctypes::parcel::ChangeShard;
use ctypes::transaction::Transaction;
@@ -99,7 +102,7 @@ impl Client {
let trie_factory = TrieFactory::new(trie_spec);
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::Archive, ::db::COL_STATE);
- let mut state_db = StateDB::new(journal_db, config.state_cache_size);
+ let mut state_db = StateDB::new(journal_db, config.state_cache_size, spec.custom_handlers.clone());
if !spec.check_genesis_root(state_db.as_hashdb()) {
return Err(SpecError::InvalidState.into())
}
@@ -449,6 +452,10 @@ impl BlockChainClient for Client {
})
})
}
+
+ fn custom_handlers(&self) -> Vec> {
+ self.state_db.read().custom_handlers().to_vec()
+ }
}
pub struct Importer {
diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs
index 155cb73a2d..c96f4ff50c 100644
--- a/core/src/client/mod.rs
+++ b/core/src/client/mod.rs
@@ -31,7 +31,7 @@ use std::sync::Arc;
use ckey::{Address, Public};
use cnetwork::NodeId;
-use cstate::{Asset, AssetScheme, AssetSchemeAddress, TopStateInfo};
+use cstate::{ActionHandler, Asset, AssetScheme, AssetSchemeAddress, TopStateInfo};
use ctypes::invoice::{Invoice, ParcelInvoice};
use ctypes::parcel::ChangeShard;
use ctypes::transaction::Transaction;
@@ -209,6 +209,8 @@ pub trait BlockChainClient: Sync + Send + AccountData + BlockChain + ImportBlock
fn parcel_invoice(&self, id: ParcelId) -> Option;
fn transaction_invoice(&self, id: TransactionId) -> Option;
+
+ fn custom_handlers(&self) -> Vec>;
}
/// Result of import block operation.
diff --git a/core/src/client/test_client.rs b/core/src/client/test_client.rs
index 7c6e3445f6..d34fff83f6 100644
--- a/core/src/client/test_client.rs
+++ b/core/src/client/test_client.rs
@@ -38,7 +38,7 @@ use std::sync::Arc;
use ckey::{Address, Generator, Random};
use cmerkle::skewed_merkle_root;
use cnetwork::NodeId;
-use cstate::StateDB;
+use cstate::{ActionHandler, StateDB};
use ctypes::invoice::ParcelInvoice;
use ctypes::parcel::{Action, Parcel};
use ctypes::BlockNumber;
@@ -114,7 +114,7 @@ impl TestBlockChainClient {
/// Creates new test client with specified extra data for each block
pub fn new_with_extra_data(extra_data: Bytes) -> Self {
- let spec = Spec::new_test();
+ let spec = Spec::new_test(Vec::new());
TestBlockChainClient::new_with_spec_and_extra(spec, extra_data)
}
@@ -288,7 +288,7 @@ impl TestBlockChainClient {
pub fn get_temp_state_db() -> StateDB {
let db = kvdb_memorydb::create(NUM_COLUMNS.unwrap_or(0));
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::Archive, COL_STATE);
- StateDB::new(journal_db, 1024 * 1024)
+ StateDB::new(journal_db, 1024 * 1024, Vec::new())
}
impl ReopenBlock for TestBlockChainClient {
@@ -508,6 +508,10 @@ impl BlockChainClient for TestBlockChainClient {
fn transaction_invoice(&self, _id: TransactionId) -> Option {
unimplemented!()
}
+
+ fn custom_handlers(&self) -> Vec> {
+ unimplemented!()
+ }
}
impl super::EngineClient for TestBlockChainClient {
diff --git a/core/src/consensus/solo/mod.rs b/core/src/consensus/solo/mod.rs
index c2bd884572..724061ee3e 100644
--- a/core/src/consensus/solo/mod.rs
+++ b/core/src/consensus/solo/mod.rs
@@ -88,7 +88,7 @@ mod tests {
#[test]
fn solo_can_seal() {
- let spec = Spec::new_test_solo();
+ let spec = Spec::new_test_solo(Vec::new());
let engine = &*spec.engine;
let db = spec.ensure_genesis_state(get_temp_state_db(), &Default::default()).unwrap();
let genesis_header = spec.genesis_header();
@@ -104,7 +104,7 @@ mod tests {
#[test]
fn solo_cant_verify() {
- let engine = Spec::new_test_solo().engine;
+ let engine = Spec::new_test_solo(Vec::new()).engine;
let mut header: Header = Header::default();
assert!(engine.verify_block_basic(&header).is_ok());
diff --git a/core/src/consensus/solo_authority/mod.rs b/core/src/consensus/solo_authority/mod.rs
index f9c18d0d89..226a0b94b1 100644
--- a/core/src/consensus/solo_authority/mod.rs
+++ b/core/src/consensus/solo_authority/mod.rs
@@ -207,13 +207,13 @@ mod tests {
#[test]
fn has_valid_metadata() {
- let engine = Spec::new_test_solo_authority().engine;
+ let engine = Spec::new_test_solo_authority(Vec::new()).engine;
assert!(!engine.name().is_empty());
}
#[test]
fn can_do_signature_verification_fail() {
- let engine = Spec::new_test_solo_authority().engine;
+ let engine = Spec::new_test_solo_authority(Vec::new()).engine;
let mut header: Header = Header::default();
header.set_seal(vec![::rlp::encode(&SignatureData::default()).into_vec()]);
@@ -223,7 +223,7 @@ mod tests {
#[test]
fn can_generate_seal() {
- let spec = Spec::new_test_solo_authority();
+ let spec = Spec::new_test_solo_authority(Vec::new());
let engine = &*spec.engine;
let db = spec.ensure_genesis_state(get_temp_state_db(), &Default::default()).unwrap();
let genesis_header = spec.genesis_header();
@@ -239,7 +239,7 @@ mod tests {
#[test]
fn seals_internally() {
- let engine = Spec::new_test_solo_authority().engine;
+ let engine = Spec::new_test_solo_authority(Vec::new()).engine;
assert!(!engine.seals_internally().unwrap());
}
}
diff --git a/core/src/consensus/tendermint/mod.rs b/core/src/consensus/tendermint/mod.rs
index 6d5e74ff78..e3f7989af2 100644
--- a/core/src/consensus/tendermint/mod.rs
+++ b/core/src/consensus/tendermint/mod.rs
@@ -953,13 +953,13 @@ mod tests {
#[test]
fn has_valid_metadata() {
- let engine = Spec::new_test_tendermint().engine;
+ let engine = Spec::new_test_tendermint(Vec::new()).engine;
assert!(!engine.name().is_empty());
}
#[test]
fn verification_fails_on_short_seal() {
- let engine = Spec::new_test_tendermint().engine;
+ let engine = Spec::new_test_tendermint(Vec::new()).engine;
let header = Header::default();
let verify_result = engine.verify_block_basic(&header);
diff --git a/core/src/miner/sealing_queue.rs b/core/src/miner/sealing_queue.rs
index a8be90562b..570b4c0fb5 100644
--- a/core/src/miner/sealing_queue.rs
+++ b/core/src/miner/sealing_queue.rs
@@ -82,7 +82,7 @@ mod tests {
const QUEUE_SIZE: usize = 2;
fn create_closed_block(address: Address) -> ClosedBlock {
- let spec = Spec::new_test();
+ let spec = Spec::new_test(Vec::new());
let genesis_header = spec.genesis_header();
let db = spec.ensure_genesis_state(get_temp_state_db(), &Default::default()).unwrap();
let b = OpenBlock::new(&*spec.engine, Default::default(), db, &genesis_header, address, vec![], false).unwrap();
diff --git a/core/src/spec/spec.rs b/core/src/spec/spec.rs
index cde1bf0d5c..8cef8b00c1 100644
--- a/core/src/spec/spec.rs
+++ b/core/src/spec/spec.rs
@@ -20,7 +20,9 @@ use std::sync::Arc;
use ccrypto::{blake256, BLAKE_NULL_RLP};
use cjson;
use ckey::Address;
-use cstate::{Backend, Metadata, MetadataAddress, Shard, ShardAddress, ShardMetadataAddress, StateDB, StateResult};
+use cstate::{
+ ActionHandler, Backend, Metadata, MetadataAddress, Shard, ShardAddress, ShardMetadataAddress, StateDB, StateResult,
+};
use ctypes::ShardId;
use hashdb::HashDB;
use parking_lot::RwLock;
@@ -103,6 +105,8 @@ pub struct Spec {
/// Genesis state as plain old data.
genesis_accounts: PodAccounts,
genesis_shards: PodShards,
+
+ pub custom_handlers: Vec>,
}
// helper for formatting errors.
@@ -111,8 +115,8 @@ fn fmt_err(f: F) -> String {
}
macro_rules! load_bundled {
- ($e:expr) => {
- Spec::load(include_bytes!(concat!("../../res/", $e, ".json")) as &[u8]).expect(concat!(
+ ($e:expr, $h:expr) => {
+ Spec::load(include_bytes!(concat!("../../res/", $e, ".json")) as &[u8], $h).expect(concat!(
"Chain spec ",
$e,
" is invalid."
@@ -147,6 +151,7 @@ impl Spec {
let root = BLAKE_NULL_RLP;
let (db, root) = self.initialize_accounts(trie_factory, db, root)?;
let (db, root) = self.initialize_shards(trie_factory, db, root)?;
+ let (db, root) = self.initialize_custom_actions(trie_factory, db, root)?;
*self.state_root_memo.write() = root;
Ok(db)
@@ -229,6 +234,24 @@ impl Spec {
Ok((db, root))
}
+ fn initialize_custom_actions(
+ &self,
+ trie_factory: &TrieFactory,
+ mut db: DB,
+ mut root: H256,
+ ) -> StateResult<(DB, H256)> {
+ // basic accounts in spec.
+ {
+ let mut t = trie_factory.from_existing(db.as_hashdb_mut(), &mut root)?;
+
+ for handler in &self.custom_handlers {
+ handler.init(t.as_mut())?;
+ }
+ }
+
+ Ok((db, root))
+ }
+
pub fn check_genesis_root(&self, db: &HashDB) -> bool {
if db.keys().is_empty() {
return true
@@ -273,43 +296,43 @@ impl Spec {
/// Loads spec from json file. Provide factories for executing contracts and ensuring
/// storage goes to the right place.
- pub fn load<'a, R>(reader: R) -> Result
+ pub fn load<'a, R>(reader: R, handlers: Vec>) -> Result
where
R: Read, {
- cjson::spec::Spec::load(reader).map_err(fmt_err).and_then(|x| load_from(x).map_err(fmt_err))
+ cjson::spec::Spec::load(reader).map_err(fmt_err).and_then(|x| load_from(x, handlers).map_err(fmt_err))
}
/// Create a new test Spec.
- pub fn new_test() -> Self {
- load_bundled!("null")
+ pub fn new_test(handlers: Vec>) -> Self {
+ load_bundled!("null", handlers)
}
/// Create a new Spec with Solo consensus which does internal sealing (not requiring
/// work).
- pub fn new_test_solo() -> Self {
- load_bundled!("solo")
+ pub fn new_test_solo(handlers: Vec>) -> Self {
+ load_bundled!("solo", handlers)
}
/// Create a new Spec with SoloAuthority consensus which does internal sealing (not requiring
/// work).
- pub fn new_test_solo_authority() -> Self {
- load_bundled!("solo_authority")
+ pub fn new_test_solo_authority(handlers: Vec>) -> Self {
+ load_bundled!("solo_authority", handlers)
}
/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring
/// work).
- pub fn new_test_tendermint() -> Self {
- load_bundled!("tendermint")
+ pub fn new_test_tendermint(handlers: Vec>) -> Self {
+ load_bundled!("tendermint", handlers)
}
/// Create a new Spec with Cuckoo PoW consensus.
- pub fn new_test_cuckoo() -> Self {
- load_bundled!("cuckoo")
+ pub fn new_test_cuckoo(handlers: Vec>) -> Self {
+ load_bundled!("cuckoo", handlers)
}
/// Create a new Spec with Blake PoW consensus.
- pub fn new_test_blake_pow() -> Self {
- load_bundled!("blake_pow")
+ pub fn new_test_blake_pow(handlers: Vec>) -> Self {
+ load_bundled!("blake_pow", handlers)
}
/// Get common blockchain parameters.
@@ -350,7 +373,7 @@ impl Spec {
}
/// Load from JSON object.
-fn load_from(s: cjson::spec::Spec) -> Result {
+fn load_from(s: cjson::spec::Spec, handlers: Vec>) -> Result {
let g = Genesis::from(s.genesis);
let GenericSeal(seal_rlp) = g.seal.into();
let params = CommonParams::from(s.params);
@@ -371,13 +394,15 @@ fn load_from(s: cjson::spec::Spec) -> Result {
state_root_memo: RwLock::new(Default::default()), // will be overwritten right after.
genesis_accounts: s.accounts.into(),
genesis_shards: s.shards.into(),
+
+ custom_handlers: handlers,
};
// use memoized state root if provided.
match g.state_root {
Some(root) => *s.state_root_memo.get_mut() = root,
None => {
- let db = StateDB::new_with_memorydb(0);
+ let db = StateDB::new_with_memorydb(0, s.custom_handlers.clone());
let trie_factory = TrieFactory::new(Default::default());
let _ = s.initialize_state(&trie_factory, db)?;
}
@@ -394,7 +419,7 @@ mod tests {
#[test]
fn extra_data_of_genesis_header_is_hash_of_common_params() {
- let spec = Spec::new_test();
+ let spec = Spec::new_test(Vec::new());
let common_params = spec.params();
let hash_of_common_params = H256::blake(&common_params.rlp_bytes()).to_vec();
diff --git a/core/src/tests/helpers.rs b/core/src/tests/helpers.rs
index aa0228c32a..3228d5521d 100644
--- a/core/src/tests/helpers.rs
+++ b/core/src/tests/helpers.rs
@@ -48,7 +48,7 @@ pub fn get_good_dummy_block() -> Bytes {
pub fn get_good_dummy_block_hash() -> (H256, Bytes) {
let mut block_header = Header::new();
- let test_spec = Spec::new_test();
+ let test_spec = Spec::new_test(Vec::new());
block_header.set_score(U256::from(0x20000));
block_header.set_timestamp(40);
block_header.set_number(1);
@@ -58,5 +58,5 @@ pub fn get_good_dummy_block_hash() -> (H256, Bytes) {
}
pub fn get_temp_state_db() -> StateDB {
- StateDB::new_with_memorydb(5 * 1024 * 1024)
+ StateDB::new_with_memorydb(5 * 1024 * 1024, Vec::new())
}
diff --git a/core/src/verification/queue/mod.rs b/core/src/verification/queue/mod.rs
index c37c480d54..b2e7661ebb 100644
--- a/core/src/verification/queue/mod.rs
+++ b/core/src/verification/queue/mod.rs
@@ -514,7 +514,7 @@ mod tests {
// create a test block queue.
// auto_scaling enables verifier adjustment.
fn get_test_queue() -> BlockQueue {
- let spec = Spec::new_test();
+ let spec = Spec::new_test(Vec::new());
let engine = spec.engine;
let config = Config::default();
@@ -524,7 +524,7 @@ mod tests {
#[test]
fn can_be_created() {
// TODO better test
- let spec = Spec::new_test();
+ let spec = Spec::new_test(Vec::new());
let engine = spec.engine;
let config = Config::default();
diff --git a/rpc/src/v1/impls/chain.rs b/rpc/src/v1/impls/chain.rs
index 773c5bb518..d27804fa8a 100644
--- a/rpc/src/v1/impls/chain.rs
+++ b/rpc/src/v1/impls/chain.rs
@@ -18,15 +18,16 @@ use std::sync::Arc;
use ccore::{
AssetClient, BlockId, ExecuteClient, MinerService, MiningBlockChainClient, RegularKey, Shard, SignedParcel,
+ UnverifiedParcel,
};
use ckey::{Address, Public};
use cstate::{Asset, AssetScheme, AssetSchemeAddress};
use ctypes::invoice::{Invoice, ParcelInvoice};
-use ctypes::parcel::ChangeShard;
+use ctypes::parcel::{Action, ChangeShard};
use ctypes::transaction::Transaction;
use ctypes::{BlockNumber, ShardId};
use primitives::{H160, H256, U256};
-use rlp::UntrustedRlp;
+use rlp::{DecoderError, UntrustedRlp};
use jsonrpc_core::Result;
@@ -64,6 +65,17 @@ where
UntrustedRlp::new(&raw.into_vec())
.as_val()
.map_err(errors::rlp)
+ .and_then(|parcel: UnverifiedParcel| {
+ match &parcel.as_unsigned().action {
+ Action::Custom(bytes) => {
+ if !self.client.custom_handlers().iter().any(|c| c.is_target(bytes)) {
+ return Err(errors::rlp(DecoderError::Custom("Invalid custom action!")))
+ }
+ }
+ _ => {}
+ }
+ Ok(parcel)
+ })
.and_then(|parcel| SignedParcel::new(parcel).map_err(errors::parcel_core))
.and_then(|signed| {
let hash = signed.hash();
diff --git a/state/src/action_handler/hit.rs b/state/src/action_handler/hit.rs
new file mode 100644
index 0000000000..3eeb02a5a4
--- /dev/null
+++ b/state/src/action_handler/hit.rs
@@ -0,0 +1,86 @@
+// Copyright 2018 Kodebox, Inc.
+// This file is part of CodeChain.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program 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 Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use ccrypto::blake256;
+use ctypes::invoice::Invoice;
+use ctypes::parcel::Outcome;
+use primitives::{Bytes, H256};
+use rlp::{self, Decodable, DecoderError, Encodable, UntrustedRlp};
+use trie::TrieMut;
+
+use super::super::{StateResult, TopLevelState, TopState, TopStateInfo};
+use super::ActionHandler;
+
+const ACTION_ID: u8 = 0;
+
+pub struct HitAction {
+ increase: u8,
+}
+
+impl Decodable for HitAction {
+ fn decode(rlp: &UntrustedRlp) -> Result {
+ if rlp.item_count()? != 2 {
+ return Err(DecoderError::RlpIncorrectListLen)
+ }
+ if rlp.val_at::(0)? != ACTION_ID {
+ return Err(DecoderError::Custom("Unknown message id detected"))
+ }
+ Ok(Self {
+ increase: rlp.val_at(1)?,
+ })
+ }
+}
+
+#[derive(Clone)]
+pub struct HitHandler {}
+
+impl HitHandler {
+ pub fn new() -> Self {
+ Self {}
+ }
+
+ fn address(&self) -> H256 {
+ let mut hash: H256 = blake256(&b"metadata hit");
+ hash[0] = b'M';
+ hash
+ }
+}
+
+impl ActionHandler for HitHandler {
+ fn init(&self, state: &mut TrieMut) -> StateResult<()> {
+ let r = state.insert(&self.address(), &1u32.rlp_bytes());
+ debug_assert_eq!(Ok(None), r);
+ r?;
+ Ok(())
+ }
+
+ fn is_target(&self, bytes: &Bytes) -> bool {
+ HitAction::decode(&UntrustedRlp::new(bytes)).is_ok()
+ }
+
+ /// `bytes` must be valid encoding of HitAction
+ fn execute(&self, bytes: &Bytes, state: &mut TopLevelState) -> Option> {
+ HitAction::decode(&UntrustedRlp::new(bytes)).ok().map(|action| {
+ let prev_counter: u32 = rlp::decode(&state.action_data(&self.address())?);
+ let increase = action.increase as u32;
+ state.update_action_data(&self.address(), (prev_counter + increase).rlp_bytes().to_vec())?;
+ Ok(Outcome::Single {
+ invoice: Invoice::Success,
+ error: None,
+ })
+ })
+ }
+}
diff --git a/state/src/action_handler/mod.rs b/state/src/action_handler/mod.rs
new file mode 100644
index 0000000000..a2cc60d3ba
--- /dev/null
+++ b/state/src/action_handler/mod.rs
@@ -0,0 +1,31 @@
+// Copyright 2018 Kodebox, Inc.
+// This file is part of CodeChain.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program 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 Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+mod hit;
+
+use ctypes::parcel::Outcome;
+use primitives::Bytes;
+use trie::TrieMut;
+
+use super::{StateResult, TopLevelState};
+
+pub trait ActionHandler: Send + Sync {
+ fn init(&self, state: &mut TrieMut) -> StateResult<()>;
+ fn is_target(&self, bytes: &Bytes) -> bool;
+ fn execute(&self, bytes: &Bytes, state: &mut TopLevelState) -> Option>;
+}
+
+pub use self::hit::HitHandler;
diff --git a/state/src/backend.rs b/state/src/backend.rs
index 057f825686..a44f56a30d 100644
--- a/state/src/backend.rs
+++ b/state/src/backend.rs
@@ -37,11 +37,15 @@
//! should become general over time to the point where not even a
//! merkle trie is strictly necessary.
+use std::sync::Arc;
+
use ckey::Address;
use hashdb::HashDB;
+use primitives::{Bytes, H256};
use super::{
- Account, Asset, AssetAddress, AssetScheme, AssetSchemeAddress, Metadata, MetadataAddress, Shard, ShardAddress,
+ Account, ActionHandler, Asset, AssetAddress, AssetScheme, AssetSchemeAddress, Metadata, MetadataAddress, Shard,
+ ShardAddress,
};
@@ -59,12 +63,14 @@ pub trait TopBackend: Send {
fn add_to_account_cache(&mut self, addr: Address, data: Option, modified: bool);
fn add_to_metadata_cache(&mut self, address: MetadataAddress, item: Option, modified: bool);
fn add_to_shard_cache(&mut self, address: ShardAddress, item: Option, modified: bool);
+ fn add_to_action_data_cache(&mut self, address: H256, item: Option, modified: bool);
/// Get basic copy of the cached account. Not required to include storage.
/// Returns 'None' if cache is disabled or if the account is not cached.
fn get_cached_account(&self, addr: &Address) -> Option