From da57a13e20b860e1410a8b2ed52e65d6e4e861ef Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 16 Apr 2020 16:49:26 +0200 Subject: [PATCH] accounts/abi: added FunctionType enumeration --- accounts/abi/abi.go | 12 +++++------ accounts/abi/abi_test.go | 10 ++++----- accounts/abi/bind/bind.go | 4 ++-- accounts/abi/method.go | 44 ++++++++++++++++++++++++++++----------- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 7953abd717..7ed6794ef3 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -139,18 +139,18 @@ func (abi *ABI) UnmarshalJSON(data []byte) error { for _, field := range fields { switch field.Type { case "constructor": - abi.Constructor = NewMethod("", "", field.StateMutability, field.Constant, field.Payable, false, false, field.Inputs, nil) + abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil) // empty defaults to function according to the abi spec case "function": name := abi.methodName(field.Name) - abi.Methods[name] = NewMethod(name, field.Name, field.StateMutability, field.Constant, field.Payable, false, false, field.Inputs, field.Outputs) + abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs) case "fallback": // New introduced function type in v0.6.0, check more detail // here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function if abi.HasFallback() { return errors.New("only single fallback is allowed") } - abi.Fallback = NewMethod("", "", field.StateMutability, field.Constant, field.Payable, true, false, nil, nil) + abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil) case "receive": // New introduced function type in v0.6.0, check more detail // here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function @@ -160,7 +160,7 @@ func (abi *ABI) UnmarshalJSON(data []byte) error { if field.StateMutability != "payable" { return errors.New("the statemutability of receive can only be payable") } - abi.Receive = NewMethod("", "", field.StateMutability, field.Constant, field.Payable, false, true, nil, nil) + abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil) case "event": name := abi.eventName(field.Name) abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs) @@ -226,10 +226,10 @@ func (abi *ABI) EventByID(topic common.Hash) (*Event, error) { // HasFallback returns an indicator whether a fallback function is included. func (abi *ABI) HasFallback() bool { - return abi.Fallback.IsFallback + return abi.Fallback.Type == Fallback } // HasReceive returns an indicator whether a receive function is included. func (abi *ABI) HasReceive() bool { - return abi.Receive.IsReceive + return abi.Receive.Type == Receive } diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 8c184e6430..509040e5d5 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -60,8 +60,8 @@ func TestReader(t *testing.T) { Uint256, _ := NewType("uint256", "", nil) abi := ABI{ Methods: map[string]Method{ - "balance": NewMethod("balance", "balance", "view", true, false, false, false, nil, nil), - "send": NewMethod("send", "send", "", false, false, false, false, []Argument{{"amount", Uint256, false}}, nil), + "balance": NewMethod("balance", "balance", Function, "view", false, false, nil, nil), + "send": NewMethod("send", "send", Function, "", false, false, []Argument{{"amount", Uint256, false}}, nil), }, } @@ -167,7 +167,7 @@ func TestTestSlice(t *testing.T) { func TestMethodSignature(t *testing.T) { String, _ := NewType("string", "", nil) - m := NewMethod("foo", "foo", "", false, false, false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil) + m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil) exp := "foo(string,string)" if m.Sig != exp { t.Error("signature mismatch", exp, "!=", m.Sig) @@ -179,7 +179,7 @@ func TestMethodSignature(t *testing.T) { } uintt, _ := NewType("uint256", "", nil) - m = NewMethod("foo", "foo", "", false, false, false, false, []Argument{{"bar", uintt, false}}, nil) + m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", uintt, false}}, nil) exp = "foo(uint256)" if m.Sig != exp { t.Error("signature mismatch", exp, "!=", m.Sig) @@ -198,7 +198,7 @@ func TestMethodSignature(t *testing.T) { {Name: "y", Type: "int256"}, }}, }) - m = NewMethod("foo", "foo", "", false, false, false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil) + m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil) exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)" if m.Sig != exp { t.Error("signature mismatch", exp, "!=", m.Sig) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index c98f8b4d4c..4c6a9e9ce1 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -639,9 +639,9 @@ func formatMethod(method abi.Method, structs map[string]*tmplStruct) string { state = state + " " } identity := fmt.Sprintf("function %v", method.RawName) - if method.IsFallback { + if method.Type == abi.Fallback { identity = "fallback" - } else if method.IsReceive { + } else if method.Type == abi.Receive { identity = "receive" } return fmt.Sprintf("%s(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", ")) diff --git a/accounts/abi/method.go b/accounts/abi/method.go index 95beda4007..72880b8ddb 100644 --- a/accounts/abi/method.go +++ b/accounts/abi/method.go @@ -23,6 +23,24 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) +// FunctionType represents different types of functions a contract might have. +type FunctionType int + +const ( + // Constructor represents the constructor of the contract. + // The constructor function is called while deploying a contract. + Constructor FunctionType = iota + // Fallback represents the fallback function. + // This function is executed if no other function matches the given function + // signature and no receive function is specified. + Fallback + // Receive represents the receive function. + // This function is executed on plain Ether transfers. + Receive + // Function represents a normal function. + Function +) + // Method represents a callable given a `Name` and whether the method is a constant. // If the method is `Const` no transaction needs to be created for this // particular Method call. It can easily be simulated using a local VM. @@ -44,6 +62,10 @@ type Method struct { Name string RawName string // RawName is the raw method name parsed from ABI + // Type indicates whether the method is a + // special fallback introduced in solidity v0.6.0 + Type FunctionType + // StateMutability indicates the mutability state of method, // the default value is nonpayable. It can be empty if the abi // is generated by legacy compiler. @@ -53,11 +75,6 @@ type Method struct { Constant bool Payable bool - // The following two flags indicates whether the method is a - // special fallback introduced in solidity v0.6.0 - IsFallback bool - IsReceive bool - Inputs Arguments Outputs Arguments str string @@ -74,8 +91,7 @@ type Method struct { // A method should always be created using NewMethod. // It also precomputes the sig representation and the string representation // of the method. -// A method should always be created using NewMethod. -func NewMethod(name string, rawName string, mutability string, isConst, isPayable, isFallback, isReceive bool, inputs Arguments, outputs Arguments) Method { +func NewMethod(name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments) Method { // inputs inputNames := make([]string, len(inputs)) types := make([]string, len(inputs)) @@ -91,6 +107,8 @@ func NewMethod(name string, rawName string, mutability string, isConst, isPayabl outputNames[i] += fmt.Sprintf(" %v", output.Name) } } + // calculate the signature. + sig := fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ",")) // Extract meaningful state mutability of solidity method. // If it's default value, never print it. state := mutability @@ -101,24 +119,26 @@ func NewMethod(name string, rawName string, mutability string, isConst, isPayabl state = state + " " } identity := fmt.Sprintf("function %v", rawName) - if isFallback { + if funType == Fallback { identity = "fallback" - } else if isReceive { + // The fallback function does not have any meaningful signature. + sig = "" + } else if funType == Receive { identity = "receive" + // The receive function does not have any meaningful signature. + sig = "" } str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", ")) - sig := fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ",")) id := crypto.Keccak256([]byte(sig))[:4] method := Method{ Name: name, RawName: rawName, + Type: funType, StateMutability: mutability, Constant: isConst, Payable: isPayable, - IsFallback: isFallback, - IsReceive: isReceive, Inputs: inputs, Outputs: outputs, str: str,