Skip to content

Commit

Permalink
Merge pull request #765 from nspcc-dev/fix-issue-tx-verification
Browse files Browse the repository at this point in the history
Fix issue tx verification
  • Loading branch information
roman-khimov authored Mar 17, 2020
2 parents a9e5cdd + ec76a0b commit 93236e0
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 13 deletions.
17 changes: 17 additions & 0 deletions pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,13 @@ func (bc *Blockchain) verifyResults(t *transaction.Transaction, results []*trans
if r.AssetID == UtilityTokenID() {
return errors.New("issue tx issues utility tokens")
}
asset, err := bc.dao.GetAssetState(r.AssetID)
if asset == nil || err != nil {
return errors.New("invalid asset in issue tx")
}
if asset.Available < r.Amount {
return errors.New("trying to issue more than available")
}
}
break
default:
Expand Down Expand Up @@ -1942,6 +1949,16 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
case transaction.EnrollmentType:
etx := t.Data.(*transaction.EnrollmentTX)
hashes[etx.PublicKey.GetScriptHash()] = true
case transaction.IssueType:
for _, res := range refsAndOutsToResults(references, t.Outputs) {
if res.Amount < 0 {
asset, err := bc.dao.GetAssetState(res.AssetID)
if asset == nil || err != nil {
return nil, errors.New("invalid asset in issue tx")
}
hashes[asset.Issuer] = true
}
}
case transaction.RegisterType:
reg := t.Data.(*transaction.RegisterTX)
hashes[reg.Owner.GetScriptHash()] = true
Expand Down
41 changes: 28 additions & 13 deletions pkg/core/mempool/mem_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
}
pItem.isLowPrio = fee.IsLowPriority(pItem.netFee)
mp.lock.Lock()
if !mp.verifyInputs(t) {
if !mp.checkTxConflicts(t) {
mp.lock.Unlock()
return ErrConflict
}
Expand Down Expand Up @@ -329,24 +329,39 @@ func (mp *Pool) GetVerifiedTransactions() []TxWithFee {
return t
}

// verifyInputs is an internal unprotected version of Verify.
func (mp *Pool) verifyInputs(tx *transaction.Transaction) bool {
for i := range tx.Inputs {
n := findIndexForInput(mp.inputs, &tx.Inputs[i])
if n < len(mp.inputs) && *mp.inputs[n] == tx.Inputs[i] {
return false
// areInputsInPool tries to find inputs in a given sorted pool and returns true
// if it finds any.
func areInputsInPool(inputs []transaction.Input, pool []*transaction.Input) bool {
for i := range inputs {
n := findIndexForInput(pool, &inputs[i])
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)
for i := range claim.Claims {
n := findIndexForInput(mp.claims, &claim.Claims[i])
if n < len(mp.claims) && *mp.claims[n] == claim.Claims[i] {
if areInputsInPool(claim.Claims, mp.claims) {
return false
}
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 true
}

Expand All @@ -356,5 +371,5 @@ func (mp *Pool) verifyInputs(tx *transaction.Transaction) bool {
func (mp *Pool) Verify(tx *transaction.Transaction) bool {
mp.lock.RLock()
defer mp.lock.RUnlock()
return mp.verifyInputs(tx)
return mp.checkTxConflicts(tx)
}
25 changes: 25 additions & 0 deletions pkg/core/mempool/mem_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,31 @@ func TestMemPoolVerifyClaims(t *testing.T) {
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 {
return &transaction.Transaction{
Type: transaction.MinerType,
Expand Down

0 comments on commit 93236e0

Please sign in to comment.