diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 6fdab3311..106149043 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1150,6 +1150,9 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e if transaction.HaveDuplicateInputs(claim.Claims) { return errors.New("duplicate claims") } + if bc.dao.IsDoubleClaim(claim) { + return errors.New("double claim") + } } return bc.verifyTxWitnesses(t, block) @@ -1171,6 +1174,12 @@ func (bc *Blockchain) isTxStillRelevant(t *transaction.Transaction) bool { if bc.dao.IsDoubleSpend(t) { return false } + if t.Type == transaction.ClaimType { + claim := t.Data.(*transaction.ClaimTX) + if bc.dao.IsDoubleClaim(claim) { + return false + } + } for i := range t.Scripts { if !vm.IsStandardContract(t.Scripts[i].VerificationScript) { recheckWitness = true diff --git a/pkg/core/dao.go b/pkg/core/dao.go index cba1d59c5..d84e50816 100644 --- a/pkg/core/dao.go +++ b/pkg/core/dao.go @@ -562,6 +562,27 @@ func (dao *dao) IsDoubleSpend(tx *transaction.Transaction) bool { return false } +// IsDoubleClaim verifies that given claim inputs are not already claimed by another tx. +func (dao *dao) IsDoubleClaim(claim *transaction.ClaimTX) bool { + if len(claim.Claims) == 0 { + return false + } + for _, inputs := range transaction.GroupInputsByPrevHash(claim.Claims) { + prevHash := inputs[0].PrevHash + scs, err := dao.GetSpentCoinState(prevHash) + if err != nil { + return true + } + for _, input := range inputs { + _, ok := scs.items[input.PrevIndex] + if !ok { + return true + } + } + } + return false +} + // Persist flushes all the changes made into the (supposedly) persistent // underlying store. func (dao *dao) Persist() (int, error) {