b6d35f6faf
Updated 4763 files with dual copyright: - Parity Technologies (UK) Ltd. - Dijital Kurdistan Tech Institute
374 lines
9.2 KiB
Rust
374 lines
9.2 KiB
Rust
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
|
// see LICENSE for license details.
|
|
|
|
use crate::{
|
|
error::Error,
|
|
helpers::StreamOf,
|
|
transaction::{AccountMetadata, Transaction, TransactionStatus},
|
|
};
|
|
use futures::stream::{self};
|
|
use futures_util::StreamExt;
|
|
use pezkuwi_subxt::ext::codec::{Decode, Encode};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::{
|
|
any::Any,
|
|
fmt,
|
|
str::FromStr,
|
|
sync::atomic::{AtomicUsize, Ordering},
|
|
time::Duration,
|
|
};
|
|
use tokio::task::yield_now;
|
|
use tracing::trace;
|
|
|
|
pub(crate) const LOG_TARGET: &str = "fake_tx";
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
|
|
#[serde(try_from = "String", into = "String")]
|
|
pub(crate) struct FakeHash([u8; 4]);
|
|
|
|
impl AsRef<[u8]> for FakeHash {
|
|
fn as_ref(&self) -> &[u8] {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl From<[u8; 4]> for FakeHash {
|
|
fn from(value: [u8; 4]) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for FakeHash {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", hex::encode(self.0))
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for FakeHash {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", hex::encode(self.0))
|
|
}
|
|
}
|
|
|
|
impl FromStr for FakeHash {
|
|
type Err = String;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let mut bytes = [0; 4];
|
|
hex::decode_to_slice(s, &mut bytes).map_err(|_| "hex::decode failed".to_string())?;
|
|
Ok(FakeHash(bytes))
|
|
}
|
|
}
|
|
|
|
impl From<FakeHash> for String {
|
|
fn from(hash: FakeHash) -> Self {
|
|
hex::encode(hash.0)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<String> for FakeHash {
|
|
type Error = String;
|
|
|
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
FakeHash::from_str(&value)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct EventDef {
|
|
event: TransactionStatus<FakeHash>,
|
|
delay: u32,
|
|
}
|
|
|
|
impl EventDef {
|
|
pub fn validated(delay: u32) -> Self {
|
|
Self { event: TransactionStatus::Validated, delay }
|
|
}
|
|
pub fn broadcasted(delay: u32) -> Self {
|
|
Self {
|
|
//todo
|
|
event: TransactionStatus::Broadcasted,
|
|
delay,
|
|
}
|
|
}
|
|
pub fn in_block(h: u32, delay: u32) -> Self {
|
|
Self { event: TransactionStatus::InBlock(h.to_le_bytes().into()), delay }
|
|
}
|
|
pub fn finalized(h: u32, delay: u32) -> Self {
|
|
Self { event: TransactionStatus::Finalized(h.to_le_bytes().into()), delay }
|
|
}
|
|
pub fn dropped(delay: u32) -> Self {
|
|
Self { event: TransactionStatus::Dropped("xxx".to_string()), delay }
|
|
}
|
|
pub fn invalid(delay: u32) -> Self {
|
|
Self { event: TransactionStatus::Invalid("xxx".to_string()), delay }
|
|
}
|
|
pub fn error(delay: u32) -> Self {
|
|
Self { event: TransactionStatus::Error("xxx".to_string()), delay }
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct EventsStreamDef(pub Vec<EventDef>);
|
|
|
|
impl From<Vec<EventDef>> for EventsStreamDef {
|
|
fn from(value: Vec<EventDef>) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
pub(crate) struct FakeTransaction {
|
|
hash: FakeHash,
|
|
stream_def: Vec<EventsStreamDef>,
|
|
current_stream_def: AtomicUsize,
|
|
nonce: u128,
|
|
account_metadata: AccountMetadata,
|
|
}
|
|
|
|
impl Transaction for FakeTransaction {
|
|
type HashType = FakeHash;
|
|
fn hash(&self) -> Self::HashType {
|
|
self.hash
|
|
}
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
fn nonce(&self) -> u128 {
|
|
self.nonce
|
|
}
|
|
fn account_metadata(&self) -> AccountMetadata {
|
|
self.account_metadata.clone()
|
|
}
|
|
fn valid_until(&self) -> &Option<u64> {
|
|
&None
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl FakeTransaction {
|
|
pub(crate) fn get_current_stream_def(&self) -> EventsStreamDef {
|
|
self.stream_def[self.current_stream_def.load(Ordering::Relaxed)].clone()
|
|
}
|
|
|
|
pub(crate) fn new_multiple(index: u32, nonce: u128, stream_def: Vec<EventsStreamDef>) -> Self {
|
|
Self {
|
|
stream_def,
|
|
hash: index.to_le_bytes().into(),
|
|
current_stream_def: Default::default(),
|
|
nonce,
|
|
account_metadata: AccountMetadata::Derived(index),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn new_with_keyring(
|
|
account: String,
|
|
nonce: u128,
|
|
stream_def: Vec<EventsStreamDef>,
|
|
) -> Self {
|
|
let acc_as_bytes = account.as_bytes();
|
|
Self {
|
|
stream_def,
|
|
hash: [acc_as_bytes[0], acc_as_bytes[1], acc_as_bytes[2], acc_as_bytes[3]].into(),
|
|
current_stream_def: Default::default(),
|
|
nonce,
|
|
account_metadata: AccountMetadata::KeyRing(account),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn new(index: u32, nonce: u128, stream_def: EventsStreamDef) -> Self {
|
|
Self {
|
|
stream_def: vec![stream_def],
|
|
hash: index.to_le_bytes().into(),
|
|
current_stream_def: Default::default(),
|
|
nonce,
|
|
account_metadata: AccountMetadata::Derived(index),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn new_inblock_then_droppable_2nd_success(
|
|
hash: u32,
|
|
nonce: u128,
|
|
delay: u32,
|
|
) -> Self {
|
|
Self::new_multiple(
|
|
hash,
|
|
nonce,
|
|
vec![
|
|
EventsStreamDef(vec![
|
|
EventDef::broadcasted(delay),
|
|
EventDef::validated(delay),
|
|
EventDef::in_block(1, delay),
|
|
EventDef::in_block(2, delay),
|
|
EventDef::in_block(3, delay),
|
|
EventDef::dropped(delay),
|
|
]),
|
|
EventsStreamDef(vec![
|
|
EventDef::broadcasted(delay),
|
|
EventDef::validated(delay),
|
|
EventDef::in_block(1, delay),
|
|
EventDef::in_block(2, delay),
|
|
EventDef::in_block(3, delay),
|
|
EventDef::finalized(2, delay),
|
|
]),
|
|
],
|
|
)
|
|
}
|
|
|
|
pub(crate) fn new_droppable_2nd_success(hash: u32, nonce: u128, delay: u32) -> Self {
|
|
Self::new_multiple(
|
|
hash,
|
|
nonce,
|
|
vec![
|
|
EventsStreamDef(vec![EventDef::dropped(delay)]),
|
|
EventsStreamDef(vec![
|
|
EventDef::broadcasted(delay),
|
|
EventDef::validated(delay),
|
|
EventDef::in_block(1, delay),
|
|
EventDef::in_block(2, delay),
|
|
EventDef::in_block(3, delay),
|
|
EventDef::finalized(2, delay),
|
|
]),
|
|
],
|
|
)
|
|
}
|
|
|
|
pub(crate) fn new_droppable_loop(hash: u32, nonce: u128, delay: u32) -> Self {
|
|
Self::new_multiple(
|
|
hash,
|
|
nonce,
|
|
vec![
|
|
EventsStreamDef(vec![EventDef::dropped(delay)]),
|
|
EventsStreamDef(vec![EventDef::dropped(delay)]),
|
|
EventsStreamDef(vec![EventDef::dropped(delay)]),
|
|
EventsStreamDef(vec![EventDef::dropped(delay)]),
|
|
EventsStreamDef(vec![EventDef::dropped(delay)]),
|
|
],
|
|
)
|
|
}
|
|
|
|
pub(crate) fn new_droppable(hash: u32, nonce: u128, delay: u32) -> Self {
|
|
Self::new(hash, nonce, EventsStreamDef(vec![EventDef::dropped(delay)]))
|
|
}
|
|
|
|
pub(crate) fn new_invalid(hash: u32, nonce: u128, delay: u32) -> Self {
|
|
Self::new(hash, nonce, EventsStreamDef(vec![EventDef::invalid(delay)]))
|
|
}
|
|
|
|
pub(crate) fn new_error(hash: u32, nonce: u128, delay: u32) -> Self {
|
|
Self::new(hash, nonce, EventsStreamDef(vec![EventDef::error(delay)]))
|
|
}
|
|
|
|
pub(crate) fn new_finalizable_quick(hash: u32, nonce: u128) -> Self {
|
|
let delay = 0;
|
|
Self::new(
|
|
hash,
|
|
nonce,
|
|
EventsStreamDef(vec![
|
|
EventDef::broadcasted(delay),
|
|
EventDef::validated(delay),
|
|
EventDef::in_block(1, delay),
|
|
EventDef::in_block(2, delay),
|
|
EventDef::in_block(3, delay),
|
|
EventDef::finalized(2, delay),
|
|
]),
|
|
)
|
|
}
|
|
|
|
pub(crate) fn new_finalizable(hash: u32, nonce: u128) -> Self {
|
|
Self::new(
|
|
hash,
|
|
nonce,
|
|
EventsStreamDef(vec![
|
|
EventDef::broadcasted(100),
|
|
EventDef::validated(300),
|
|
EventDef::in_block(1, 1000),
|
|
EventDef::in_block(2, 1000),
|
|
EventDef::in_block(3, 1000),
|
|
EventDef::finalized(2, 2000),
|
|
]),
|
|
)
|
|
}
|
|
|
|
pub(crate) fn events(&self) -> StreamOf<TransactionStatus<FakeHash>> {
|
|
let def = self.get_current_stream_def();
|
|
stream::unfold(def.0.into_iter(), move |mut i| async {
|
|
yield_now().await;
|
|
if let Some(EventDef { event, delay }) = i.next() {
|
|
if delay > 0 {
|
|
tokio::time::sleep(Duration::from_millis(delay.into())).await;
|
|
}
|
|
trace!(target:LOG_TARGET, ?event, ?delay, "play");
|
|
Some((event, i))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.boxed()
|
|
}
|
|
|
|
pub(crate) async fn submit_result(&self) -> Result<FakeHash, Error> {
|
|
let EventDef { event, delay } = self
|
|
.get_current_stream_def()
|
|
.0
|
|
.pop()
|
|
.expect("there shall be at least event. qed.");
|
|
if delay > 0 {
|
|
tokio::time::sleep(Duration::from_millis(delay.into())).await;
|
|
}
|
|
trace!(target:LOG_TARGET, "submit_result: delayed: {:?}", self.hash);
|
|
match event {
|
|
TransactionStatus::Finalized(_) => Ok(self.hash),
|
|
TransactionStatus::Dropped(message) => {
|
|
Err(Error::Other(format!("submit-error:dropped:{message}").to_string()))
|
|
},
|
|
TransactionStatus::Invalid(message) => {
|
|
Err(Error::Other(format!("submit-error:invalid:{message}").to_string()))
|
|
},
|
|
TransactionStatus::Error(message) => {
|
|
Err(Error::Other(format!("submit-error:error:{message}").to_string()))
|
|
},
|
|
TransactionStatus::Validated
|
|
| TransactionStatus::NoLongerInBestBlock
|
|
| TransactionStatus::Broadcasted
|
|
| TransactionStatus::InBlock(_) => todo!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use crate::init_logger;
|
|
|
|
#[tokio::test]
|
|
async fn fake_transaction_events() {
|
|
init_logger();
|
|
let t = FakeTransaction::new(
|
|
1,
|
|
0,
|
|
EventsStreamDef(vec![
|
|
EventDef::broadcasted(100),
|
|
EventDef::validated(300),
|
|
EventDef::in_block(1, 1000),
|
|
EventDef::in_block(2, 1000),
|
|
EventDef::in_block(3, 1000),
|
|
EventDef::finalized(2, 2000),
|
|
]),
|
|
);
|
|
let v = t.events().collect::<Vec<_>>().await;
|
|
assert_eq!(
|
|
v,
|
|
vec![
|
|
TransactionStatus::Broadcasted,
|
|
TransactionStatus::Validated,
|
|
TransactionStatus::InBlock(1u32.to_le_bytes().into()),
|
|
TransactionStatus::InBlock(2u32.to_le_bytes().into()),
|
|
TransactionStatus::InBlock(3u32.to_le_bytes().into()),
|
|
TransactionStatus::Finalized(2u32.to_le_bytes().into())
|
|
]
|
|
);
|
|
tracing::info!("{v:?}")
|
|
}
|
|
}
|