penumbra icon indicating copy to clipboard operation
penumbra copied to clipboard

view service should gracefully handle duplicate note commitment during sync

Open redshiftzero opened this issue 11 months ago • 2 comments

Describe the bug If an attacker sends a duplicate note commitment to a target address, when the target tries to sync the view service will hang (PING/REJECT vector)

To Reproduce Steps to reproduce the behavior:

You need to send two notes with the same (address, value, rseed) to the target.

  1. Set the Rseed in OutputPlan::new to e.g. Rseed([0u8; 32]). Relevant line here.
  2. Send the same value to the target address twice, e.g. make two transactions sending 1upenumbra to the address
  3. As the target, sync

Expected behavior

View service syncs without hanging

Actual behavior

View service hangs due to unique constraint violation:

2024-03-04T23:34:11.859746Z ERROR penumbra_view::worker: view worker error e=UNIQUE constraint failed: spendable_notes.note_commitment

Caused by:
    Error code 1555: A PRIMARY KEY constraint failed
Scanning blocks from last sync height 96893 to latest height 97379
[0s] ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░       0/486     0/s ETA: 0s
^C

redshiftzero avatar Mar 04 '24 23:03 redshiftzero

Flagging that #1507 is very similar, might be the same underlying issue. Not sure how easy it is to write a regression test to cover this case

redshiftzero avatar Mar 05 '24 00:03 redshiftzero

We concluded in discord that the TCT crate is doing the right thing, and that the fix is to use an upsert to avoid the constraint violation. It would be helpful to summarize that discussion in the issue.

hdevalence avatar Mar 10 '24 20:03 hdevalence

blocked by planner refactor but should be prioritized once unblocked

aubrika avatar Apr 10 '24 17:04 aubrika

Suggestion from Discord discussion:

  1. check all new note commitments against storage
  2. if note commitment already known, write a zero byte into the note payload ciphertext
  3. this triggers decryption failure

redshiftzero avatar May 08 '24 14:05 redshiftzero

Looking at it I think there's an even simpler solution here, which is to modify the record_block behavior to ignore/do nothing when it receives a duplicate note (or swap) commitment - this seems to be basically a primary key uniqueness violation on account of having overly simple INSERT behavior - I'll throw up a PR shortly and try to reproduce to ensure this works

aubrika avatar May 20 '24 20:05 aubrika

we might need to upsert rather than discard, if the TCT behavior is to retain the auth path for the newer duplicate leaf, since in that case the TCT would have an auth path for the newer note rather than the older one

hdevalence avatar May 20 '24 21:05 hdevalence

Oh yeah, I'll ensure that's the case and if the auth path is being updated out of sync with view storage, just update the columns with the data attending the duplicate commitment

aubrika avatar May 20 '24 21:05 aubrika