[WIP] Spam Prevention via Wallet-level transaction buffer

[WIP] Spam Prevention via Wallet-level transaction buffer


This proposal introduces a new wallet-level buffer to the nodes to prevent a precomputed proof-of-work (PoW) attack on the network from a wallet. Each wallet address is given a “send” transaction buffer. Newly unconfirmed transactions are added to the buffer. Confirmed “send” transactions are removed from the buffer. If a wallet sends out a burst of transactions (e.g. precomputed PoW transactions), and is able to fill up their buffer, then they are put on a waitlist. While on a waitlist, the “send” transactions from that wallet address are ignored, no longer re-broadcasted, and not added to the buffer. That wallet address is removed from the waitlist only after their buffer empties and after X number of confirmations are seen by the node starting from when the walletAddress was waitlisted. This wallet-level buffer solves the precomputed PoW attack because it disincentivizes the user from sending out large burst transactions to prevent being waitlisted. This also does not cause any issues for normal users as most users do not require large amounts of burst transactions.

Precomputed PoW Attack

[Original RaiBlocks/Nano Whitepaper - Nano Documentation]

Since the owner of an account will be the only entity adding blocks to the account-chain, sequential blocks can be computed, along with their PoW, before being broadcasted to the network. Here the attacker generates a myriad of sequential blocks, each of minimal value, over an extended period of time. At a certain point, the attacker performs a Denial of Service (DoS) by flooding the network with lots of valid transactions, which other nodes will process and echo as quickly as possible. This is an advanced version of the transaction flooding described in the Transaction flooding section.


Each node will create a buffer for each newly encountered wallet from “send” transactions. The buffer has a configurable BUFFER_SIZE that contains all of the unconfirmed send transactions from that wallet. The wallet-buffers could have the following data structures:

WalletBuffers = Map<WalletAddress, SortedSet<UnconfirmedTransactions>>

The node processing goes as follows. When a node receives a “send” transaction, it checks the wallet address in the transactions. An example send block is shown on figure A.

Figure A

send {
previous: 1967EA355...F2F3E5BF801,
balance: 010a8044a0...1d49289d88c,
destination: xrb_3w...m37goeuufdp,
work: 0000000000000000,
type: send,
signature: 83B0...006433265C7B204

It then puts that transaction into the wallet’s buffer of unconfirmed transactions. It keeps adding newly seen “send” transactions into the wallets buffer until it fills up. Once the wallet buffer fills up. The wallet address (walletAddress) is flagged on WAITLIST status. Any further “send” transactions from this wallet is ignored by the node and not re-broadcasted until the walletAddress is out of the WAITLIST. The walletAddress is removed from the WAITLIST status when their wallet buffer empties and after a configurable amount of CONFIRMATION is seen by the node when the walletAddress was added to the WAITLIST.

WaitTime = Time(WalletBuffer to empty) + Time( See X amount of CONFIRMATION)

The time to wait for an X amount of confirmations is a variable that allows nodes to configure the penalty of filling up the wallets buffer. Most users don’t do enough burst transactions to fill up their buffer. The BUFFER_SIZE should be large enough to not affect normal users, but small enough to detect precomputed PoW attacks from a wallet.

How are “send” transactions removed from the wallet buffer?

The transactions from the wallet buffer are removed when the “send” transaction is CONFIRMED by the network. If the confirmation time is very quick for the “send” transactions, then the rate of removing from the wallet buffer should be faster than the rate of unconfirmed transactions coming in.

If(RateOfConfirmation >= RateOfUnconfirmedTransaction)
      //The buffer does not become full
       //The buffer will most likely fill up and cause the wallet to be waitlisted.

Buffer Design

Simple Buffer

  1. Sorted Set of size N based on the transaction hash. The buffer may be a sorted set based on the transaction hash for quick searching when removing a confirmed transaction.
  2. First-in first-out (FIFO) size of N Queue. New “send” transactions are added to the end. Confirmed transactions are removed from the front. (Assumption: the transactions are in order. If the transactions are out of order, this might not work.)

Tiered Buffer with PoW levels

What if normal users do not want to wait and must send out transactions even if they are waitlisted? With this buffer design, we can allow them to do that, but for a cost of higher PoW.

Let us have multiple levels of buffers. Each with ever increasing PoW that exponentially increases. These numbers are an example, but they can be configurable per level. We can also choose the number of levels allowed. Each level of buffer can increase the number of allowed unconfirmed transactions on the wallet. There might be a case where certain network actors want to do a burst transaction and are willing to pay the PoW. Examples I can think are exchanges or a busy service provider. However, for most users they can just wait.

Level 1 (normal-1x Pow)
Level 2 (5x PoW
Level 3 (20x PoW)
Level 4 (50x PoW)
Level N

Pruning the Wallet Buffers

Keeping the wallet buffers takes some RAM/storage as each wallet has their own buffer. However, empty wallet buffers can be removed or “pruned”. Most users only “send” transactions intermittently, and their buffers can be safely removed. We can order the encountered wallet addresses in a Least-Recently-Used (LRU) cache. Nodes can regularly clear out empty wallet buffers that haven't been used recently every Z number of confirmations from the network. This Z variable could be adjusted so it could be done every hour/day/week,etc

New Attack Vectors

Buffer Creation Attack

Force the nodes to create a lot of new buffers by sending transactions from inactive wallets. If the number of newly activated is large enough, the nodes would be forced to create a lot of new buffers. This should not be a big issue as it only causes more storage space for the buffers in the nodes. The attacker could keep resending transactions every period to force the nodes to keep the buffers. (Need calculation to examine this issue). These newly created buffers are only temporary and do not cause ledger bloat. This attack could also be mitigated by lowering the number of BUFFER_SIZE per wallet. Do note that lowering the BUFFER_SIZE also limits the max burst that a wallet can send.

I will keep updating the idea on a google docs as I get comments. Commenters are allowed on the google docs:

Feedback is welcome.


Great write up. You've presented some very interesting ideas that I think are worth investigating. I particularly like that the heavy lifting are handled on the wallet-side.

I like this idea a lot, it's elegant. How does this prevent the attacker from setting up n number of different addresses (wallets), each with it's own buffer, to carry out a sophisticated attack?


Thank you for your contribution. Did you take a look at Time-as-a-Currency & PoS4QoS - PoS-based Anti-spam via Timestamping ?

I think that your proposal, when fully finished, will lead to this approach above.

Even if the attacker create different address, the different addresses would be limited by their BURST. This proposal was intended to solve Precomputed POW attack from a wallet. It isn't as effective defense against multi-wallet attacks.

Hey Hwro, thank you for the reply. I had originally read that proposal first before writing this proposal. The wallet-transaction buffer differ from TaaC/P4Q in several ways.

  1. Wallet transaction buffers do not require any protocol changes. The buffers works in a similar way to the bounded backlog proposal except that the Wallet transaction buffer is per wallet.
  2. This proposal doesn't require a concept of time.
  3. The penalties in this proposal is more strict as wallets are WAITLISTED/transactions ignored for failing to follow the rules.
  4. This proposal was mostly intended to solve precomputed POW attack from a single wallet.