diff --git a/execution/errors/errors.go b/execution/errors/errors.go index 8e3bd2cc144312712983a4ddb2418dea055daace..b3bc554166d19ec2900db0c186d47069a8d06edc 100644 --- a/execution/errors/errors.go +++ b/execution/errors/errors.go @@ -32,6 +32,12 @@ const ( ErrorCodeEventPublish ErrorCodeInvalidString ErrorCodeEventMapping + ErrorCodeInvalidAddress + ErrorCodeDuplicateAddress + ErrorCodeInsufficientFunds + ErrorCodeOverpayment + ErrorCodeZeroPayment + ErrorCodeInvalidSequence ) func (c Code) ErrorCode() Code { @@ -39,6 +45,10 @@ func (c Code) ErrorCode() Code { } func (c Code) Error() string { + return fmt.Sprintf("Error %d: %s", c, c.String()) +} + +func (c Code) String() string { switch c { case ErrorCodeUnknownAddress: return "Unknown address" @@ -82,6 +92,18 @@ func (c Code) Error() string { return "Event mapping error" case ErrorCodeGeneric: return "Generic error" + case ErrorCodeInvalidAddress: + return "Invalid address" + case ErrorCodeDuplicateAddress: + return "Duplicate address" + case ErrorCodeInsufficientFunds: + return "Insufficient funds" + case ErrorCodeOverpayment: + return "Overpayment" + case ErrorCodeZeroPayment: + return "Zero payment error" + case ErrorCodeInvalidSequence: + return "Invalid sequence number" default: return "Unknown error" } diff --git a/execution/errors/errors_test.go b/execution/errors/errors_test.go index 2a4a0a37bfda11113edfeb08417a37cf497c0e25..c80c057229958daa077b7b7e19bb6aa589b33c7b 100644 --- a/execution/errors/errors_test.go +++ b/execution/errors/errors_test.go @@ -2,6 +2,7 @@ package errors import ( "encoding/json" + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -9,7 +10,7 @@ import ( ) func TestErrorCode_MarshalJSON(t *testing.T) { - ec := NewCodedError(ErrorCodeDataStackOverflow, "arrg") + ec := NewCodedError(ErrorCodeDataStackOverflow, "arrgh") bs, err := json.Marshal(ec) require.NoError(t, err) @@ -19,3 +20,8 @@ func TestErrorCode_MarshalJSON(t *testing.T) { assert.Equal(t, ec, ecOut) } + +func TestCode_String(t *testing.T) { + err := ErrorCodeCodeOutOfBounds + fmt.Println(err.Error()) +} diff --git a/execution/executors/call_context.go b/execution/executors/call_context.go index 78fa25e4a7218770693655e267da7a433d428bfd..0f343819c67bdc7e4167fcdb5a6c0a936f0b8472 100644 --- a/execution/executors/call_context.go +++ b/execution/executors/call_context.go @@ -58,7 +58,7 @@ func (ctx *CallContext) Precheck() (*acm.MutableAccount, acm.Account, error) { if inAcc == nil { ctx.Logger.InfoMsg("Cannot find input account", "tx_input", ctx.tx.Input) - return nil, nil, payload.ErrTxInvalidAddress + return nil, nil, errors.ErrorCodeInvalidAddress } err = validateInput(inAcc, ctx.tx.Input) @@ -70,7 +70,7 @@ func (ctx *CallContext) Precheck() (*acm.MutableAccount, acm.Account, error) { if ctx.tx.Input.Amount < ctx.tx.Fee { ctx.Logger.InfoMsg("Sender did not send enough to cover the fee", "tx_input", ctx.tx.Input) - return nil, nil, payload.ErrTxInsufficientFunds + return nil, nil, errors.ErrorCodeInsufficientFunds } ctx.Logger.TraceMsg("Incrementing sequence number for CallTx", @@ -191,7 +191,7 @@ func (ctx *CallContext) Deliver(inAcc, outAcc acm.Account, value uint64) error { "caller_address", inAcc.Address(), "callee_address", ctx.tx.Address) } - ctx.CallEvents(payload.ErrTxInvalidAddress) + ctx.CallEvents(errors.ErrorCodeInvalidAddress) return nil } callee = acm.AsMutableAccount(outAcc) diff --git a/execution/executors/name_context.go b/execution/executors/name_context.go index ce8396704b35f75b58d536f017282ecaab9589d6..3be220f9f4d5d23ce0eab129b324495c77a6568e 100644 --- a/execution/executors/name_context.go +++ b/execution/executors/name_context.go @@ -42,7 +42,7 @@ func (ctx *NameContext) Execute(txe *exec.TxExecution) error { if inAcc == nil { ctx.Logger.InfoMsg("Cannot find input account", "tx_input", ctx.tx.Input) - return payload.ErrTxInvalidAddress + return errors.ErrorCodeInvalidAddress } // check permission if !hasNamePermission(ctx.StateWriter, inAcc, ctx.Logger) { @@ -57,7 +57,7 @@ func (ctx *NameContext) Execute(txe *exec.TxExecution) error { if ctx.tx.Input.Amount < ctx.tx.Fee { ctx.Logger.InfoMsg("Sender did not send enough to cover the fee", "tx_input", ctx.tx.Input) - return payload.ErrTxInsufficientFunds + return errors.ErrorCodeInsufficientFunds } // validate the input strings diff --git a/execution/executors/permissions_context.go b/execution/executors/permissions_context.go index 39292a3b0f8cd20c74cb1b0d78ad4c54b0f02f88..11730e1376886a0dfebaf22b39bd00adc45e8491 100644 --- a/execution/executors/permissions_context.go +++ b/execution/executors/permissions_context.go @@ -7,6 +7,7 @@ import ( "github.com/hyperledger/burrow/acm/state" "github.com/hyperledger/burrow/blockchain" "github.com/hyperledger/burrow/crypto" + "github.com/hyperledger/burrow/execution/errors" "github.com/hyperledger/burrow/execution/exec" "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/logging/structure" @@ -35,7 +36,7 @@ func (ctx *PermissionsContext) Execute(txe *exec.TxExecution) error { if inAcc == nil { ctx.Logger.InfoMsg("Cannot find input account", "tx_input", ctx.tx.Input) - return payload.ErrTxInvalidAddress + return errors.ErrorCodeInvalidAddress } err = ctx.tx.PermArgs.EnsureValid() diff --git a/execution/executors/send_context.go b/execution/executors/send_context.go index 5cad86303d48049084e716abb33c803233a3c5f3..813a433bf6b093550331ab101a14c0a8b0f04db9 100644 --- a/execution/executors/send_context.go +++ b/execution/executors/send_context.go @@ -5,6 +5,7 @@ import ( "github.com/hyperledger/burrow/acm/state" "github.com/hyperledger/burrow/blockchain" + "github.com/hyperledger/burrow/execution/errors" "github.com/hyperledger/burrow/execution/exec" "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/txs/payload" @@ -49,7 +50,13 @@ func (ctx *SendContext) Execute(txe *exec.TxExecution) error { return err } if outTotal > inTotal { - return payload.ErrTxInsufficientFunds + return errors.ErrorCodeInsufficientFunds + } + if outTotal < inTotal { + return errors.ErrorCodeOverpayment + } + if outTotal == 0 { + return errors.ErrorCodeZeroPayment } // Good! Adjust accounts diff --git a/execution/executors/shared.go b/execution/executors/shared.go index 58904bb98efddc27cd0df9e2a4e3ef7cbc6e94da..33ad90a0de8bfe602cdf8d60c5d169c4d958da80 100644 --- a/execution/executors/shared.go +++ b/execution/executors/shared.go @@ -6,6 +6,7 @@ import ( "github.com/hyperledger/burrow/acm" "github.com/hyperledger/burrow/acm/state" "github.com/hyperledger/burrow/crypto" + "github.com/hyperledger/burrow/execution/errors" "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/logging/structure" "github.com/hyperledger/burrow/permission" @@ -23,14 +24,14 @@ func getInputs(accountGetter state.AccountGetter, for _, in := range ins { // Account shouldn't be duplicated if _, ok := accounts[in.Address]; ok { - return nil, payload.ErrTxDuplicateAddress + return nil, errors.ErrorCodeDuplicateAddress } acc, err := state.GetMutableAccount(accountGetter, in.Address) if err != nil { return nil, err } if acc == nil { - return nil, payload.ErrTxInvalidAddress + return nil, errors.ErrorCodeInvalidAddress } accounts[in.Address] = acc } @@ -48,7 +49,7 @@ func getOrMakeOutputs(accountGetter state.AccountGetter, accs map[crypto.Address for _, out := range outs { // Account shouldn't be duplicated if _, ok := accs[out.Address]; ok { - return nil, payload.ErrTxDuplicateAddress + return nil, errors.ErrorCodeDuplicateAddress } acc, err := state.GetMutableAccount(accountGetter, out.Address) if err != nil { @@ -92,20 +93,16 @@ func validateInputs(accs map[crypto.Address]*acm.MutableAccount, ins []*payload. } func validateInput(acc *acm.MutableAccount, in *payload.TxInput) error { - // Check TxInput basic - if err := in.ValidateBasic(); err != nil { - return err - } // Check sequences if acc.Sequence()+1 != uint64(in.Sequence) { return payload.ErrTxInvalidSequence{ - Got: in.Sequence, - Expected: acc.Sequence() + uint64(1), + Input: in, + Account: acc, } } // Check amount if acc.Balance() < uint64(in.Amount) { - return payload.ErrTxInsufficientFunds + return errors.ErrorCodeInsufficientFunds } return nil } @@ -113,10 +110,6 @@ func validateInput(acc *acm.MutableAccount, in *payload.TxInput) error { func validateOutputs(outs []*payload.TxOutput) (uint64, error) { total := uint64(0) for _, out := range outs { - // Check TxOutput basic - if err := out.ValidateBasic(); err != nil { - return 0, err - } // Good. Add amount to total total += out.Amount } diff --git a/txs/payload/errors.go b/txs/payload/errors.go index affd564371667d29d81218fdf8316182c5ff6a91..0c056c4f317157e52ff24268f632b1007b5c6e88 100644 --- a/txs/payload/errors.go +++ b/txs/payload/errors.go @@ -1,25 +1,22 @@ package payload import ( - "errors" "fmt" -) -var ( - ErrTxInvalidAddress = errors.New("error invalid address") - ErrTxDuplicateAddress = errors.New("error duplicate address") - ErrTxInvalidAmount = errors.New("error invalid amount") - ErrTxInsufficientFunds = errors.New("error insufficient funds") - ErrTxUnknownPubKey = errors.New("error unknown pubkey") - ErrTxInvalidPubKey = errors.New("error invalid pubkey") - ErrTxInvalidSignature = errors.New("error invalid signature") + "github.com/hyperledger/burrow/acm" + "github.com/hyperledger/burrow/execution/errors" ) type ErrTxInvalidSequence struct { - Got uint64 - Expected uint64 + Input *TxInput + Account acm.Account } func (e ErrTxInvalidSequence) Error() string { - return fmt.Sprintf("Error invalid sequence. Got %d, expected %d", e.Got, e.Expected) + return fmt.Sprintf("Error invalid sequence in input %v: input has sequence %d, but account has expected "+ + "%d, so expected input to have sequence %d", e.Input, e.Input.Sequence, e.Account.Sequence(), e.Account.Sequence()+1) +} + +func (e ErrTxInvalidSequence) ErrorCode() errors.Code { + return errors.ErrorCodeInvalidSequence } diff --git a/txs/payload/tx_input.go b/txs/payload/tx_input.go index 0a69fa2c401b79f76df1c659b835a66c1b094cef..ff02d7cf9e313130a94e15385c1a584c4bad720a 100644 --- a/txs/payload/tx_input.go +++ b/txs/payload/tx_input.go @@ -2,20 +2,8 @@ package payload import ( "fmt" - - "github.com/hyperledger/burrow/crypto" ) -func (txIn *TxInput) ValidateBasic() error { - if txIn.Address == crypto.ZeroAddress { - return ErrTxInvalidAddress - } - if txIn.Amount == 0 { - return ErrTxInvalidAmount - } - return nil -} - func (txIn *TxInput) String() string { return fmt.Sprintf("TxInput{%s, Amount: %v, Sequence:%v}", txIn.Address, txIn.Amount, txIn.Sequence) } diff --git a/txs/payload/tx_output.go b/txs/payload/tx_output.go index dcd6d73ce1604d1392e200a0d99aff62de51a31a..f32a29f7ae1af7b1c4a4c07e3bbf6c0f58bc3178 100644 --- a/txs/payload/tx_output.go +++ b/txs/payload/tx_output.go @@ -4,16 +4,6 @@ import ( "fmt" ) -func (txOut *TxOutput) ValidateBasic() error { - if len(txOut.Address) != 20 { - return ErrTxInvalidAddress - } - if txOut.Amount == 0 { - return ErrTxInvalidAmount - } - return nil -} - func (txOut *TxOutput) String() string { return fmt.Sprintf("TxOutput{%s, Amount: %v}", txOut.Address, txOut.Amount) }