diff --git a/accounts/abi/abigen/bindv2.go b/accounts/abi/abigen/bindv2.go index ef4b769bb4..ad5b8a6aea 100644 --- a/accounts/abi/abigen/bindv2.go +++ b/accounts/abi/abigen/bindv2.go @@ -357,6 +357,7 @@ func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs "bindtopictype": bindTopicType, "capitalise": abi.ToCamelCase, "decapitalise": decapitalise, + "hasErrors": hasErrors, "ispointertype": isPointerType, "underlyingbindtype": underlyingBindType, } @@ -371,3 +372,12 @@ func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs } return string(code), nil } + +func hasErrors(contracts map[string]*tmplContractV2) bool { + for _, contract := range contracts { + if len(contract.Errors) > 0 { + return true + } + } + return false +} diff --git a/accounts/abi/abigen/source2.go.tpl b/accounts/abi/abigen/source2.go.tpl index 9285497511..deaa3fa044 100644 --- a/accounts/abi/abigen/source2.go.tpl +++ b/accounts/abi/abigen/source2.go.tpl @@ -4,7 +4,8 @@ package {{.Package}} import ( - "bytes" + "bytes"{{if hasErrors .Contracts}} + "fmt"{{end}} "math/big" "errors" @@ -16,7 +17,8 @@ import ( // Reference imports to suppress errors if they are not otherwise used. var ( - _ = bytes.Equal + _ = bytes.Equal{{if hasErrors .Contracts}} + _ = fmt.Sprintf{{end}} _ = errors.New _ = big.NewInt _ = common.Big1 @@ -218,6 +220,9 @@ var ( // UnpackError attempts to decode the provided error data using user-defined // error definitions. func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) UnpackError(raw []byte) (any, error) { + if len(raw) < 4 { + return nil, fmt.Errorf("insufficient error data: have %d, want at least 4", len(raw)) + } {{- range $k, $v := .Errors}} if bytes.Equal(raw[:4], {{ decapitalise $contract.Type}}.abi.Errors["{{.Normalized.Name}}"].ID.Bytes()[:4]) { return {{ decapitalise $contract.Type}}.Unpack{{.Normalized.Name}}Error(raw[4:]) diff --git a/accounts/abi/bind/v2/internal/contracts/solc_errors/bindings.go b/accounts/abi/bind/v2/internal/contracts/solc_errors/bindings.go index 12d8b7f3e9..7acdcdb9c9 100644 --- a/accounts/abi/bind/v2/internal/contracts/solc_errors/bindings.go +++ b/accounts/abi/bind/v2/internal/contracts/solc_errors/bindings.go @@ -6,6 +6,7 @@ package solc_errors import ( "bytes" "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum/accounts/abi" @@ -17,6 +18,7 @@ import ( // Reference imports to suppress errors if they are not otherwise used. var ( _ = bytes.Equal + _ = fmt.Sprintf _ = errors.New _ = big.NewInt _ = common.Big1 @@ -103,6 +105,9 @@ func (c *C) TryPackFoo() ([]byte, error) { // UnpackError attempts to decode the provided error data using user-defined // error definitions. func (c *C) UnpackError(raw []byte) (any, error) { + if len(raw) < 4 { + return nil, fmt.Errorf("insufficient error data: have %d, want at least 4", len(raw)) + } if bytes.Equal(raw[:4], c.abi.Errors["BadThing"].ID.Bytes()[:4]) { return c.UnpackBadThingError(raw[4:]) } @@ -223,6 +228,9 @@ func (c2 *C2) TryPackFoo() ([]byte, error) { // UnpackError attempts to decode the provided error data using user-defined // error definitions. func (c2 *C2) UnpackError(raw []byte) (any, error) { + if len(raw) < 4 { + return nil, fmt.Errorf("insufficient error data: have %d, want at least 4", len(raw)) + } if bytes.Equal(raw[:4], c2.abi.Errors["BadThing"].ID.Bytes()[:4]) { return c2.UnpackBadThingError(raw[4:]) } diff --git a/accounts/abi/bind/v2/lib_test.go b/accounts/abi/bind/v2/lib_test.go index 9903dbbc21..dd3f7e9119 100644 --- a/accounts/abi/bind/v2/lib_test.go +++ b/accounts/abi/bind/v2/lib_test.go @@ -368,6 +368,32 @@ func TestErrors(t *testing.T) { } } +func TestUnpackErrorRejectsMalformedData(t *testing.T) { + c := solc_errors.NewC() + selector := solc_errors.CBadThingErrorID() + tests := []struct { + name string + raw []byte + want string + }{ + {"nil data", nil, "insufficient error data: have 0, want at least 4"}, + {"short data", []byte{1, 2, 3}, "insufficient error data: have 3, want at least 4"}, + {"unknown selector", []byte{0, 1, 2, 3}, "Unknown error"}, + {"known selector with malformed payload", append(append([]byte{}, selector[:4]...), 0), ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := c.UnpackError(tt.raw) + if err == nil { + t.Fatal("expected an error") + } + if tt.want != "" && err.Error() != tt.want { + t.Fatalf("error: got %q, want %q", err, tt.want) + } + }) + } +} + func TestEventUnpackEmptyTopics(t *testing.T) { c := events.NewC()