forked from TrueCloudLab/neoneo-go
transaction: add tests for (*Transaction).isValid()
This commit is contained in:
parent
890c752b3b
commit
7ee1ddff61
2 changed files with 106 additions and 9 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue