raiden-state-machine 0.1.0

Raiden Network implementation in Rust
Documentation
#![warn(clippy::missing_docs_in_private_items)]

use raiden_primitives::types::{
	H256,
	U256,
	U64,
};

use super::channel;
use crate::{
	errors::StateTransitionError,
	types::{
		ContractReceiveChannelOpened,
		Event,
		Random,
		StateChange,
		TokenNetworkState,
	},
};

/// A transition result for the token network state machine.
type TransitionResult = std::result::Result<TokenNetworkTransition, StateTransitionError>;

/// Token network transition content.
#[derive(Debug)]
pub struct TokenNetworkTransition {
	pub new_state: TokenNetworkState,
	pub events: Vec<Event>,
}

/// Dispatch `state_change` to a channel by channei identifier.
fn subdispatch_to_channel_by_id(
	mut token_network_state: TokenNetworkState,
	channel_identifier: U256,
	state_change: StateChange,
	block_number: U64,
	block_hash: H256,
	pseudo_random_number_generator: &mut Random,
) -> TransitionResult {
	let channel_state =
		match token_network_state.channelidentifiers_to_channels.get(&channel_identifier) {
			Some(channel_state) => channel_state.clone(),
			None =>
				return Ok(TokenNetworkTransition { new_state: token_network_state, events: vec![] }),
		};

	let result = channel::state_transition(
		channel_state.clone(),
		state_change,
		block_number,
		block_hash,
		pseudo_random_number_generator,
	)?;
	match result.new_state {
		Some(channel_state) => {
			token_network_state
				.channelidentifiers_to_channels
				.insert(channel_identifier, channel_state);
		},
		None => {
			token_network_state.channelidentifiers_to_channels.remove(&channel_identifier);

			token_network_state
				.partneraddresses_to_channelidentifiers
				.remove(&channel_state.partner_state.address);
		},
	}

	Ok(TokenNetworkTransition { new_state: token_network_state, events: result.events })
}

/// Handle `ContractReceiveChannelOpened` state change.
fn handle_contract_receive_channel_opened(
	mut token_network_state: TokenNetworkState,
	state_change: ContractReceiveChannelOpened,
) -> TransitionResult {
	let channel_state = state_change.channel_state;
	let canonical_identifier = channel_state.canonical_identifier.clone();

	token_network_state
		.partneraddresses_to_channelidentifiers
		.entry(channel_state.partner_state.address)
		.or_insert(vec![]);
	if let Some(entry) = token_network_state
		.partneraddresses_to_channelidentifiers
		.get_mut(&channel_state.partner_state.address)
	{
		if !entry.contains(&canonical_identifier.channel_identifier) {
			entry.push(canonical_identifier.channel_identifier);
		}
	}

	token_network_state
		.channelidentifiers_to_channels
		.entry(canonical_identifier.channel_identifier)
		.or_insert(channel_state);

	Ok(TokenNetworkTransition { new_state: token_network_state, events: vec![] })
}

/// State machine for the token network.
pub fn state_transition(
	token_network_state: TokenNetworkState,
	state_change: StateChange,
	block_number: U64,
	block_hash: H256,
	pseudo_random_number_generator: &mut Random,
) -> TransitionResult {
	match state_change {
		StateChange::ActionChannelClose(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ActionChannelWithdraw(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ActionChannelCoopSettle(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ActionChannelSetRevealTimeout(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ContractReceiveChannelOpened(inner) =>
			handle_contract_receive_channel_opened(token_network_state, inner),
		StateChange::ContractReceiveChannelClosed(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ContractReceiveChannelDeposit(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ContractReceiveChannelWithdraw(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ContractReceiveChannelSettled(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ContractReceiveUpdateTransfer(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ContractReceiveChannelBatchUnlock(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ReceiveWithdrawRequest(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ReceiveWithdrawConfirmation(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		StateChange::ReceiveWithdrawExpired(ref inner) => {
			let channel_identifier = inner.canonical_identifier.channel_identifier;
			subdispatch_to_channel_by_id(
				token_network_state,
				channel_identifier,
				state_change,
				block_number,
				block_hash,
				pseudo_random_number_generator,
			)
		},
		_ => Err(StateTransitionError { msg: String::from("Could not transition token network") }),
	}
}