Skip to content

DoS vector: TXID calculation #10522

Description

@jeffro256

When validating that a transaction has some PoW bound to it, either during block sync or PoWER we usually use a transaction's TXID to bind that PoW. And transactions are transmitted over the network as opaque byte blobs. The current way that the TXID is calculated from a transaction blob, is that it is A) deserialized, then B) expanded, then C) the subsections are hashed, and finally, D) the final transaction hash is calculated from the subsection hashes. Ideally, this process should be as quick as possible, so that bad transaction data can be failed as quickly as possible, but in reality, this process is much more expensive than it needs to be.

  1. There's one Ed25519 point decompression, variable-base scalar-point multiplication, and point compression in cryptonote::expand_transaction_1() per output here and here. Each decompress + multiply + compress op is ~100x slower (citation needed) than crypto::cn_fast_hash()!!!
  2. For a $N$-input RCTTypeBulletproofPlus (v6) RingCT transaction, deserializing the transaction takes $2N +10$ memory allocations:
  1. Expanding the transaction takes a memory allocation:
    rv.p.bulletproofs_plus[0].V.resize(n_amounts);
  2. Serializing the transaction prefix inside the hashing function takes at least one allocation (calculate_transaction_prunable_hash() uses cached unprunable_size field instead of serializing):

So all told, for a N-in M-out transaction today, the daemon does $2N + 12$ memory allocations, $M$ Ed25519 point decompressions, $M$ Ed25519 variable-base scalar-point multiplications, $M$ Ed25519 point compressions, and 4 Keccak256 hashes, just to calculate the TXID. This is a DoS vector. Without a fork, performant code could reduce this to just the 4 Keccak256 hashes. With a hard fork, keeping the TXID bound to its proof data, we could reduce this to 2 Keccak256 hash, and still retain pruning capabilities. With a hard fork, moving the transaction proof data to a field in the block, we could reduce this to just 1 Keccak256 hash, and still retain pruning capabilities.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions