forked from TrueCloudLab/neoneo-go
Merge pull request #765 from nspcc-dev/fix-issue-tx-verification
Fix issue tx verification
This commit is contained in:
commit
93236e0cfa
3 changed files with 70 additions and 13 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue