transaction: add tests for (*Transaction).isValid()

This commit is contained in:
Evgenii Stratonikov 2020-08-19 16:39:49 +03:00
parent 890c752b3b
commit 7ee1ddff61
2 changed files with 106 additions and 9 deletions

View file

@ -3,6 +3,7 @@ package transaction
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
@ -332,30 +333,43 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
return t.isValid()
}
// Various errors for transaction validation.
var (
ErrInvalidVersion = errors.New("only version 0 is supported")
ErrNegativeSystemFee = errors.New("negative system fee")
ErrNegativeNetworkFee = errors.New("negative network fee")
ErrTooBigFees = errors.New("too big fees: int64 overflow")
ErrEmptySigners = errors.New("signers array should contain sender")
ErrInvalidScope = errors.New("FeeOnly scope can be used only for sender")
ErrNonUniqueSigners = errors.New("transaction signers should be unique")
ErrInvalidAttribute = errors.New("invalid attribute")
ErrEmptyScript = errors.New("no script")
)
// isValid checks whether decoded/unmarshalled transaction has all fields valid.
func (t *Transaction) isValid() error {
if t.Version > 0 {
return errors.New("only version 0 is supported")
return ErrInvalidVersion
}
if t.SystemFee < 0 {
return errors.New("negative system fee")
return ErrNegativeSystemFee
}
if t.NetworkFee < 0 {
return errors.New("negative network fee")
return ErrNegativeNetworkFee
}
if t.NetworkFee+t.SystemFee < t.SystemFee {
return errors.New("too big fees: int64 overflow")
return ErrTooBigFees
}
if len(t.Signers) == 0 {
return errors.New("signers array should contain sender")
return ErrEmptySigners
}
for i := 0; i < len(t.Signers); i++ {
if i > 0 && t.Signers[i].Scopes == FeeOnly {
return errors.New("FeeOnly scope can be used only for sender")
return ErrInvalidScope
}
for j := i + 1; j < len(t.Signers); j++ {
if t.Signers[i].Account.Equals(t.Signers[j].Account) {
return errors.New("transaction signers should be unique")
return ErrNonUniqueSigners
}
}
}
@ -364,13 +378,13 @@ func (t *Transaction) isValid() error {
switch t.Attributes[i].Type {
case HighPriority:
if hasHighPrio {
return errors.New("multiple high priority attributes")
return fmt.Errorf("%w: multiple high priority attributes", ErrInvalidAttribute)
}
hasHighPrio = true
}
}
if len(t.Script) == 0 {
return errors.New("no script")
return ErrEmptyScript
}
return nil
}

View file

@ -3,6 +3,8 @@ package transaction
import (
"encoding/base64"
"encoding/hex"
"errors"
"math"
"testing"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
@ -128,3 +130,84 @@ func TestTransaction_HasAttribute(t *testing.T) {
tx.Attributes = append(tx.Attributes, Attribute{Type: HighPriority})
require.True(t, tx.HasAttribute(HighPriority))
}
func TestTransaction_isValid(t *testing.T) {
newTx := func() *Transaction {
return &Transaction{
Version: 0,
SystemFee: 100,
NetworkFee: 100,
Signers: []Signer{
{Account: util.Uint160{1, 2, 3}},
{
Account: util.Uint160{4, 5, 6},
Scopes: Global,
},
},
Script: []byte{1, 2, 3, 4},
Attributes: []Attribute{},
Scripts: []Witness{},
Trimmed: false,
}
}
t.Run("Valid", func(t *testing.T) {
t.Run("NoAttributes", func(t *testing.T) {
tx := newTx()
require.NoError(t, tx.isValid())
})
t.Run("HighPriority", func(t *testing.T) {
tx := newTx()
tx.Attributes = []Attribute{{Type: HighPriority}}
require.NoError(t, tx.isValid())
})
})
t.Run("InvalidVersion", func(t *testing.T) {
tx := newTx()
tx.Version = 1
require.True(t, errors.Is(tx.isValid(), ErrInvalidVersion))
})
t.Run("NegativeSystemFee", func(t *testing.T) {
tx := newTx()
tx.SystemFee = -1
require.True(t, errors.Is(tx.isValid(), ErrNegativeSystemFee))
})
t.Run("NegativeNetworkFee", func(t *testing.T) {
tx := newTx()
tx.NetworkFee = -1
require.True(t, errors.Is(tx.isValid(), ErrNegativeNetworkFee))
})
t.Run("TooBigFees", func(t *testing.T) {
tx := newTx()
tx.SystemFee = math.MaxInt64 - tx.NetworkFee + 1
require.True(t, errors.Is(tx.isValid(), ErrTooBigFees))
})
t.Run("EmptySigners", func(t *testing.T) {
tx := newTx()
tx.Signers = tx.Signers[:0]
require.True(t, errors.Is(tx.isValid(), ErrEmptySigners))
})
t.Run("InvalidScope", func(t *testing.T) {
tx := newTx()
tx.Signers[1].Scopes = FeeOnly
require.True(t, errors.Is(tx.isValid(), ErrInvalidScope))
})
t.Run("NonUniqueSigners", func(t *testing.T) {
tx := newTx()
tx.Signers[1].Account = tx.Signers[0].Account
require.True(t, errors.Is(tx.isValid(), ErrNonUniqueSigners))
})
t.Run("MultipleHighPriority", func(t *testing.T) {
tx := newTx()
tx.Attributes = []Attribute{
{Type: HighPriority},
{Type: HighPriority},
}
require.True(t, errors.Is(tx.isValid(), ErrInvalidAttribute))
})
t.Run("NoScript", func(t *testing.T) {
tx := newTx()
tx.Script = []byte{}
require.True(t, errors.Is(tx.isValid(), ErrEmptyScript))
})
}