Merge pull request #765 from nspcc-dev/fix-issue-tx-verification

Fix issue tx verification
This commit is contained in:
Roman Khimov 2020-03-17 11:42:23 +03:00 committed by GitHub
commit 93236e0cfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 13 deletions

View file

@ -1660,6 +1660,13 @@ func (bc *Blockchain) verifyResults(t *transaction.Transaction, results []*trans
if r.AssetID == UtilityTokenID() { if r.AssetID == UtilityTokenID() {
return errors.New("issue tx issues utility tokens") 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 break
default: default:
@ -1942,6 +1949,16 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
case transaction.EnrollmentType: case transaction.EnrollmentType:
etx := t.Data.(*transaction.EnrollmentTX) etx := t.Data.(*transaction.EnrollmentTX)
hashes[etx.PublicKey.GetScriptHash()] = true 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: case transaction.RegisterType:
reg := t.Data.(*transaction.RegisterTX) reg := t.Data.(*transaction.RegisterTX)
hashes[reg.Owner.GetScriptHash()] = true hashes[reg.Owner.GetScriptHash()] = true

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])
if n < len(pool) && *pool[n] == inputs[i] {
return true
}
}
return false return false
} }
}
if tx.Type == 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] {
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)
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 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,