0

I'm developing an cryptocurrency project. When a customer tries to withdraw his money, I use the code shown here to ensure that this customer has enough balance to make the withdrawal, then I pay the desired amount into customer wallet.

public async Task EnsureCustomerHasEnoughBalance(decimal withdrawAmount, Guid customerId)
{
    var currentBalance = await _transactionService.GetCustomerBalance(customerId);

    if (currentBalance < withdrawAmount)
        throw new Exception("Insufficient balance");
}

The problem is if someone calls my async API many times and quickly, some of the requests will be processed the same time in different threads. So, any customer can hack my code and withdraw more money than his balance allows.

I've tried using lock, semaphore and etc but none of them work in async. Do you have any solution?

Thanks for any help

3
  • 3
    Use a transaction. A transaction lets you do multiple operations atomically. Commented Dec 28, 2022 at 12:42
  • I used transaction but when I call my API in parallel, I still can withdraw money more than my balance ! Commented Feb 6, 2023 at 14:04
  • Then there's probably something wrong with your transaction code. Commented Feb 6, 2023 at 14:26

2 Answers 2

2

I have some experiences for this case. To be honest when running a payment application, and with the critical action relate to balance, we need to be really carefully. In this case i don't use interal lock since we would deploy the app in many nodes, that can not guarantee the atomic. In my project, we have two options:

  • Centralize locking by using redis (redlock).
  • Using transaction, we write procedure and wrap it in transaction.
CREATE PROCEDURE withraw(in amount int)
BEGIN
    START TRANSACTION;
    select balance from accounts where id = account_id  into @avaiable_balance for update;
    if(@avaiable_balance  > amount )
    then
        update accounts set balance = balance -amount,balance=balance-amount where id = account_id;
        select 1;
    else
        select -1;
    COMMIT;
END 
Sign up to request clarification or add additional context in comments.

5 Comments

I think the problem is processing request parallel (in same time in diferrent treads), Is using transaction garantee that no one can't withdraw money more that his balance ? I mean if a hacker call withdraw api many times in short period of time (call api in a loop), can not hack our code ?
using transaction garantee that no one can't withdraw money more that his balance => yes, transaction make sure that in a single momment, just a single transcation could process. so even you call 1000 times withdraw process (100u) for an account (100u balance). just 1 process could withdraw, and 999 must be fail
I am using ef core with unit of work design pattern, but it doesn't work. so I have to use procedure to add transaction to my code ? I mean is there any way to use transaction in my c# code instead of using procedure ?
of course, you can, wrap your check function and withdraw function by using trans = context.Database.BeginTransaction(); try {...}catch(Ex){ trans.rollback()}
I try this way but when I call API 10 times at same time, it create 10 withdraw !
-1

Do you use Nethereum nuget package? If you use Nethereum package, it's ok. I used Nethereum package for withdraw money. There is no problem. Please check this site. https://nethereum.com/ https://docs.nethereum.com/en/latest/nethereum-block-processing-detail/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.