hello, I’ve been facing problem with making async call. my state have some asynchronous methods, and how do you call those method inside the thread local?
I handled my code this way:
pub async fn pay_loan(note_id: u128, loan_index: u32) -> PayLoanResponse{
let caller = ic_cdk::caller();
let note_data: Result<NoteData, PayLoanResponse> = NOTE_STATE.with(|state|{
let state = state.borrow();
match state.note_list.get(¬e_id){
Some(note_data) => Ok(note_data.clone()),
None => Err(PayLoanResponse::InvalidNoteId)
}
});
let mut note_data = match note_data{
Err(e) => return e,
Ok(note_data) => note_data,
};
let response = note_data.pay_loan(note_id, loan_index, &caller).await;
NOTE_STATE.with(|state|{
let state = &mut state.borrow_mut();
state.note_list.insert(note_id, note_data);
});
response
}
but my code is re entrancy attack prone. how do you solve it in a better way?
1 Like
Your code is valid and yes, it is prone to reentrancy attack.
The common way to solve this is to never update the state after (async) external calls. You should swap these two instructions:
let response = note_data.pay_loan(note_id, loan_index, &caller).await;
NOTE_STATE.with(|state|{
let state = &mut state.borrow_mut();
state.note_list.insert(note_id, note_data);
});
So the call happens after the state update. This approach also reveals another error in your code snippet - you should always check for errors that might happen during an external async call. In your case you can simply revert the state to it’s original:
NOTE_STATE.with(|state|{
let state = &mut state.borrow_mut();
state.note_list.insert(note_id, note_data);
});
let response = note_data.pay_loan(note_id, loan_index, &caller).await;
if let Err(e) = &response {
NOTE_STATE.with(|state|{
let state = &mut state.borrow_mut();
state.note_list.remove(¬e_id);
});
}
response
2 Likes
Congrats on realising that your code is vulnerable, that alone is already a huge accomplishment. Not many people realise that.
There is no general solution except for ‘fix it’. In your case, @senior.joinu already explained very nicely how to fix in your case.
If you want an example of how I do exactly that in the cycles faucet, have a look at this post, which explains the process in a lot of detail.
3 Likes
thanks, will look update the code again.
thanks, haha. but your answer is based for motoko, isn’t it??
Yes, I’m trying to show you how to approach the logic, not a copy/paste solution that may or may not fix your situation