Skip to content

Fix #16: reject a transaction id that is already pending or mined - #18

Open
tanujb03 wants to merge 2 commits into
techcsispit:mainfrom
tanujb03:fix/16-reject-replayed-transaction-id
Open

tanujb03 wants to merge 2 commits into
techcsispit:mainfrom
tanujb03:fix/16-reject-replayed-transaction-id

Conversation

@tanujb03

@tanujb03 tanujb03 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Closes #16

What I changed

  • Blockchain.addTransaction now rejects a transaction whose id is already in the pending list or in any mined block ("Transaction was already submitted."). The check runs right after the signature check, before the sender's key is registered.
  • Blockchain.isValid now returns false if the same transaction id appears more than once across the mined blocks.
  • Three tests in a new file, ReplayTest.java: a pending transaction can't be added twice; a transaction copied out of a mined block can't be submitted again (balances stay 20 / 80 and the chain stays valid); a properly mined block that repeats a transaction id makes isValid() false.

Only Blockchain.java and the new ReplayTest.java change.

How it works

Every transaction already has a unique id, but the chain never checked whether it had seen that id before, so one payment Asha signed once could be copied out of a block and submitted again, and each copy was charged to her. Now addTransaction looks for the id in the pending list and in every block and refuses it if it is found, so a signed payment moves coins once. isValid does the same check while walking the chain (a set of ids seen so far), so a chain that contains a repeated id is reported as invalid and is not saved.

Existing chain.dat files: if a saved chain already contains a replayed transaction (the same id twice), isValid() is now false for it, so Storage.load will print "doesn't hold a valid chain" and start a new chain instead of loading it. That is the intended behaviour for a chain that was already double-spent, but it means such a file is no longer loadable. Files without repeated ids are unaffected, and no saved format or serialVersionUID changes. Duplicates inside a saved pending list are not checked by isValid (it only looks at mined blocks, as before).

How I tested it

  • Added the three tests above; they fail on main (pendingTransactionCannotBeAddedTwice and minedTransactionCannotBeReplayed expect an exception that is never thrown, repeatedTransactionIdInvalidatesChain expects false but gets true) and pass with the change.
  • mvn -B clean test: 21 tests, 0 failures (18 existing + 3 new), JDK 17.
  • Ran the reproduction from the issue (copy a mined transaction out of the chain and submit it again): it is now rejected.

tanujb03 and others added 2 commits October 10, 2026 11:40
…r mined

addTransaction now refuses a transaction whose id is already waiting in
the pending list or stored in any block, so an authorised payment can be
applied only once. isValid() also fails a chain in which the same
transaction id appears more than once. Adds regression tests for the
pending duplicate, the replay of a mined transaction and a hand-mined
block that repeats an id.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
They only exercise addTransaction and isValid, so they do not need to share
BlockchainTest.java, which keeps this change from touching a file other
changes append to.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Blockchain: a payment that was already mined can be submitted again, and it is accepted and mined a second time

1 participant