network: refactor RelayTx error handling

We don't need to wrap different core errors in server. Also it would be
good to provede more error info to the user.
This commit is contained in:
Anna Shaleva 2021-02-17 14:51:54 +03:00
parent 608df7fb21
commit 94430ef3ca
4 changed files with 26 additions and 101 deletions

View file

@ -1,17 +0,0 @@
package network
//go:generate stringer -type=RelayReason -output=relay_reason_string.go
// RelayReason is the type which describes the different relay outcome.
type RelayReason uint8
// List of valid RelayReason.
const (
RelaySucceed RelayReason = iota
RelayAlreadyExists
RelayOutOfMemory
RelayUnableToVerify
RelayInvalid
RelayPolicyFail
RelayUnknown
)

View file

@ -1,29 +0,0 @@
// Code generated by "stringer -type=RelayReason -output=relay_reason_string.go"; DO NOT EDIT.
package network
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[RelaySucceed-0]
_ = x[RelayAlreadyExists-1]
_ = x[RelayOutOfMemory-2]
_ = x[RelayUnableToVerify-3]
_ = x[RelayInvalid-4]
_ = x[RelayPolicyFail-5]
_ = x[RelayUnknown-6]
}
const _RelayReason_name = "RelaySucceedRelayAlreadyExistsRelayOutOfMemoryRelayUnableToVerifyRelayInvalidRelayPolicyFailRelayUnknown"
var _RelayReason_index = [...]uint8{0, 12, 30, 46, 65, 77, 92, 104}
func (i RelayReason) String() string {
if i >= RelayReason(len(_RelayReason_index)-1) {
return "RelayReason(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _RelayReason_name[_RelayReason_index[i]:_RelayReason_index[i+1]]
}

View file

@ -13,7 +13,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/consensus" "github.com/nspcc-dev/neo-go/pkg/consensus"
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/mempool"
@ -152,9 +151,8 @@ func newServerFromConstructors(config ServerConfig, chain blockchainer.Blockchai
Log: log, Log: log,
} }
n, err := notary.NewNotary(cfg, s.notaryRequestPool, func(tx *transaction.Transaction) error { n, err := notary.NewNotary(cfg, s.notaryRequestPool, func(tx *transaction.Transaction) error {
r := s.RelayTxn(tx) if err := s.RelayTxn(tx); err != nil {
if r != RelaySucceed { return fmt.Errorf("can't relay completed notary transaction: hash %s, error: %w", tx.Hash().StringLE(), err)
return fmt.Errorf("can't relay completed notary transaction: hash %s, reason: %s", tx.Hash().StringLE(), r.String())
} }
return nil return nil
}) })
@ -185,11 +183,10 @@ func newServerFromConstructors(config ServerConfig, chain blockchainer.Blockchai
return nil, fmt.Errorf("can't initialize Oracle module: %w", err) return nil, fmt.Errorf("can't initialize Oracle module: %w", err)
} }
orc.SetOnTransaction(func(tx *transaction.Transaction) { orc.SetOnTransaction(func(tx *transaction.Transaction) {
r := s.RelayTxn(tx) if err := s.RelayTxn(tx); err != nil {
if r != RelaySucceed {
orc.Log.Error("can't pool oracle tx", orc.Log.Error("can't pool oracle tx",
zap.String("hash", tx.Hash().StringLE()), zap.String("hash", tx.Hash().StringLE()),
zap.Uint8("reason", byte(r))) zap.Error(err))
} }
}) })
s.oracle = orc s.oracle = orc
@ -825,7 +822,7 @@ func (s *Server) handleExtensibleCmd(e *payload.Extensible) error {
func (s *Server) handleTxCmd(tx *transaction.Transaction) error { func (s *Server) handleTxCmd(tx *transaction.Transaction) error {
// It's OK for it to fail for various reasons like tx already existing // It's OK for it to fail for various reasons like tx already existing
// in the pool. // in the pool.
if s.verifyAndPoolTX(tx) == RelaySucceed { if s.verifyAndPoolTX(tx) == nil {
s.consensus.OnTransaction(tx) s.consensus.OnTransaction(tx)
s.broadcastTX(tx, nil) s.broadcastTX(tx, nil)
} }
@ -837,35 +834,25 @@ func (s *Server) handleP2PNotaryRequestCmd(r *payload.P2PNotaryRequest) error {
if !s.chain.P2PSigExtensionsEnabled() { if !s.chain.P2PSigExtensionsEnabled() {
return errors.New("P2PNotaryRequestCMD was received, but P2PSignatureExtensions are disabled") return errors.New("P2PNotaryRequestCMD was received, but P2PSignatureExtensions are disabled")
} }
// It's OK for it to fail for various reasons like request already existing
// in the pool.
s.RelayP2PNotaryRequest(r) s.RelayP2PNotaryRequest(r)
return nil return nil
} }
// RelayP2PNotaryRequest adds given request to the pool and relays. It does not check // RelayP2PNotaryRequest adds given request to the pool and relays. It does not check
// P2PSigExtensions enabled. // P2PSigExtensions enabled.
func (s *Server) RelayP2PNotaryRequest(r *payload.P2PNotaryRequest) RelayReason { func (s *Server) RelayP2PNotaryRequest(r *payload.P2PNotaryRequest) error {
ret := s.verifyAndPoolNotaryRequest(r) err := s.verifyAndPoolNotaryRequest(r)
if ret == RelaySucceed { if err == nil {
s.broadcastP2PNotaryRequestPayload(nil, r) s.broadcastP2PNotaryRequestPayload(nil, r)
} }
return ret return err
} }
// verifyAndPoolNotaryRequest verifies NotaryRequest payload and adds it to the payload mempool. // verifyAndPoolNotaryRequest verifies NotaryRequest payload and adds it to the payload mempool.
func (s *Server) verifyAndPoolNotaryRequest(r *payload.P2PNotaryRequest) RelayReason { func (s *Server) verifyAndPoolNotaryRequest(r *payload.P2PNotaryRequest) error {
if err := s.chain.PoolTxWithData(r.FallbackTransaction, r, s.notaryRequestPool, s.notaryFeer, verifyNotaryRequest); err != nil { return s.chain.PoolTxWithData(r.FallbackTransaction, r, s.notaryRequestPool, s.notaryFeer, verifyNotaryRequest)
switch {
case errors.Is(err, core.ErrAlreadyExists):
return RelayAlreadyExists
case errors.Is(err, core.ErrOOM):
return RelayOutOfMemory
case errors.Is(err, core.ErrPolicy):
return RelayPolicyFail
default:
return RelayInvalid
}
}
return RelaySucceed
} }
// verifyNotaryRequest is a function for state-dependant P2PNotaryRequest payload verification which is executed before ordinary blockchain's verification. // verifyNotaryRequest is a function for state-dependant P2PNotaryRequest payload verification which is executed before ordinary blockchain's verification.
@ -1163,30 +1150,18 @@ func (s *Server) relayBlocksLoop() {
} }
// verifyAndPoolTX verifies the TX and adds it to the local mempool. // verifyAndPoolTX verifies the TX and adds it to the local mempool.
func (s *Server) verifyAndPoolTX(t *transaction.Transaction) RelayReason { func (s *Server) verifyAndPoolTX(t *transaction.Transaction) error {
if err := s.chain.PoolTx(t); err != nil { return s.chain.PoolTx(t)
switch {
case errors.Is(err, core.ErrAlreadyExists):
return RelayAlreadyExists
case errors.Is(err, core.ErrOOM):
return RelayOutOfMemory
case errors.Is(err, core.ErrPolicy):
return RelayPolicyFail
default:
return RelayInvalid
}
}
return RelaySucceed
} }
// RelayTxn a new transaction to the local node and the connected peers. // RelayTxn a new transaction to the local node and the connected peers.
// Reference: the method OnRelay in C#: https://github.com/neo-project/neo/blob/master/neo/Network/P2P/LocalNode.cs#L159 // Reference: the method OnRelay in C#: https://github.com/neo-project/neo/blob/master/neo/Network/P2P/LocalNode.cs#L159
func (s *Server) RelayTxn(t *transaction.Transaction) RelayReason { func (s *Server) RelayTxn(t *transaction.Transaction) error {
ret := s.verifyAndPoolTX(t) err := s.verifyAndPoolTX(t)
if ret == RelaySucceed { if err == nil {
s.broadcastTX(t, nil) s.broadcastTX(t, nil)
} }
return ret return err
} }
// broadcastTX broadcasts an inventory message about new transaction. // broadcastTX broadcasts an inventory message about new transaction.

View file

@ -1266,24 +1266,20 @@ func (s *Server) submitNotaryRequest(ps request.Params) (interface{}, *response.
} }
// getRelayResult returns successful relay result or an error. // getRelayResult returns successful relay result or an error.
func getRelayResult(relayReason network.RelayReason, hash util.Uint256) (interface{}, *response.Error) { func getRelayResult(err error, hash util.Uint256) (interface{}, *response.Error) {
switch relayReason { switch {
case network.RelaySucceed: case err == nil:
return result.RelayResult{ return result.RelayResult{
Hash: hash, Hash: hash,
}, nil }, nil
case network.RelayAlreadyExists: case errors.Is(err, core.ErrAlreadyExists):
return nil, response.ErrAlreadyExists return nil, response.ErrAlreadyExists
case network.RelayOutOfMemory: case errors.Is(err, core.ErrOOM):
return nil, response.ErrOutOfMemory return nil, response.ErrOutOfMemory
case network.RelayUnableToVerify: case errors.Is(err, core.ErrPolicy):
return nil, response.ErrUnableToVerify
case network.RelayInvalid:
return nil, response.ErrValidationFailed
case network.RelayPolicyFail:
return nil, response.ErrPolicyFail return nil, response.ErrPolicyFail
default: default:
return nil, response.ErrUnknown return nil, response.ErrValidationFailed
} }
} }