mempool: disallow more than one issue tx at once

Technically they could conflict for available asset amount, but as they're
very rare (and even can be considered obsolete) we can simplify this check.
This commit is contained in:
Roman Khimov 2020-03-16 19:07:23 +03:00
parent d5d0479671
commit ec76a0bf15
2 changed files with 53 additions and 13 deletions

View file

@ -168,7 +168,7 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
} }
pItem.isLowPrio = fee.IsLowPriority(pItem.netFee) pItem.isLowPrio = fee.IsLowPriority(pItem.netFee)
mp.lock.Lock() mp.lock.Lock()
if !mp.verifyInputs(t) { if !mp.checkTxConflicts(t) {
mp.lock.Unlock() mp.lock.Unlock()
return ErrConflict return ErrConflict
} }
@ -329,24 +329,39 @@ func (mp *Pool) GetVerifiedTransactions() []TxWithFee {
return t return t
} }
// verifyInputs is an internal unprotected version of Verify. // areInputsInPool tries to find inputs in a given sorted pool and returns true
func (mp *Pool) verifyInputs(tx *transaction.Transaction) bool { // if it finds any.
for i := range tx.Inputs { func areInputsInPool(inputs []transaction.Input, pool []*transaction.Input) bool {
n := findIndexForInput(mp.inputs, &tx.Inputs[i]) for i := range inputs {
if n < len(mp.inputs) && *mp.inputs[n] == tx.Inputs[i] { n := findIndexForInput(pool, &inputs[i])
return false if n < len(pool) && *pool[n] == inputs[i] {
return true
} }
} }
if tx.Type == transaction.ClaimType { return false
}
// checkTxConflicts is an internal unprotected version of Verify.
func (mp *Pool) checkTxConflicts(tx *transaction.Transaction) bool {
if areInputsInPool(tx.Inputs, mp.inputs) {
return false
}
switch tx.Type {
case transaction.ClaimType:
claim := tx.Data.(*transaction.ClaimTX) claim := tx.Data.(*transaction.ClaimTX)
for i := range claim.Claims { if areInputsInPool(claim.Claims, mp.claims) {
n := findIndexForInput(mp.claims, &claim.Claims[i]) return false
if n < len(mp.claims) && *mp.claims[n] == claim.Claims[i] { }
case transaction.IssueType:
// It's a hack, because technically we could check for
// available asset amount, but these transactions are so rare
// that no one really cares about this restriction.
for i := range mp.verifiedTxes {
if mp.verifiedTxes[i].txn.Type == transaction.IssueType {
return false return false
} }
} }
} }
return true return true
} }
@ -356,5 +371,5 @@ func (mp *Pool) verifyInputs(tx *transaction.Transaction) bool {
func (mp *Pool) Verify(tx *transaction.Transaction) bool { func (mp *Pool) Verify(tx *transaction.Transaction) bool {
mp.lock.RLock() mp.lock.RLock()
defer mp.lock.RUnlock() defer mp.lock.RUnlock()
return mp.verifyInputs(tx) return mp.checkTxConflicts(tx)
} }

View file

@ -177,6 +177,31 @@ func TestMemPoolVerifyClaims(t *testing.T) {
require.Error(t, mp.Add(tx3, &FeerStub{})) require.Error(t, mp.Add(tx3, &FeerStub{}))
} }
func TestMemPoolVerifyIssue(t *testing.T) {
mp := NewMemPool(50)
tx1 := newIssueTX()
require.Equal(t, true, mp.Verify(tx1))
require.NoError(t, mp.Add(tx1, &FeerStub{}))
tx2 := newIssueTX()
require.Equal(t, false, mp.Verify(tx2))
require.Error(t, mp.Add(tx2, &FeerStub{}))
}
func newIssueTX() *transaction.Transaction {
return &transaction.Transaction{
Type: transaction.IssueType,
Data: &transaction.IssueTX{},
Outputs: []transaction.Output{
transaction.Output{
AssetID: random.Uint256(),
Amount: util.Fixed8FromInt64(42),
ScriptHash: random.Uint160(),
},
},
}
}
func newMinerTX(i uint32) *transaction.Transaction { func newMinerTX(i uint32) *transaction.Transaction {
return &transaction.Transaction{ return &transaction.Transaction{
Type: transaction.MinerType, Type: transaction.MinerType,