pub mod events_endpoint;
#[cfg(test)]
pub mod event_log {
use ethbridge_bridge_events::{
TransferToChainFilter, TransferToErcFilter, ValidatorSetUpdateFilter,
};
use namada_sdk::eth_bridge::ethers::abi::AbiEncode;
use namada_sdk::eth_bridge::ethers::contract::EthEvent;
pub trait GetLog {
fn get_log(self) -> ethabi::RawLog;
}
impl GetLog for TransferToChainFilter {
fn get_log(self) -> ethabi::RawLog {
ethabi::RawLog {
topics: vec![Self::signature()],
data: self.encode(),
}
}
}
impl GetLog for TransferToErcFilter {
fn get_log(self) -> ethabi::RawLog {
ethabi::RawLog {
topics: vec![Self::signature(), {
let mut buf = [0; 32];
self.nonce.to_big_endian(&mut buf);
ethabi::ethereum_types::H256(buf)
}],
data: (self.transfers, self.relayer_address).encode(),
}
}
}
impl GetLog for ValidatorSetUpdateFilter {
fn get_log(self) -> ethabi::RawLog {
ethabi::RawLog {
topics: vec![Self::signature(), {
let mut buf = [0; 32];
self.validator_set_nonce.to_big_endian(&mut buf);
ethabi::ethereum_types::H256(buf)
}],
data: (
self.bridge_validator_set_hash,
self.governance_validator_set_hash,
)
.encode(),
}
}
}
}
#[cfg(any(test, feature = "testing"))]
pub mod mock_web3_client {
use std::borrow::Cow;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use ethabi::Address;
use ethbridge_events::EventCodec;
use namada_sdk::control_flow::time::{Duration, Instant};
use namada_sdk::ethereum_structs::BlockHeight;
use num256::Uint256;
use tokio::sync::mpsc::{
UnboundedReceiver, UnboundedSender, unbounded_channel,
};
use tokio::sync::oneshot::Sender;
use super::super::super::ethereum_oracle::{Error, Oracle, RpcClient};
use crate::ethereum_oracle::SyncStatus;
pub type TestOracle = Oracle<Web3Client>;
#[derive(Debug)]
pub enum TestCmd {
Normal,
Unresponsive,
NewHeight(Uint256),
NewEvent {
event_type: MockEventType,
log: ethabi::RawLog,
height: u32,
seen: Sender<()>,
},
}
pub type MockEventType = Cow<'static, str>;
pub struct Web3Client(Arc<Mutex<Web3ClientInner>>);
pub struct Web3Controller(Arc<Mutex<Web3ClientInner>>);
impl Web3Controller {
pub fn apply_cmd(&self, cmd: TestCmd) {
let mut oracle = self.0.lock().unwrap();
match cmd {
TestCmd::Normal => oracle.active = true,
TestCmd::Unresponsive => oracle.active = false,
TestCmd::NewHeight(height) => {
oracle.latest_block_height = height
}
TestCmd::NewEvent {
event_type: ty,
log,
height,
seen,
} => oracle.events.push((ty, log, height, seen)),
}
}
}
impl Clone for Web3Controller {
#[inline]
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
pub struct Web3ClientInner {
active: bool,
latest_block_height: Uint256,
events: Vec<(MockEventType, ethabi::RawLog, u32, Sender<()>)>,
blocks_processed: UnboundedSender<Uint256>,
last_block_processed: Option<Uint256>,
}
#[async_trait(?Send)]
impl RpcClient for Web3Client {
type Log = ethabi::RawLog;
#[cold]
fn new_client(_: &str) -> Self
where
Self: Sized,
{
panic!(
"Method is here for api completeness. It is not meant to be \
used in tests."
)
}
async fn check_events_in_block(
&self,
block: BlockHeight,
addr: Address,
ty: &str,
) -> Result<Vec<Self::Log>, Error> {
let block_to_check: Uint256 = block.into();
let mut client = self.0.lock().unwrap();
if client.active {
let mut logs = vec![];
let mut events = vec![];
std::mem::swap(&mut client.events, &mut events);
for (event_ty, log, height, seen) in events.into_iter() {
if event_ty == ty && block_to_check >= Uint256::from(height)
{
seen.send(()).unwrap();
logs.push(log);
} else {
client.events.push((event_ty, log, height, seen));
}
}
if client.last_block_processed.as_ref() < Some(&block_to_check)
{
_ = client.blocks_processed.send(block_to_check);
client.last_block_processed = Some(block_to_check);
}
Ok(logs)
} else {
tracing::debug!(
"No events to be processed by the Test Ethereum oracle, \
as it has been artificially set as unresponsive"
);
Err(Error::CheckEvents(
ty.into(),
addr,
"Test oracle is not responding".into(),
))
}
}
async fn syncing(
&self,
_: Option<&BlockHeight>,
_: Duration,
_: Instant,
) -> Result<SyncStatus, Error> {
let height = self.0.lock().unwrap().latest_block_height;
Ok(SyncStatus::AtHeight(height))
}
#[inline(always)]
fn may_recover(&self, _: &Error) -> bool {
true
}
}
impl Web3Client {
pub fn setup() -> (UnboundedReceiver<Uint256>, Self) {
let (block_processed_send, block_processed_recv) =
unbounded_channel();
(
block_processed_recv,
Self(Arc::new(Mutex::new(Web3ClientInner {
active: true,
latest_block_height: Default::default(),
events: vec![],
blocks_processed: block_processed_send,
last_block_processed: None,
}))),
)
}
pub fn controller(&self) -> Web3Controller {
Web3Controller(Arc::clone(&self.0))
}
}
pub fn event_signature<C>() -> Cow<'static, str>
where
PhantomData<C>: EventCodec,
{
PhantomData::<C>.event_signature()
}
}