raiden-blockchain 0.1.0

Raiden Network implementation in Rust
Documentation
use std::{
	ops::Mul,
	sync::Arc,
};

use raiden_primitives::types::{
	Address,
	U256,
};
use raiden_state_machine::{
	types::ChainState,
	views,
};
use web3::Transport;

use super::{
	common::{
		Account,
		Result,
	},
	ProxyManager,
};
use crate::{
	constants::{
		GAS_RESERVE_ESTIMATE_SECURITY_FACTOR,
		UNLOCK_TX_GAS_LIMIT,
	},
	contracts::GasMetadata,
};

const GAS_REQUIRED_FOR_CHANNEL_LIFECYCLE_AFTER_SETTLE: u64 = UNLOCK_TX_GAS_LIMIT;

pub struct GasReserve {
	proxy_manager: Arc<ProxyManager>,
	registry_address: Address,
	gas_metadata: Arc<GasMetadata>,
}

impl GasReserve {
	pub fn new(proxy_manager: Arc<ProxyManager>, registry_address: Address) -> Self {
		Self { gas_metadata: proxy_manager.gas_metadata(), proxy_manager, registry_address }
	}
	fn gas_required_for_channel_lifecycle_after_close(&self) -> u64 {
		self.gas_metadata.get("TokenNetwork.settleChannel") +
			GAS_REQUIRED_FOR_CHANNEL_LIFECYCLE_AFTER_SETTLE
	}

	fn gas_required_for_channel_lifecycle_after_open(&self) -> u64 {
		self.gas_metadata.get("TokenNetwork.closeChannel") +
			self.gas_required_for_channel_lifecycle_after_close()
	}

	fn gas_required_for_channel_lifecycle_complete(&self) -> u64 {
		self.gas_metadata.get("TokenNetwork.openChannel") +
			self.gas_metadata.get("TokenNetwork.setTotalDeposit") +
			self.gas_required_for_channel_lifecycle_after_open()
	}

	fn calc_required_gas_estimate(
		&self,
		opening_channels: u64,
		opened_channels: u64,
		closing_channels: u64,
		closed_channels: u64,
		settling_channels: u64,
		settled_channels: u64,
	) -> u64 {
		let mut estimate = 0;

		estimate += opening_channels * self.gas_required_for_channel_lifecycle_complete();
		estimate += opened_channels * self.gas_required_for_channel_lifecycle_after_open();
		estimate += closing_channels * self.gas_required_for_channel_lifecycle_after_close();
		estimate += closed_channels * self.gas_required_for_channel_lifecycle_after_close();
		estimate += settling_channels * GAS_REQUIRED_FOR_CHANNEL_LIFECYCLE_AFTER_SETTLE;
		estimate += settled_channels * GAS_REQUIRED_FOR_CHANNEL_LIFECYCLE_AFTER_SETTLE;

		estimate
	}

	async fn get_required_gas_estimate(
		&self,
		chain_state: &ChainState,
		channels_to_open: u64,
	) -> Result<u64> {
		let mut num_opened_channels = 0;
		let mut num_closing_channels = 0;
		let mut num_closed_channels = 0;
		let mut num_settling_channels = 0;
		let mut num_settled_channels = 0;

		let num_opening_channels = self
			.proxy_manager
			.token_networks
			.read()
			.await
			.values()
			.fold(0u64, |sum, tn| sum + tn.opening_channels_count as u64);

		let token_addresses = views::get_token_identifiers(chain_state, self.registry_address);

		for token_address in token_addresses {
			num_opened_channels +=
				views::get_channelstate_open(chain_state, self.registry_address, token_address)
					.len() as u64;
			num_closing_channels +=
				views::get_channelstate_closing(chain_state, self.registry_address, token_address)
					.len() as u64;
			num_closed_channels +=
				views::get_channelstate_closed(chain_state, self.registry_address, token_address)
					.len() as u64;
			num_settling_channels +=
				views::get_channelstate_settling(chain_state, self.registry_address, token_address)
					.len() as u64;
			num_settled_channels +=
				views::get_channelstate_settled(chain_state, self.registry_address, token_address)
					.len() as u64;
		}

		Ok(self.calc_required_gas_estimate(
			num_opening_channels + channels_to_open,
			num_opened_channels,
			num_closing_channels,
			num_closed_channels,
			num_settling_channels,
			num_settled_channels,
		))
	}

	pub async fn get_estimate(
		&self,
		chain_state: &ChainState,
		channels_to_open: u64,
	) -> Result<U256> {
		let gas_estimate = self.get_required_gas_estimate(chain_state, channels_to_open).await?;
		let gas_price = self.proxy_manager.web3().eth().gas_price().await?;

		let reserve_amount: U256 = gas_price.mul(gas_estimate);

		Ok(reserve_amount.mul((100.0 * GAS_RESERVE_ESTIMATE_SECURITY_FACTOR).round() as u32 / 100))
	}

	pub async fn has_enough<T: Transport>(
		&self,
		account: Account<T>,
		chain_state: &ChainState,
		channels_to_open: u64,
	) -> Result<(bool, U256)> {
		let gas_reserve_estimate = self.get_estimate(chain_state, channels_to_open).await?;
		let balance = self.proxy_manager.web3().eth().balance(account.address(), None).await?;

		Ok((gas_reserve_estimate < balance, gas_reserve_estimate))
	}
}