use std::{
cell::{Ref, RefMut},
collections::BTreeMap,
};
use crate::{GAS_MULTIPLIER, state::WithOverlay};
use gear_common::{
Gas, GasMultiplier, LockId, Origin,
gas_provider::{
ConsumeResultOf, GasNodeId, LockableTree, Provider, ReservableTree, Tree,
auxiliary::{
AuxiliaryGasProvider, GasNodesProvider, GasNodesStorage, GasTreeError, Node, NodeId,
PlainNodeId, TotalIssuanceProvider, TotalIssuanceStorage,
},
},
};
use gear_core::ids::{ActorId, MessageId, ReservationId};
pub(crate) type PositiveImbalance = <GasTree as Tree>::PositiveImbalance;
pub(crate) type NegativeImbalance = <GasTree as Tree>::NegativeImbalance;
pub type OriginNodeDataOf = (
<GasTree as Tree>::ExternalOrigin,
GasMultiplier<<GasTree as Tree>::Funds, <GasTree as Tree>::Balance>,
<GasTree as Tree>::NodeId,
);
type GtestGasProvider = AuxiliaryGasProvider<
GTestTotalIssuanceStorage,
GTestTotalIssuanceProvider,
GTestGasNodesStorage,
GTestGasNodesProvider,
>;
type GasTree = <GtestGasProvider as Provider>::GasTree;
std::thread_local! {
pub(super) static GTEST_TOTAL_ISSUANCE: GTestTotalIssuanceProvider = Default::default();
pub(super) static GTEST_GAS_NODES: GTestGasNodesProvider = Default::default();
}
pub struct GTestTotalIssuanceStorage;
pub type GTestTotalIssuanceProvider = WithOverlay<Option<Gas>>;
impl TotalIssuanceStorage<GTestTotalIssuanceProvider> for GTestTotalIssuanceStorage {
fn storage() -> &'static std::thread::LocalKey<GTestTotalIssuanceProvider> {
>EST_TOTAL_ISSUANCE
}
}
impl TotalIssuanceProvider for GTestTotalIssuanceProvider {
fn data(&self) -> Ref<'_, Option<Gas>> {
self.data()
}
fn data_mut(&self) -> RefMut<'_, Option<Gas>> {
self.data_mut()
}
}
pub struct GTestGasNodesStorage;
pub type GTestGasNodesProvider = WithOverlay<BTreeMap<NodeId, Node>>;
impl GasNodesStorage<GTestGasNodesProvider> for GTestGasNodesStorage {
fn storage() -> &'static std::thread::LocalKey<GTestGasNodesProvider> {
>EST_GAS_NODES
}
}
impl GasNodesProvider for GTestGasNodesProvider {
fn data(&self) -> Ref<'_, BTreeMap<NodeId, Node>> {
self.data()
}
fn data_mut(&self) -> RefMut<'_, BTreeMap<NodeId, Node>> {
self.data_mut()
}
}
#[derive(Debug, Default)]
pub(crate) struct GasTreeManager;
impl GasTreeManager {
pub(crate) fn create(
&self,
origin: ActorId,
mid: MessageId,
amount: Gas,
is_reply: bool,
) -> Result<PositiveImbalance, GasTreeError> {
if !is_reply || !self.exists_and_deposit(mid) {
GasTree::create(
origin.cast(),
GAS_MULTIPLIER,
GasNodeId::from(mid.cast::<PlainNodeId>()),
amount,
)
} else {
Ok(PositiveImbalance::new(0))
}
}
pub(crate) fn create_deposit(
&self,
original_mid: MessageId,
future_reply_id: MessageId,
amount: Gas,
) -> Result<(), GasTreeError> {
GasTree::create_deposit(
GasNodeId::from(original_mid.cast::<PlainNodeId>()),
GasNodeId::from(future_reply_id.cast::<PlainNodeId>()),
amount,
)
}
pub(crate) fn split_with_value(
&self,
is_reply: bool,
original_mid: impl Origin,
new_mid: MessageId,
amount: Gas,
) -> Result<(), GasTreeError> {
if !is_reply || !GasTree::exists_and_deposit(GasNodeId::from(new_mid.cast::<PlainNodeId>()))
{
return GasTree::split_with_value(
GasNodeId::from(original_mid.cast::<PlainNodeId>()),
GasNodeId::from(new_mid.cast::<PlainNodeId>()),
amount,
);
}
Ok(())
}
pub(crate) fn split(
&self,
is_reply: bool,
original_node: impl Origin,
new_mid: MessageId,
) -> Result<(), GasTreeError> {
if !is_reply || !GasTree::exists_and_deposit(GasNodeId::from(new_mid.cast::<PlainNodeId>()))
{
return GasTree::split(
GasNodeId::from(original_node.cast::<PlainNodeId>()),
GasNodeId::from(new_mid.cast::<PlainNodeId>()),
);
}
Ok(())
}
pub(crate) fn cut(
&self,
original_node: impl Origin,
new_mid: MessageId,
amount: Gas,
) -> Result<(), GasTreeError> {
GasTree::cut(
GasNodeId::from(original_node.cast::<PlainNodeId>()),
GasNodeId::from(new_mid.cast::<PlainNodeId>()),
amount,
)
}
pub(crate) fn get_limit(&self, mid: impl Origin) -> Result<Gas, GasTreeError> {
GasTree::get_limit(GasNodeId::from(mid.cast::<PlainNodeId>()))
}
pub(crate) fn spend(
&self,
mid: MessageId,
amount: Gas,
) -> Result<NegativeImbalance, GasTreeError> {
GasTree::spend(GasNodeId::from(mid.cast::<PlainNodeId>()), amount)
}
pub(crate) fn consume(&self, mid: impl Origin) -> ConsumeResultOf<GasTree> {
GasTree::consume(GasNodeId::from(mid.cast::<PlainNodeId>()))
}
pub(crate) fn reserve_gas(
&self,
original_mid: MessageId,
reservation_id: ReservationId,
amount: Gas,
) -> Result<(), GasTreeError> {
GasTree::reserve(
GasNodeId::from(original_mid.cast::<PlainNodeId>()),
GasNodeId::from(reservation_id.cast::<PlainNodeId>()),
amount,
)
}
#[cfg(test)]
pub(crate) fn exists(&self, node_id: impl Origin) -> bool {
GasTree::exists(GasNodeId::from(node_id.cast::<PlainNodeId>()))
}
pub(crate) fn exists_and_deposit(&self, node_id: impl Origin) -> bool {
GasTree::exists_and_deposit(GasNodeId::from(node_id.cast::<PlainNodeId>()))
}
pub(crate) fn clear(&self) {
<GtestGasProvider as Provider>::reset();
}
pub(crate) fn system_unreserve(&self, message_id: MessageId) -> Result<Gas, GasTreeError> {
GasTree::system_unreserve(GasNodeId::from(message_id.cast::<PlainNodeId>()))
}
pub(crate) fn system_reserve(
&self,
message_id: MessageId,
amount: Gas,
) -> Result<(), GasTreeError> {
GasTree::system_reserve(GasNodeId::from(message_id.cast::<PlainNodeId>()), amount)
}
pub fn lock(&self, node_id: impl Origin, id: LockId, amount: Gas) -> Result<(), GasTreeError> {
GasTree::lock(GasNodeId::from(node_id.cast::<PlainNodeId>()), id, amount)
}
pub(crate) fn unlock_all(
&self,
message_id: impl Origin,
id: LockId,
) -> Result<Gas, GasTreeError> {
GasTree::unlock_all(GasNodeId::from(message_id.cast::<PlainNodeId>()), id)
}
pub(crate) fn get_origin_node(
&self,
message_id: MessageId,
) -> Result<OriginNodeDataOf, GasTreeError> {
GasTree::get_origin_node(GasNodeId::from(message_id.cast::<PlainNodeId>()))
}
}