raiden_state_machine/machine/
target.rs

1#![warn(clippy::missing_docs_in_private_items)]
2
3use raiden_primitives::{
4	constants::CANONICAL_IDENTIFIER_UNORDERED_QUEUE,
5	types::{
6		BlockHash,
7		BlockNumber,
8	},
9};
10
11use super::{
12	channel,
13	mediator,
14	secret_registry,
15	utils::{
16		self,
17		update_channel,
18	},
19};
20use crate::{
21	errors::StateTransitionError,
22	types::{
23		ActionInitTarget,
24		Block,
25		ChainState,
26		ChannelState,
27		ContractReceiveSecretReveal,
28		ErrorUnlockClaimFailed,
29		Event,
30		PaymentReceivedSuccess,
31		ReceiveLockExpired,
32		ReceiveSecretReveal,
33		ReceiveUnlock,
34		SendMessageEventInner,
35		SendSecretRequest,
36		SendSecretReveal,
37		StateChange,
38		TargetState,
39		TargetTransferState,
40	},
41	views,
42};
43
44/// A transition result for the initiator state.
45pub(super) type TransitionResult = std::result::Result<TargetTransition, StateTransitionError>;
46
47/// Target transition content.
48#[derive(Debug)]
49pub struct TargetTransition {
50	pub new_state: Option<TargetTransferState>,
51	pub chain_state: ChainState,
52	pub events: Vec<Event>,
53}
54
55/// Emits the event for revealing the secret on-chain if the transfer
56/// can not be settled off-chain.
57fn events_for_onchain_secretreveal(
58	target_state: &mut TargetTransferState,
59	channel_state: &ChannelState,
60	block_number: BlockNumber,
61	block_hash: BlockHash,
62) -> Result<Vec<Event>, String> {
63	let transfer = &target_state.transfer;
64	let expiration = transfer.lock.expiration;
65
66	let safe_to_wait =
67		mediator::is_safe_to_wait(expiration, channel_state.reveal_timeout, block_number).is_ok();
68	let secret_known_offchain =
69		channel_state.partner_state.is_secret_known_offchain(transfer.lock.secrethash);
70	let has_onchain_reveal_started = target_state.state == TargetState::OnchainSecretReveal;
71
72	if !safe_to_wait && secret_known_offchain && !has_onchain_reveal_started {
73		target_state.state = TargetState::OnchainSecretReveal;
74		let secret = match channel_state.partner_state.get_secret(transfer.lock.secrethash) {
75			Some(secret) => secret,
76			None => return Err("Secret should be known at this point".to_owned()),
77		};
78
79		return Ok(secret_registry::events_for_onchain_secretreveal(
80			channel_state,
81			secret,
82			expiration,
83			block_hash,
84		))
85	}
86
87	Ok(vec![])
88}
89
90/// Handles an ActionInitTarget state change.
91fn handle_init_target(
92	mut chain_state: ChainState,
93	target_state: Option<TargetTransferState>,
94	state_change: ActionInitTarget,
95) -> TransitionResult {
96	if target_state.is_some() {
97		// Target state should be None
98		return Ok(TargetTransition { new_state: target_state, chain_state, events: vec![] })
99	}
100
101	let transfer = &state_change.transfer;
102	let from_hop = state_change.from_hop;
103
104	let mut channel_state = match views::get_channel_by_canonical_identifier(
105		&chain_state,
106		transfer.balance_proof.canonical_identifier.clone(),
107	) {
108		Some(channel_state) => channel_state.clone(),
109		None => return Ok(TargetTransition { new_state: None, chain_state, events: vec![] }),
110	};
111
112	let sender = match transfer.balance_proof.sender {
113		Some(sender) => sender,
114		None => return Err("Transfer sender should be set".to_owned().into()),
115	};
116	let handle_locked_transfer = channel::handle_receive_locked_transfer(
117		&mut channel_state,
118		transfer.clone(),
119		views::get_address_metadata(sender, transfer.route_states.clone()),
120	);
121	let reveal_timeout = channel_state.reveal_timeout;
122	update_channel(&mut chain_state, channel_state).map_err(Into::into)?;
123
124	let mut events = vec![];
125	let target_state = match handle_locked_transfer {
126		Ok(channel_event) => {
127			// A valid balance proof does not mean the payment itself is still valid.
128			// e.g. the lock may be near expiration or have expired. This is fine. The
129			// message with an unusable lock must be handled to properly synchronize the
130			// local view of the partner's channel state, allowing the next balance
131			// proofs to be handled. This however, must only be done once, which is
132			// enforced by the nonce increasing sequentially, which is verified by
133			// the handler handle_receive_lockedtransfer.
134			let target_state = TargetTransferState {
135				from_hop,
136				transfer: transfer.clone(),
137				secret: None,
138				state: TargetState::SecretRequest,
139				initiator_address_metadata: None,
140			};
141			events.push(channel_event);
142
143			if state_change.received_valid_secret {
144				return Ok(TargetTransition { new_state: Some(target_state), chain_state, events })
145			}
146
147			let safe_to_wait = mediator::is_safe_to_wait(
148				transfer.lock.expiration,
149				reveal_timeout,
150				chain_state.block_number,
151			)
152			.is_ok();
153			if safe_to_wait {
154				let message_identifier = chain_state.pseudo_random_number_generator.next();
155				let recipient = transfer.initiator;
156				let secret_request = SendSecretRequest {
157					inner: SendMessageEventInner {
158						recipient,
159						recipient_metadata: views::get_address_metadata(
160							recipient,
161							transfer.route_states.clone(),
162						),
163						canonical_identifier: CANONICAL_IDENTIFIER_UNORDERED_QUEUE,
164						message_identifier,
165					},
166					payment_identifier: transfer.payment_identifier,
167					amount: transfer.lock.amount,
168					expiration: transfer.lock.expiration,
169					secrethash: transfer.lock.secrethash,
170				};
171				events.push(secret_request.into());
172			}
173			Some(target_state)
174		},
175		Err((e, err_events)) => {
176			let unlock_failed = ErrorUnlockClaimFailed {
177				identifier: transfer.payment_identifier,
178				secrethash: transfer.lock.secrethash,
179				reason: e,
180			};
181			events.push(unlock_failed.into());
182			events.extend(err_events);
183			None
184		},
185	};
186
187	Ok(TargetTransition { new_state: target_state, chain_state, events })
188}
189
190/// After Raiden learns about a new block this function must be called to
191/// handle expiration of the hash time lock.
192fn handle_block(
193	chain_state: ChainState,
194	target_state: Option<TargetTransferState>,
195	state_change: Block,
196) -> TransitionResult {
197	let mut target_state = match target_state {
198		Some(target_state) => target_state,
199		None => return Err("Block should be accompanied by a valid target state".to_owned().into()),
200	};
201
202	let mut events = vec![];
203
204	let transfer = &target_state.transfer;
205	let lock = &transfer.lock;
206
207	let channel_state = match views::get_channel_by_canonical_identifier(
208		&chain_state,
209		transfer.balance_proof.canonical_identifier.clone(),
210	) {
211		Some(channel_state) => channel_state,
212		None =>
213			return Ok(TargetTransition {
214				new_state: Some(target_state),
215				chain_state,
216				events: vec![],
217			}),
218	};
219
220	let secret_known = channel_state.partner_state.is_secret_known(lock.secrethash);
221	let lock_has_expired = channel::validators::is_lock_expired(
222		&channel_state.our_state,
223		lock,
224		chain_state.block_number,
225		channel::views::get_receiver_expiration_threshold(lock.expiration),
226	)
227	.is_ok();
228
229	if lock_has_expired && target_state.state != TargetState::Expired {
230		target_state.state = TargetState::Expired;
231		events.push(
232			ErrorUnlockClaimFailed {
233				identifier: transfer.payment_identifier,
234				secrethash: transfer.lock.secrethash,
235				reason: "Lock expired".to_owned(),
236			}
237			.into(),
238		);
239	} else if secret_known {
240		events.extend(
241			events_for_onchain_secretreveal(
242				&mut target_state,
243				channel_state,
244				state_change.block_number,
245				state_change.block_hash,
246			)
247			.map_err(Into::into)?,
248		);
249	}
250
251	Ok(TargetTransition { new_state: Some(target_state), chain_state, events })
252}
253
254/// Validates and handles a ReceiveSecretReveal state change.
255fn handle_offchain_secret_reveal(
256	mut chain_state: ChainState,
257	target_state: Option<TargetTransferState>,
258	state_change: ReceiveSecretReveal,
259) -> TransitionResult {
260	let mut target_state = match target_state {
261		Some(target_state) => target_state,
262		None => return Err("Block should be accompanied by a valid target state".to_owned().into()),
263	};
264
265	let mut events = vec![];
266
267	let transfer = &target_state.transfer;
268
269	let mut channel_state = match views::get_channel_by_canonical_identifier(
270		&chain_state,
271		transfer.balance_proof.canonical_identifier.clone(),
272	) {
273		Some(channel_state) => channel_state.clone(),
274		None =>
275			return Ok(TargetTransition {
276				new_state: Some(target_state),
277				chain_state,
278				events: vec![],
279			}),
280	};
281
282	let valid_secret = utils::is_valid_secret_reveal(&state_change, transfer.lock.secrethash);
283	let has_transfer_expired = channel::validators::is_transfer_expired(
284		transfer,
285		&channel_state,
286		chain_state.block_number,
287	);
288
289	if valid_secret && !has_transfer_expired {
290		channel::register_offchain_secret(
291			&mut channel_state,
292			state_change.secret.clone(),
293			state_change.secrethash,
294		);
295		update_channel(&mut chain_state, channel_state).map_err(Into::into)?;
296
297		let from_hop = &target_state.from_hop;
298		let message_identifier = chain_state.pseudo_random_number_generator.next();
299		target_state.state = TargetState::OffchainSecretReveal;
300		target_state.secret = Some(state_change.secret.clone());
301		let recipient = from_hop.node_address;
302
303		let reveal = SendSecretReveal {
304			inner: SendMessageEventInner {
305				recipient,
306				recipient_metadata: views::get_address_metadata(
307					recipient,
308					transfer.route_states.clone(),
309				),
310				canonical_identifier: CANONICAL_IDENTIFIER_UNORDERED_QUEUE,
311				message_identifier,
312			},
313			secret: state_change.secret,
314			secrethash: state_change.secrethash,
315		};
316
317		events.push(reveal.into());
318	}
319
320	Ok(TargetTransition { new_state: Some(target_state), chain_state, events })
321}
322
323/// Validates and handles a `ContractReceiveSecretReveal` state change.
324fn handle_onchain_secret_reveal(
325	mut chain_state: ChainState,
326	target_state: Option<TargetTransferState>,
327	state_change: ContractReceiveSecretReveal,
328) -> TransitionResult {
329	let mut target_state = match target_state {
330		Some(target_state) => target_state,
331		None => return Err("Block should be accompanied by a valid target state".to_owned().into()),
332	};
333
334	let transfer = &target_state.transfer;
335
336	let mut channel_state = match views::get_channel_by_canonical_identifier(
337		&chain_state,
338		transfer.balance_proof.canonical_identifier.clone(),
339	) {
340		Some(channel_state) => channel_state.clone(),
341		None =>
342			return Ok(TargetTransition {
343				new_state: Some(target_state),
344				chain_state,
345				events: vec![],
346			}),
347	};
348
349	let valid_secret =
350		utils::is_valid_onchain_secret_reveal(&state_change, transfer.lock.secrethash);
351
352	if valid_secret {
353		channel::register_onchain_secret(
354			&mut channel_state,
355			state_change.secret.clone(),
356			state_change.secrethash,
357			state_change.block_number,
358			true,
359		);
360		update_channel(&mut chain_state, channel_state).map_err(Into::into)?;
361
362		target_state.state = TargetState::OffchainSecretReveal;
363		target_state.secret = Some(state_change.secret);
364	}
365
366	Ok(TargetTransition { new_state: Some(target_state), chain_state, events: vec![] })
367}
368
369/// Remove expired locks from channel states.
370fn handle_lock_expired(
371	mut chain_state: ChainState,
372	target_state: Option<TargetTransferState>,
373	state_change: ReceiveLockExpired,
374) -> TransitionResult {
375	let target_state = match target_state {
376		Some(target_state) => target_state,
377		None => return Err("Block should be accompanied by a valid target state".to_owned().into()),
378	};
379
380	let transfer = &target_state.transfer;
381
382	let mut channel_state = match views::get_channel_by_canonical_identifier(
383		&chain_state,
384		transfer.balance_proof.canonical_identifier.clone(),
385	) {
386		Some(channel_state) => channel_state.clone(),
387		None =>
388			return Ok(TargetTransition {
389				new_state: Some(target_state),
390				chain_state,
391				events: vec![],
392			}),
393	};
394
395	let sender = match transfer.balance_proof.sender {
396		Some(sender) => sender,
397		None => return Err("Transfer sender should be set".to_owned().into()),
398	};
399	let recipient_metadata = views::get_address_metadata(sender, transfer.route_states.clone());
400	let mut result = channel::handle_receive_lock_expired(
401		&mut channel_state,
402		state_change,
403		chain_state.block_number,
404		recipient_metadata,
405	)?;
406	let channel_state = match result.new_state {
407		Some(channel_state) => channel_state,
408		None =>
409			return Err("handle_receive_lock_expired should not delete channel".to_owned().into()),
410	};
411
412	update_channel(&mut chain_state, channel_state.clone()).map_err(Into::into)?;
413
414	if channel::views::get_lock(&channel_state.partner_state, transfer.lock.secrethash).is_none() {
415		let unlock_failed = ErrorUnlockClaimFailed {
416			identifier: transfer.payment_identifier,
417			secrethash: transfer.lock.secrethash,
418			reason: "Lock expired".to_owned(),
419		};
420		result.events.push(unlock_failed.into());
421	}
422
423	Ok(TargetTransition { new_state: Some(target_state), chain_state, events: result.events })
424}
425
426/// Handles a `ReceiveUnlock` state change.
427fn handle_unlock(
428	mut chain_state: ChainState,
429	target_state: Option<TargetTransferState>,
430	state_change: ReceiveUnlock,
431) -> TransitionResult {
432	let target_state = match target_state {
433		Some(target_state) => target_state,
434		None => return Err("Block should be accompanied by a valid target state".to_owned().into()),
435	};
436
437	let mut events = vec![];
438	let transfer = &target_state.transfer;
439
440	let mut channel_state = match views::get_channel_by_canonical_identifier(
441		&chain_state,
442		transfer.balance_proof.canonical_identifier.clone(),
443	) {
444		Some(channel_state) => channel_state.clone(),
445		None =>
446			return Ok(TargetTransition {
447				new_state: Some(target_state),
448				chain_state,
449				events: vec![],
450			}),
451	};
452
453	let sender = match transfer.balance_proof.sender {
454		Some(sender) => sender,
455		None => return Err("Transfer sender should be set".to_owned().into()),
456	};
457	let recipient_metadata = views::get_address_metadata(sender, transfer.route_states.clone());
458
459	let unlock_event =
460		match channel::handle_unlock(&mut channel_state, state_change, recipient_metadata) {
461			Ok(unlock_event) => unlock_event,
462			Err((_, error_event)) =>
463				return Ok(TargetTransition {
464					new_state: Some(target_state),
465					chain_state,
466					events: vec![error_event],
467				}),
468		};
469
470	update_channel(&mut chain_state, channel_state.clone()).map_err(Into::into)?;
471
472	let payment_received_success = PaymentReceivedSuccess {
473		token_network_registry_address: channel_state.token_network_registry_address,
474		token_network_address: channel_state.canonical_identifier.token_network_address,
475		identifier: transfer.payment_identifier,
476		amount: transfer.lock.amount,
477		initiator: transfer.initiator,
478	};
479	events.push(unlock_event);
480	events.push(payment_received_success.into());
481
482	Ok(TargetTransition { new_state: None, chain_state, events })
483}
484
485/// State machine for the target node of a mediated transfer.
486pub fn state_transition(
487	chain_state: ChainState,
488	target_state: Option<TargetTransferState>,
489	state_change: StateChange,
490) -> TransitionResult {
491	match state_change {
492		StateChange::ActionInitTarget(inner) =>
493			handle_init_target(chain_state, target_state, inner),
494		StateChange::Block(inner) => handle_block(chain_state, target_state, inner),
495		StateChange::ReceiveSecretReveal(inner) =>
496			handle_offchain_secret_reveal(chain_state, target_state, inner),
497		StateChange::ContractReceiveSecretReveal(inner) =>
498			handle_onchain_secret_reveal(chain_state, target_state, inner),
499		StateChange::ReceiveUnlock(inner) => handle_unlock(chain_state, target_state, inner),
500		StateChange::ReceiveLockExpired(inner) =>
501			handle_lock_expired(chain_state, target_state, inner),
502		_ => Ok(TargetTransition { new_state: target_state, chain_state, events: vec![] }),
503	}
504}