diff --git a/errors/common.go b/errors/common.go index 73a0f6a507..fdc04ea8ad 100644 --- a/errors/common.go +++ b/errors/common.go @@ -16,21 +16,14 @@ import ( var ( errDecoding = rawerr.New("Error decoding input") errUnauthorized = rawerr.New("Unauthorized") - errInvalidAddress = rawerr.New("Invalid Address") - errInvalidCoins = rawerr.New("Invalid Coins") - errInvalidFormat = rawerr.New("Invalid Format") - errInvalidSequence = rawerr.New("Invalid Sequence") errInvalidSignature = rawerr.New("Invalid Signature") - errInsufficientFees = rawerr.New("Insufficient Fees") - errInsufficientFunds = rawerr.New("Insufficient Funds") - errNoInputs = rawerr.New("No Input Coins") - errNoOutputs = rawerr.New("No Output Coins") errTooLarge = rawerr.New("Input size too large") errMissingSignature = rawerr.New("Signature missing") errTooManySignatures = rawerr.New("Too many signatures") errNoChain = rawerr.New("No chain id provided") errWrongChain = rawerr.New("Wrong chain for tx") errUnknownTxType = rawerr.New("Tx type unknown") + errInvalidFormat = rawerr.New("Invalid format") ) func ErrUnknownTxType(tx basecoin.Tx) TMError { @@ -38,11 +31,19 @@ func ErrUnknownTxType(tx basecoin.Tx) TMError { w := errors.Wrap(errUnknownTxType, msg) return WithCode(w, abci.CodeType_UnknownRequest) } - func IsUnknownTxTypeErr(err error) bool { return IsSameError(errUnknownTxType, err) } +func ErrInvalidFormat(tx basecoin.Tx) TMError { + msg := fmt.Sprintf("%T", tx.Unwrap()) + w := errors.Wrap(errInvalidFormat, msg) + return WithCode(w, abci.CodeType_UnknownRequest) +} +func IsInvalidFormatErr(err error) bool { + return IsSameError(errInvalidFormat, err) +} + func ErrInternal(msg string) TMError { return New(msg, abci.CodeType_InternalError) } @@ -55,7 +56,6 @@ func IsInternalErr(err error) bool { func ErrDecoding() TMError { return WithCode(errDecoding, abci.CodeType_EncodingError) } - func IsDecodingErr(err error) bool { return IsSameError(errDecoding, err) } @@ -73,7 +73,6 @@ func IsUnauthorizedErr(err error) bool { func ErrMissingSignature() TMError { return WithCode(errMissingSignature, abci.CodeType_Unauthorized) } - func IsMissingSignatureErr(err error) bool { return IsSameError(errMissingSignature, err) } @@ -81,7 +80,6 @@ func IsMissingSignatureErr(err error) bool { func ErrTooManySignatures() TMError { return WithCode(errTooManySignatures, abci.CodeType_Unauthorized) } - func IsTooManySignaturesErr(err error) bool { return IsSameError(errTooManySignatures, err) } @@ -89,7 +87,6 @@ func IsTooManySignaturesErr(err error) bool { func ErrInvalidSignature() TMError { return WithCode(errInvalidSignature, abci.CodeType_Unauthorized) } - func IsInvalidSignatureErr(err error) bool { return IsSameError(errInvalidSignature, err) } @@ -97,7 +94,6 @@ func IsInvalidSignatureErr(err error) bool { func ErrNoChain() TMError { return WithCode(errNoChain, abci.CodeType_Unauthorized) } - func IsNoChainErr(err error) bool { return IsSameError(errNoChain, err) } @@ -106,47 +102,13 @@ func ErrWrongChain(chain string) TMError { msg := errors.Wrap(errWrongChain, chain) return WithCode(msg, abci.CodeType_Unauthorized) } - func IsWrongChainErr(err error) bool { return IsSameError(errWrongChain, err) } -func InvalidAddress() TMError { - return WithCode(errInvalidAddress, abci.CodeType_BaseInvalidInput) -} - -func InvalidCoins() TMError { - return WithCode(errInvalidCoins, abci.CodeType_BaseInvalidInput) -} - -func InvalidFormat() TMError { - return WithCode(errInvalidFormat, abci.CodeType_BaseInvalidInput) -} - -func InvalidSequence() TMError { - return WithCode(errInvalidSequence, abci.CodeType_BaseInvalidInput) -} - -func InsufficientFees() TMError { - return WithCode(errInsufficientFees, abci.CodeType_BaseInvalidInput) -} - -func InsufficientFunds() TMError { - return WithCode(errInsufficientFunds, abci.CodeType_BaseInvalidInput) -} - -func NoInputs() TMError { - return WithCode(errNoInputs, abci.CodeType_BaseInvalidInput) -} - -func NoOutputs() TMError { - return WithCode(errNoOutputs, abci.CodeType_BaseInvalidOutput) -} - func ErrTooLarge() TMError { return WithCode(errTooLarge, abci.CodeType_EncodingError) } - func IsTooLargeErr(err error) bool { return IsSameError(errTooLarge, err) } diff --git a/modules/coin/errors.go b/modules/coin/errors.go new file mode 100644 index 0000000000..d9b2fea6c0 --- /dev/null +++ b/modules/coin/errors.go @@ -0,0 +1,88 @@ +package coin + +import ( + rawerr "errors" + + abci "github.com/tendermint/abci/types" + "github.com/tendermint/basecoin/errors" +) + +var ( + errNoAccount = rawerr.New("No such account") + errInsufficientFunds = rawerr.New("Insufficient Funds") + errNoInputs = rawerr.New("No Input Coins") + errNoOutputs = rawerr.New("No Output Coins") + errInvalidAddress = rawerr.New("Invalid Address") + errInvalidCoins = rawerr.New("Invalid Coins") + errInvalidSequence = rawerr.New("Invalid Sequence") +) + +var ( + invalidInput = abci.CodeType_BaseInvalidInput + invalidOutput = abci.CodeType_BaseInvalidOutput + unknownAddress = abci.CodeType_BaseUnknownAddress +) + +// here are some generic handlers to grab classes of errors based on code +func IsInputErr(err error) bool { + return errors.HasErrorCode(err, invalidInput) +} +func IsOutputErr(err error) bool { + return errors.HasErrorCode(err, invalidOutput) +} +func IsAddressErr(err error) bool { + return errors.HasErrorCode(err, unknownAddress) +} +func IsCoinErr(err error) bool { + return err != nil && (IsInputErr(err) || IsOutputErr(err) || IsAddressErr(err)) +} + +func ErrNoAccount() errors.TMError { + return errors.WithCode(errNoAccount, unknownAddress) +} + +func IsNoAccountErr(err error) bool { + return errors.IsSameError(errNoAccount, err) +} + +func ErrInvalidAddress() errors.TMError { + return errors.WithCode(errInvalidAddress, invalidInput) +} +func IsInvalidAddressErr(err error) bool { + return errors.IsSameError(errInvalidAddress, err) +} + +func ErrInvalidCoins() errors.TMError { + return errors.WithCode(errInvalidCoins, invalidInput) +} +func IsInvalidCoinsErr(err error) bool { + return errors.IsSameError(errInvalidCoins, err) +} + +func ErrInvalidSequence() errors.TMError { + return errors.WithCode(errInvalidSequence, invalidInput) +} +func IsInvalidSequenceErr(err error) bool { + return errors.IsSameError(errInvalidSequence, err) +} + +func ErrInsufficientFunds() errors.TMError { + return errors.WithCode(errInsufficientFunds, invalidInput) +} +func IsInsufficientFundsErr(err error) bool { + return errors.IsSameError(errInsufficientFunds, err) +} + +func ErrNoInputs() errors.TMError { + return errors.WithCode(errNoInputs, invalidInput) +} +func IsNoInputsErr(err error) bool { + return errors.IsSameError(errNoInputs, err) +} + +func ErrNoOutputs() errors.TMError { + return errors.WithCode(errNoOutputs, invalidOutput) +} +func IsNoOutputsErr(err error) bool { + return errors.IsSameError(errNoOutputs, err) +} diff --git a/modules/coin/handler.go b/modules/coin/handler.go index 7947283084..0e1740f1c1 100644 --- a/modules/coin/handler.go +++ b/modules/coin/handler.go @@ -11,10 +11,18 @@ const ( ) // Handler writes -type Handler struct{} +type Handler struct { + Accountant +} var _ basecoin.Handler = Handler{} +func NewHandler() Handler { + return Handler{ + Accountant: Accountant{Prefix: []byte(NameCoin + "/")}, + } +} + func (_ Handler) Name() string { return NameCoin } @@ -47,7 +55,7 @@ func checkTx(ctx basecoin.Context, tx basecoin.Tx) (send SendTx, err error) { // check if the tx is proper type and valid send, ok := tx.Unwrap().(SendTx) if !ok { - return send, errors.UnknownTxType(tx) + return send, errors.ErrInvalidFormat(tx) } err = send.ValidateBasic() if err != nil { @@ -57,7 +65,7 @@ func checkTx(ctx basecoin.Context, tx basecoin.Tx) (send SendTx, err error) { // check if all inputs have permission for _, in := range send.Inputs { if !ctx.HasPermission(in.Address) { - return send, errors.Unauthorized() + return send, errors.ErrUnauthorized() } } return send, nil diff --git a/modules/coin/handler_test.go b/modules/coin/handler_test.go index f9a4c490bb..52560f128b 100644 --- a/modules/coin/handler_test.go +++ b/modules/coin/handler_test.go @@ -12,7 +12,7 @@ import ( func TestHandlerPermissions(t *testing.T) { assert := assert.New(t) // TODO: need to update this when we actually have token store - h := Handler{} + h := NewHandler() // these are all valid, except for minusCoins addr1 := basecoin.Actor{App: "coin", Address: []byte{1, 2}} diff --git a/modules/coin/store.go b/modules/coin/store.go index 2b217d4f01..bede0c009c 100644 --- a/modules/coin/store.go +++ b/modules/coin/store.go @@ -15,8 +15,12 @@ type Accountant struct { } func (a Accountant) GetAccount(store types.KVStore, addr basecoin.Actor) (Account, error) { - // TODO: how to handle empty accounts?? - return loadAccount(store, a.makeKey(addr)) + acct, err := loadAccount(store, a.makeKey(addr)) + // for empty accounts, don't return an error, but rather an empty account + if IsNoAccountErr(err) { + err = nil + } + return acct, err } // CheckCoins makes sure there are funds, but doesn't change anything @@ -47,14 +51,14 @@ func (a Accountant) updateCoins(store types.KVStore, addr basecoin.Actor, coins // check sequence if seq != acct.Sequence+1 { - return acct, errors.InvalidSequence() + return acct, ErrInvalidSequence() } acct.Sequence += 1 // check amount final := acct.Coins.Minus(coins) if !final.IsNonnegative() { - return acct, errors.InsufficientFunds() + return acct, ErrInsufficientFunds() } acct.Coins = final @@ -77,13 +81,12 @@ type Account struct { func loadAccount(store types.KVStore, key []byte) (acct Account, err error) { data := store.Get(key) if len(data) == 0 { - // TODO: error or empty???? - return acct, errors.InternalError("No account found") + return acct, ErrNoAccount() } err = wire.ReadBinaryBytes(data, &acct) if err != nil { msg := fmt.Sprintf("Error reading account %X", key) - return acct, errors.InternalError(msg) + return acct, errors.ErrInternal(msg) } return acct, nil } diff --git a/modules/coin/tx.go b/modules/coin/tx.go index 711362dfcd..e40967e48e 100644 --- a/modules/coin/tx.go +++ b/modules/coin/tx.go @@ -5,7 +5,6 @@ import ( "github.com/tendermint/basecoin" - "github.com/tendermint/basecoin/errors" "github.com/tendermint/basecoin/types" ) @@ -29,20 +28,20 @@ type TxInput struct { func (txIn TxInput) ValidateBasic() error { if txIn.Address.App == "" { - return errors.InvalidAddress() + return ErrInvalidAddress() } // TODO: knowledge of app-specific codings? if len(txIn.Address.Address) == 0 { - return errors.InvalidAddress() + return ErrInvalidAddress() } if !txIn.Coins.IsValid() { - return errors.InvalidCoins() + return ErrInvalidCoins() } if !txIn.Coins.IsPositive() { - return errors.InvalidCoins() + return ErrInvalidCoins() } if txIn.Sequence <= 0 { - return errors.InvalidSequence() + return ErrInvalidSequence() } return nil } @@ -69,17 +68,17 @@ type TxOutput struct { func (txOut TxOutput) ValidateBasic() error { if txOut.Address.App == "" { - return errors.InvalidAddress() + return ErrInvalidAddress() } // TODO: knowledge of app-specific codings? if len(txOut.Address.Address) == 0 { - return errors.InvalidAddress() + return ErrInvalidAddress() } if !txOut.Coins.IsValid() { - return errors.InvalidCoins() + return ErrInvalidCoins() } if !txOut.Coins.IsPositive() { - return errors.InvalidCoins() + return ErrInvalidCoins() } return nil } @@ -109,10 +108,10 @@ func (tx SendTx) ValidateBasic() error { // this just makes sure all the inputs and outputs are properly formatted, // not that they actually have the money inside if len(tx.Inputs) == 0 { - return errors.NoInputs() + return ErrNoInputs() } if len(tx.Outputs) == 0 { - return errors.NoOutputs() + return ErrNoOutputs() } // make sure all inputs and outputs are individually valid var totalIn, totalOut types.Coins @@ -130,7 +129,7 @@ func (tx SendTx) ValidateBasic() error { } // make sure inputs and outputs match if !totalIn.IsEqual(totalOut) { - return errors.InvalidCoins() + return ErrInvalidCoins() } return nil } diff --git a/modules/fee/errors.go b/modules/fee/errors.go new file mode 100644 index 0000000000..0b6d7fcaf8 --- /dev/null +++ b/modules/fee/errors.go @@ -0,0 +1,19 @@ +package fee + +import ( + rawerr "errors" + + abci "github.com/tendermint/abci/types" + "github.com/tendermint/basecoin/errors" +) + +var ( + errInsufficientFees = rawerr.New("Insufficient Fees") +) + +func ErrInsufficientFees() errors.TMError { + return errors.WithCode(errInsufficientFees, abci.CodeType_BaseInvalidInput) +} +func IsInsufficientFeesErr(err error) bool { + return errors.IsSameError(errInsufficientFees, err) +} diff --git a/modules/fee/handler.go b/modules/fee/handler.go index 18b82812b7..1b4556ef4e 100644 --- a/modules/fee/handler.go +++ b/modules/fee/handler.go @@ -36,16 +36,16 @@ var _ stack.Middleware = SimpleFeeHandler{} func (h SimpleFeeHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) { feeTx, ok := tx.Unwrap().(*Fee) if !ok { - return res, errors.InvalidFormat() + return res, errors.ErrInvalidFormat(tx) } fees := types.Coins{feeTx.Fee} if !fees.IsGTE(h.MinFee) { - return res, errors.InsufficientFees() + return res, ErrInsufficientFees() } if !ctx.HasPermission(feeTx.Payer) { - return res, errors.Unauthorized() + return res, errors.ErrUnauthorized() } _, err = h.ChangeAmount(store, feeTx.Payer, fees.Negative()) @@ -59,16 +59,16 @@ func (h SimpleFeeHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx func (h SimpleFeeHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) { feeTx, ok := tx.Unwrap().(*Fee) if !ok { - return res, errors.InvalidFormat() + return res, errors.ErrInvalidFormat(tx) } fees := types.Coins{feeTx.Fee} if !fees.IsGTE(h.MinFee) { - return res, errors.InsufficientFees() + return res, ErrInsufficientFees() } if !ctx.HasPermission(feeTx.Payer) { - return res, errors.Unauthorized() + return res, errors.ErrUnauthorized() } _, err = h.ChangeAmount(store, feeTx.Payer, fees.Negative()) diff --git a/stack/chain.go b/stack/chain.go index f351c24864..ef174495b8 100644 --- a/stack/chain.go +++ b/stack/chain.go @@ -42,10 +42,10 @@ func (c Chain) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin. func (c Chain) checkChain(tx basecoin.Tx) (basecoin.Tx, error) { ctx, ok := tx.Unwrap().(*txs.Chain) if !ok { - return tx, errors.NoChain() + return tx, errors.ErrNoChain() } if ctx.ChainID != c.ChainID { - return tx, errors.WrongChain(ctx.ChainID) + return tx, errors.ErrWrongChain(ctx.ChainID) } return ctx.Tx, nil } diff --git a/stack/chain_test.go b/stack/chain_test.go index b41bd0b57b..a17b6bd481 100644 --- a/stack/chain_test.go +++ b/stack/chain_test.go @@ -25,7 +25,7 @@ func TestChain(t *testing.T) { errorMsg string }{ {txs.NewChain(chainID, raw).Wrap(), true, ""}, - {txs.NewChain("someone-else", raw).Wrap(), false, "Tx belongs to different chain - someone-else"}, + {txs.NewChain("someone-else", raw).Wrap(), false, "someone-else"}, {raw, false, "No chain id provided"}, } @@ -47,7 +47,7 @@ func TestChain(t *testing.T) { assert.Equal(msg, res.Log, i) } else { if assert.NotNil(err, i) { - assert.Equal(tc.errorMsg, err.Error(), i) + assert.Contains(err.Error(), tc.errorMsg, i) } } @@ -58,7 +58,7 @@ func TestChain(t *testing.T) { assert.Equal(msg, res.Log, i) } else { if assert.NotNil(err, i) { - assert.Equal(tc.errorMsg, err.Error(), i) + assert.Contains(err.Error(), tc.errorMsg, i) } } } diff --git a/stack/helperware.go b/stack/helperware.go index 15201b892f..ec2282ac84 100644 --- a/stack/helperware.go +++ b/stack/helperware.go @@ -25,14 +25,14 @@ func (_ CheckMiddleware) Name() string { func (p CheckMiddleware) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) { if !ctx.HasPermission(p.Required) { - return res, errors.Unauthorized() + return res, errors.ErrUnauthorized() } return next.CheckTx(ctx, store, tx) } func (p CheckMiddleware) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) { if !ctx.HasPermission(p.Required) { - return res, errors.Unauthorized() + return res, errors.ErrUnauthorized() } return next.DeliverTx(ctx, store, tx) } diff --git a/stack/middleware_test.go b/stack/middleware_test.go index 82f802ebf4..eed6a183e5 100644 --- a/stack/middleware_test.go +++ b/stack/middleware_test.go @@ -31,12 +31,12 @@ func TestPermissionSandbox(t *testing.T) { grant basecoin.Actor require basecoin.Actor expectedRes data.Bytes - expectedErr error + expected func(error) bool }{ {grantee, grantee, rawBytes, nil}, - {grantee, grantee2, nil, errors.Unauthorized()}, - {grantee, signer, nil, errors.Unauthorized()}, - {signer, signer, nil, errors.InternalError("panic")}, + {grantee, grantee2, nil, errors.IsUnauthorizedErr}, + {grantee, signer, nil, errors.IsUnauthorizedErr}, + {signer, signer, nil, errors.IsInternalErr}, } for i, tc := range cases { @@ -47,24 +47,22 @@ func TestPermissionSandbox(t *testing.T) { ).Use(EchoHandler{}) res, err := app.CheckTx(ctx, store, raw) - checkPerm(t, i, tc.expectedRes, tc.expectedErr, res, err) + checkPerm(t, i, tc.expectedRes, tc.expected, res, err) res, err = app.DeliverTx(ctx, store, raw) - checkPerm(t, i, tc.expectedRes, tc.expectedErr, res, err) + checkPerm(t, i, tc.expectedRes, tc.expected, res, err) } } -func checkPerm(t *testing.T, idx int, data []byte, expected error, res basecoin.Result, err error) { +func checkPerm(t *testing.T, idx int, data []byte, check func(error) bool, res basecoin.Result, err error) { assert := assert.New(t) - if expected == nil { + if len(data) > 0 { assert.Nil(err, "%d: %+v", idx, err) assert.EqualValues(data, res.Data) } else { assert.NotNil(err, "%d", idx) // check error code! - shouldCode := errors.Wrap(expected).ErrorCode() - isCode := errors.Wrap(err).ErrorCode() - assert.Equal(shouldCode, isCode, "%d: %+v", idx, err) + assert.True(check(err), "%d: %+v", idx, err) } } diff --git a/stack/recovery.go b/stack/recovery.go index a572534fb2..b6e84496c6 100644 --- a/stack/recovery.go +++ b/stack/recovery.go @@ -45,5 +45,5 @@ func normalizePanic(p interface{}) error { return errors.Wrap(err) } msg := fmt.Sprintf("%v", p) - return errors.InternalError(msg) + return errors.ErrInternal(msg) } diff --git a/stack/signature.go b/stack/signature.go index 13846e516a..049351dd10 100644 --- a/stack/signature.go +++ b/stack/signature.go @@ -61,7 +61,7 @@ func addSigners(ctx basecoin.Context, sigs []crypto.PubKey) basecoin.Context { func getSigners(tx basecoin.Tx) ([]crypto.PubKey, basecoin.Tx, error) { stx, ok := tx.Unwrap().(Signed) if !ok { - return nil, basecoin.Tx{}, errors.Unauthorized() + return nil, basecoin.Tx{}, errors.ErrUnauthorized() } sig, err := stx.Signers() return sig, stx.Next(), err diff --git a/txs/base.go b/txs/base.go index c2463b83c9..fcbb17899a 100644 --- a/txs/base.go +++ b/txs/base.go @@ -50,7 +50,7 @@ func (r Raw) Wrap() basecoin.Tx { func (r Raw) ValidateBasic() error { if len(r.Bytes) > rawMaxSize { - return errors.TooLarge() + return errors.ErrTooLarge() } return nil } diff --git a/txs/sigs.go b/txs/sigs.go index 5550f262f9..699b6aa520 100644 --- a/txs/sigs.go +++ b/txs/sigs.go @@ -66,7 +66,7 @@ func (s *OneSig) Next() basecoin.Tx { func (s *OneSig) ValidateBasic() error { // TODO: VerifyBytes here, we do it in Signers? if s.Empty() || !s.Pubkey.VerifyBytes(s.SignBytes(), s.Sig) { - return errors.Unauthorized() + return errors.ErrUnauthorized() } return s.Tx.ValidateBasic() } @@ -92,10 +92,10 @@ func (s *OneSig) SignBytes() []byte { func (s *OneSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { signed := Signed{sig, pubkey} if signed.Empty() { - return errors.MissingSignature() + return errors.ErrMissingSignature() } if !s.Empty() { - return errors.TooManySignatures() + return errors.ErrTooManySignatures() } // set the value once we are happy s.Signed = signed @@ -107,10 +107,10 @@ func (s *OneSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { // including if there are no signatures func (s *OneSig) Signers() ([]crypto.PubKey, error) { if s.Empty() { - return nil, errors.MissingSignature() + return nil, errors.ErrMissingSignature() } if !s.Pubkey.VerifyBytes(s.SignBytes(), s.Sig) { - return nil, errors.InvalidSignature() + return nil, errors.ErrInvalidSignature() } return []crypto.PubKey{s.Pubkey}, nil } @@ -168,7 +168,7 @@ func (s *MultiSig) SignBytes() []byte { func (s *MultiSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { signed := Signed{sig, pubkey} if signed.Empty() { - return errors.MissingSignature() + return errors.ErrMissingSignature() } // set the value once we are happy s.Sigs = append(s.Sigs, signed) @@ -180,7 +180,7 @@ func (s *MultiSig) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { // including if there are no signatures func (s *MultiSig) Signers() ([]crypto.PubKey, error) { if len(s.Sigs) == 0 { - return nil, errors.MissingSignature() + return nil, errors.ErrMissingSignature() } // verify all the signatures before returning them keys := make([]crypto.PubKey, len(s.Sigs)) @@ -188,7 +188,7 @@ func (s *MultiSig) Signers() ([]crypto.PubKey, error) { for i := range s.Sigs { ms := s.Sigs[i] if !ms.Pubkey.VerifyBytes(data, ms.Sig) { - return nil, errors.InvalidSignature() + return nil, errors.ErrInvalidSignature() } keys[i] = ms.Pubkey }