use exonum::{
crypto::KeyPair,
merkledb::ObjectHash,
runtime::{ExecutionContext, ExecutionError, InstanceId},
};
use exonum_derive::*;
use exonum_rust_runtime::{DefaultInstance, Service};
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
use exonum_testkit::{Spec, TestKit, TestKitBuilder};
#[exonum_interface(auto_ids)]
trait ErroneousInterface<Ctx> {
type Output;
fn generate_error(&self, context: Ctx, code: u8) -> Self::Output;
}
#[derive(Debug, Clone, Default, ServiceDispatcher, ServiceFactory)]
#[service_dispatcher(implements("ErroneousInterface"))]
#[service_factory(
artifact_name = "erroneous-service",
service_constructor = "Self::new_instance"
)]
struct ErroneousService(Arc<AtomicU64>);
impl ErroneousService {
fn new_instance(&self) -> Box<dyn Service> {
Box::new(self.clone())
}
}
impl ErroneousInterface<ExecutionContext<'_>> for ErroneousService {
type Output = Result<(), ExecutionError>;
fn generate_error(&self, _context: ExecutionContext<'_>, code: u8) -> Self::Output {
let count = self.0.fetch_add(1, Ordering::SeqCst);
let description = format!("Error was generated {} times", count + 1);
Err(ExecutionError::service(code, description))
}
}
impl Service for ErroneousService {}
impl DefaultInstance for ErroneousService {
const INSTANCE_ID: InstanceId = 100;
const INSTANCE_NAME: &'static str = "erroneous";
}
fn init_testkit() -> TestKit {
let service = ErroneousService::default();
TestKitBuilder::validator()
.with(Spec::new(service).with_default_instance())
.build()
}
#[test]
fn diverging_error_descriptions() {
let mut testkit = init_testkit();
let keypair = KeyPair::random();
let tx = keypair.generate_error(ErroneousService::INSTANCE_ID, 1);
testkit.checkpoint();
let block = testkit.create_block_with_transaction(tx.clone());
assert_eq!(block.errors.len(), 1);
assert_eq!(
block.errors[0].error.description(),
"Error was generated 1 times"
);
let block = block.header;
testkit.rollback();
let new_block = testkit.create_block_with_transaction(tx);
assert_eq!(new_block.errors.len(), 1);
assert_eq!(
new_block.errors[0].error.description(),
"Error was generated 2 times"
);
let new_block = new_block.header;
assert_eq!(block.error_hash, new_block.error_hash);
assert_eq!(block.object_hash(), new_block.object_hash());
}
#[test]
fn diverging_error_codes() {
let mut testkit = init_testkit();
let keypair = KeyPair::random();
testkit.checkpoint();
let tx = keypair.generate_error(ErroneousService::INSTANCE_ID, 1);
let block = testkit.create_block_with_transaction(tx).header;
testkit.rollback();
let other_tx = keypair.generate_error(ErroneousService::INSTANCE_ID, 2);
let new_block = testkit.create_block_with_transaction(other_tx).header;
assert_ne!(block.error_hash, new_block.error_hash);
assert_ne!(block.object_hash(), new_block.object_hash());
}