Suppose we are building online casino/market and want to receive payments.

To be scalable, safe and have good user experience, we:

  • cannot generate new address for every single payment. Having single deposit address is a lot easier. Also users always can deposit to old address by mitake even if generated new address.
  • cannot check listreceivedbyaddress because regulary checking hundreds of thausands of addresses is not scalable. Also large RPC output when single user make thousand of deposits.
  • we should do not rely on external services online blockchain API.

We absolutely cannot use any RPC call that will list complete and growing list of transactions like all transactions for wallet or all transactions for address.

So, the most sensible way should be checking by listsinceblock to parse all transactions "per analysed block" and joining to user balances by destination address - incrase user amount in our online service and mark this payment as "completed" to not count it second time. Simplest way of marking this transaction as "already resolved" is to store its ID as "already used" in same SQL transaction that incrases user balance.

But I heard that it is possible to change transaction id in block chain, so is such solution safe?

How can I use RPC calls to receive payments block after block and which data can I use to mark payment as resolved to be safe from duplicated deposits on single payment?

TL;DR how "big players" like Cryptsy do receive payments?


3 Answers 3


Use Bitcoin Core in server mode and use it's notification functions:

blocknotify = curl\yoururl
walletnotify = curl\yoururl

Set them up to make a call to your app.

This way, you won't be making expensive RPC calls to Bitcoin Core. You will only be receiving notifications for operations that Bitcoin Core itself already is making. So you won't bring any extra load to Bitcoin Core. You will be limited by it's own scalability.

how "big players" like Cryptsy do receive payments?

Most big players are relying on Bitcoin Core's scalability running in server mode without bringing extra load to it as far as I know.

But I heard that it is possible to change transaction id in block chain, so is such solution safe?

Not after they have received a confirmation. So the method you described works if you wait for the blocknotify call for a TX before you update your users balance.

  • Strictly speaking it is not true that transaction IDs cannot be changed once they have received a confirmation. If a fork confirms the same transaction with a different ID and the fork then becomes the longest chain, then the ID has effectively been changed.
    – cdecker
    Jun 9, 2014 at 11:44
  • Yep. True. I thought a bc fork was too far out of the scope of the question.
    – Emre K.
    Jun 9, 2014 at 11:55
  • +1 but I see a disadventage of this. This will cause problem in situation when wallet is working but web service is down (for example for maintance) and notify will be missed. But, with conjuction with scaning last blocks on startup of exchange, this can definately bring some improvement. Will accept this if no better answer during bounty will show up. Jun 9, 2014 at 16:10
  • 1
    I strongly suggest you implement a separate service that will talk to the wallet and let your exchange software talk to that service. Do not make your exchange talk directly to Bitcoin Core. And yes what you are saying is true. If you miss the first 6 confirmations, then you will not receive notifications and you will need to query Bitcoin Core. If you implement a wallet service and separate it from your exchange, this should be easier to avoid.
    – Emre K.
    Jun 10, 2014 at 6:08

Cryptsy is generating a new address per user. Once generated it remains the same so there is no risk to deposit to "old address". This is scalable and also easier to associate deposits to the right user independently from the sending address.

  • single-address-per-user doesn't guarantee scalability - if you check deposits by calling RPC transactions for address, it will not be scalable. After hundreds of deposits it will always return a huge list of transactions Jun 8, 2014 at 11:02
  • 1
    That's not the best way, you can listen websockets transmitting new transactions in the network and refresh your address balance only if they appear in new transaction Jun 8, 2014 at 22:00

I know it's late to answer but I provide my solution which handles most of the risks.

First of all, I prefer to generate a new address for any time when user wants to deposit; But if the condition doesn't let, there is another solution!

Solution: There is a getreceivedbyaddress API which gives the total amount of received Bitcoins for an specific address. I use this and a database for tracking user's deposits.

Here is the way I track it:

var totalReceived = (RPC request including the address in params field)

var lastDeposit = (DB query for fetching the user's last deposit's data)

if(lastDeposit.amount < totalReceived) {
   // insert a new deposit record for user
   // record includes the following fields:
   // 1. userId (Obvious)
   // 2. amount 
   // 3. diff
   // 4. ts (for sorting, etc)
   insertNewDeposit(userId: userId, amount: totalReceived, diff: totalReceived - lastDeposit.amount)

// fetch the user's balance from db
var currentBalance = (db query)

// calculate the user's new balance (based on the new deposit)
var newBalance = currentBalance + (currenctBalance - lastDeposit.amount)

// update the user's balance in db
updateBalance(userId: userId, balance: newBalance)

You should use this method before any others which may require the latest balance of an address (user).

This will always track the balance of an specific address (user) even if the web service goes down; right after your web service gets online, when the user requests something related to the balance, this method will update and keeps the track of the balance!

The code could have been more optimized!

Regards :)

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.