Skip to content

Commit

Permalink
Merge pull request #164 from vsbogd/return-incorrect-nonce-error
Browse files Browse the repository at this point in the history
Return incorrect nonce error
  • Loading branch information
stellarspot authored Dec 3, 2018
2 parents 93a5e73 + 9255723 commit c22b445
Show file tree
Hide file tree
Showing 13 changed files with 204 additions and 81 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ blockchain/registry.go
blockchain/multi_party_escrow.go
blockchain/singularity_net_token.go
escrow/state_service.pb.go
handler/grpc_test.pb.go

# Binaries for programs and plugins
*.exe
Expand Down
2 changes: 1 addition & 1 deletion escrow/escrow.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func (h *lockingPaymentChannelService) StartPaymentTransaction(payment *Payment)

lock, ok, err := h.locker.Lock(channelKey.String())
if err != nil {
return nil, NewPaymentError(FailedPrecondition, "cannot get mutex for channel: %v", channelKey)
return nil, NewPaymentError(Internal, "cannot get mutex for channel: %v", channelKey)
}
if !ok {
return nil, NewPaymentError(FailedPrecondition, "another transaction on channel: %v is in progress", channelKey)
Expand Down
12 changes: 5 additions & 7 deletions escrow/income.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"math/big"

"github.com/singnet/snet-daemon/handler"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

// IncomeData is used to pass information to the pricing validation system.
Expand All @@ -27,9 +25,9 @@ type IncomeData struct {
// implement this strategies additional information from gRPC context can be
// required. In such case it should be added into handler.GrpcStreamContext.
type IncomeValidator interface {
// Validate returns nil if validation is successful or correct gRPC status
// to be sent to client in case of validation error.
Validate(*IncomeData) (err *status.Status)
// Validate returns nil if validation is successful or correct PaymentError
// status to be sent to client in case of validation error.
Validate(*IncomeData) (err error)
}

type incomeValidator struct {
Expand All @@ -41,12 +39,12 @@ func NewIncomeValidator(priceInCogs *big.Int) (validator IncomeValidator) {
return &incomeValidator{priceInCogs: priceInCogs}
}

func (validator *incomeValidator) Validate(data *IncomeData) (err *status.Status) {
func (validator *incomeValidator) Validate(data *IncomeData) (err error) {

price := validator.priceInCogs

if data.Income.Cmp(price) != 0 {
err = status.Newf(codes.Unauthenticated, "income %d does not equal to price %d", data.Income, price)
err = NewPaymentError(Unauthenticated, "income %d does not equal to price %d", data.Income, price)
return
}

Expand Down
13 changes: 6 additions & 7 deletions escrow/income_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ package escrow

import (
"fmt"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"math/big"
"testing"

"github.com/stretchr/testify/assert"
)

type incomeValidatorMockType struct {
err *status.Status
err error
}

func (incomeValidator *incomeValidatorMockType) Validate(income *IncomeData) (err *status.Status) {
func (incomeValidator *incomeValidatorMockType) Validate(income *IncomeData) (err error) {
return incomeValidator.err
}

Expand All @@ -26,7 +25,7 @@ func TestIncomeValidate(t *testing.T) {
income.Sub(price, one)
err := incomeValidator.Validate(&IncomeData{Income: income})
msg := fmt.Sprintf("income %s does not equal to price %s", income, price)
assert.Equal(t, status.New(codes.Unauthenticated, msg), err)
assert.Equal(t, NewPaymentError(Unauthenticated, msg), err)

income.Set(price)
err = incomeValidator.Validate(&IncomeData{Income: income})
Expand All @@ -35,5 +34,5 @@ func TestIncomeValidate(t *testing.T) {
income.Add(price, one)
err = incomeValidator.Validate(&IncomeData{Income: income})
msg = fmt.Sprintf("income %s does not equal to price %s", income, price)
assert.Equal(t, status.New(codes.Unauthenticated, msg), err)
assert.Equal(t, NewPaymentError(Unauthenticated, msg), err)
}
2 changes: 2 additions & 0 deletions escrow/payment_channel_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ const (
// FailedPrecondition means that request cannot be handled because system
// is not in appropriate state.
FailedPrecondition PaymentErrorCode = 3
// IncorrectNonce is returned when nonce value sent by client is incorrect.
IncorrectNonce PaymentErrorCode = 4
)

// PaymentError contains error code and message and implements Error interface.
Expand Down
33 changes: 15 additions & 18 deletions escrow/payment_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/ethereum/go-ethereum/common"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/singnet/snet-daemon/blockchain"
"github.com/singnet/snet-daemon/handler"
Expand Down Expand Up @@ -53,28 +52,28 @@ func (h *paymentChannelPaymentHandler) Type() (typ string) {
return EscrowPaymentType
}

func (h *paymentChannelPaymentHandler) Payment(context *handler.GrpcStreamContext) (payment handler.Payment, err *status.Status) {
func (h *paymentChannelPaymentHandler) Payment(context *handler.GrpcStreamContext) (payment handler.Payment, err *handler.GrpcError) {
internalPayment, err := h.getPaymentFromContext(context)
if err != nil {
return
}

transaction, e := h.service.StartPaymentTransaction(internalPayment)
if e != nil {
return nil, paymentErrorToGrpcStatus(e)
return nil, paymentErrorToGrpcError(e)
}

income := big.NewInt(0)
income.Sub(internalPayment.Amount, transaction.Channel().AuthorizedAmount)
err = h.incomeValidator.Validate(&IncomeData{Income: income, GrpcContext: context})
if err != nil {
return
e = h.incomeValidator.Validate(&IncomeData{Income: income, GrpcContext: context})
if e != nil {
return nil, paymentErrorToGrpcError(e)
}

return transaction, nil
}

func (h *paymentChannelPaymentHandler) getPaymentFromContext(context *handler.GrpcStreamContext) (payment *Payment, err *status.Status) {
func (h *paymentChannelPaymentHandler) getPaymentFromContext(context *handler.GrpcStreamContext) (payment *Payment, err *handler.GrpcError) {
channelID, err := handler.GetBigInt(context.MD, PaymentChannelIDHeader)
if err != nil {
return
Expand Down Expand Up @@ -104,25 +103,21 @@ func (h *paymentChannelPaymentHandler) getPaymentFromContext(context *handler.Gr
}, nil
}

func (h *paymentChannelPaymentHandler) Validate(_payment handler.Payment) (err *status.Status) {
return nil
}

func (h *paymentChannelPaymentHandler) Complete(payment handler.Payment) (err *status.Status) {
return paymentErrorToGrpcStatus(payment.(*paymentTransaction).Commit())
func (h *paymentChannelPaymentHandler) Complete(payment handler.Payment) (err *handler.GrpcError) {
return paymentErrorToGrpcError(payment.(*paymentTransaction).Commit())
}

func (h *paymentChannelPaymentHandler) CompleteAfterError(payment handler.Payment, result error) (err *status.Status) {
return paymentErrorToGrpcStatus(payment.(*paymentTransaction).Rollback())
func (h *paymentChannelPaymentHandler) CompleteAfterError(payment handler.Payment, result error) (err *handler.GrpcError) {
return paymentErrorToGrpcError(payment.(*paymentTransaction).Rollback())
}

func paymentErrorToGrpcStatus(err error) *status.Status {
func paymentErrorToGrpcError(err error) *handler.GrpcError {
if err == nil {
return nil
}

if _, ok := err.(*PaymentError); !ok {
return status.Newf(codes.Internal, "internal error: %v", err)
return handler.NewGrpcErrorf(codes.Internal, "internal error: %v", err)
}

var grpcCode codes.Code
Expand All @@ -133,9 +128,11 @@ func paymentErrorToGrpcStatus(err error) *status.Status {
grpcCode = codes.Unauthenticated
case FailedPrecondition:
grpcCode = codes.FailedPrecondition
case IncorrectNonce:
grpcCode = handler.IncorrectNonce
default:
grpcCode = codes.Internal
}

return status.Newf(grpcCode, err.(*PaymentError).Message)
return handler.NewGrpcErrorf(grpcCode, err.(*PaymentError).Message)
}
17 changes: 8 additions & 9 deletions escrow/payment_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/suite"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"

"github.com/singnet/snet-daemon/blockchain"
"github.com/singnet/snet-daemon/handler"
Expand Down Expand Up @@ -73,7 +72,7 @@ func (suite *PaymentHandlerTestSuite) TestGetPayment() {

_, err := suite.paymentHandler.Payment(context)

assert.Nil(suite.T(), err, "Unexpected error: %v", err.Message())
assert.Nil(suite.T(), err, "Unexpected error: %v", err)
}

func (suite *PaymentHandlerTestSuite) TestGetPaymentNoChannelId() {
Expand All @@ -83,7 +82,7 @@ func (suite *PaymentHandlerTestSuite) TestGetPaymentNoChannelId() {

payment, err := suite.paymentHandler.Payment(context)

assert.Equal(suite.T(), status.New(codes.InvalidArgument, "missing \"snet-payment-channel-id\""), err)
assert.Equal(suite.T(), handler.NewGrpcError(codes.InvalidArgument, "missing \"snet-payment-channel-id\""), err)
assert.Nil(suite.T(), payment)
}

Expand All @@ -94,7 +93,7 @@ func (suite *PaymentHandlerTestSuite) TestGetPaymentNoChannelNonce() {

payment, err := suite.paymentHandler.Payment(context)

assert.Equal(suite.T(), status.New(codes.InvalidArgument, "missing \"snet-payment-channel-nonce\""), err)
assert.Equal(suite.T(), handler.NewGrpcError(codes.InvalidArgument, "missing \"snet-payment-channel-nonce\""), err)
assert.Nil(suite.T(), payment)
}

Expand All @@ -105,7 +104,7 @@ func (suite *PaymentHandlerTestSuite) TestGetPaymentNoChannelAmount() {

payment, err := suite.paymentHandler.Payment(context)

assert.Equal(suite.T(), status.New(codes.InvalidArgument, "missing \"snet-payment-channel-amount\""), err)
assert.Equal(suite.T(), handler.NewGrpcError(codes.InvalidArgument, "missing \"snet-payment-channel-amount\""), err)
assert.Nil(suite.T(), payment)
}

Expand All @@ -116,7 +115,7 @@ func (suite *PaymentHandlerTestSuite) TestGetPaymentNoSignature() {

payment, err := suite.paymentHandler.Payment(context)

assert.Equal(suite.T(), status.New(codes.InvalidArgument, "missing \"snet-payment-channel-signature-bin\""), err)
assert.Equal(suite.T(), handler.NewGrpcError(codes.InvalidArgument, "missing \"snet-payment-channel-signature-bin\""), err)
assert.Nil(suite.T(), payment)
}

Expand All @@ -129,18 +128,18 @@ func (suite *PaymentHandlerTestSuite) TestStartTransactionError() {

payment, err := paymentHandler.Payment(context)

assert.Equal(suite.T(), status.New(codes.FailedPrecondition, "another transaction in progress"), err)
assert.Equal(suite.T(), handler.NewGrpcError(codes.FailedPrecondition, "another transaction in progress"), err)
assert.Nil(suite.T(), payment)
}

func (suite *PaymentHandlerTestSuite) TestValidatePaymentIncorrectIncome() {
context := suite.grpcContext(func(md *metadata.MD) {})
incomeErr := status.New(codes.Unauthenticated, "incorrect payment income: \"45\", expected \"46\"")
incomeErr := NewPaymentError(Unauthenticated, "incorrect payment income: \"45\", expected \"46\"")
paymentHandler := suite.paymentHandler
paymentHandler.incomeValidator = &incomeValidatorMockType{err: incomeErr}

payment, err := paymentHandler.Payment(context)

assert.Equal(suite.T(), incomeErr, err)
assert.Equal(suite.T(), handler.NewGrpcError(codes.Unauthenticated, "incorrect payment income: \"45\", expected \"46\""), err)
assert.Nil(suite.T(), payment)
}
2 changes: 1 addition & 1 deletion escrow/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (validator *ChannelPaymentValidator) Validate(payment *Payment, channel *Pa

if payment.ChannelNonce.Cmp(channel.Nonce) != 0 {
log.Warn("Incorrect nonce is sent by client")
return NewPaymentError(Unauthenticated, "incorrect payment channel nonce, latest: %v, sent: %v", channel.Nonce, payment.ChannelNonce)
return NewPaymentError(IncorrectNonce, "incorrect payment channel nonce, latest: %v, sent: %v", channel.Nonce, payment.ChannelNonce)
}

signerAddress, err := getSignerAddressFromPayment(payment)
Expand Down
2 changes: 1 addition & 1 deletion escrow/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func (suite *ValidationTestSuite) TestValidatePaymentChannelNonce() {

err := suite.validator.Validate(payment, channel)

assert.Equal(suite.T(), NewPaymentError(Unauthenticated, "incorrect payment channel nonce, latest: 3, sent: 2"), err)
assert.Equal(suite.T(), NewPaymentError(IncorrectNonce, "incorrect payment channel nonce, latest: 3, sent: 2"), err)
}

func (suite *ValidationTestSuite) TestValidatePaymentIncorrectSignatureLength() {
Expand Down
75 changes: 75 additions & 0 deletions handler/grpc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//go:generate protoc grpc_test.proto --go_out=plugins=grpc:.

package handler

import (
"context"
"net"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
)

type GrpcTestSuite struct {
suite.Suite
}

func (suite *GrpcTestSuite) SetupSuite() {
}

func (suite *GrpcTestSuite) TearDownSuite() {
}

func TestGrpcTestSuite(t *testing.T) {
suite.Run(t, new(GrpcTestSuite))
}

type exampleServiceMock struct {
output *Output
err error
}

func (service *exampleServiceMock) Ping(context context.Context, input *Input) (output *Output, err error) {
return service.output, service.err
}

func startServiceAndClient(service ExampleServiceServer) (ExampleServiceClient, *grpc.ClientConn) {
ch := make(chan int)
go func() {
listener, err := net.Listen("tcp", ":12345")
if err != nil {
panic(err)
}

server := grpc.NewServer()
RegisterExampleServiceServer(server, service)

ch <- 0

server.Serve(listener)
}()

_ = <-ch

connection, err := grpc.Dial("localhost:12345", grpc.WithInsecure())
if err != nil {
panic(err)
}

client := NewExampleServiceClient(connection)

return client, connection
}

func (suite *GrpcTestSuite) TestReturnCustomErrorCodeViaGrpc() {
expectedErr := status.Newf(1000, "error message").Err()
client, connection := startServiceAndClient(&exampleServiceMock{err: expectedErr})
defer connection.Close()

_, err := client.Ping(context.Background(), &Input{Message: "ping"})

assert.Equal(suite.T(), err, expectedErr)
}
15 changes: 15 additions & 0 deletions handler/grpc_test.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
syntax = "proto3";

package handler;

service ExampleService {
rpc Ping(Input) returns (Output) {}
}

message Input {
string message = 1;
}

message Output {
string message = 1;
}
Loading

0 comments on commit c22b445

Please sign in to comment.