accounts/abi: added FunctionType enumeration

This commit is contained in:
Marius van der Wijden 2020-04-16 16:49:26 +02:00
parent 6b6c912462
commit da57a13e20
4 changed files with 45 additions and 25 deletions

View file

@ -139,18 +139,18 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
for _, field := range fields { for _, field := range fields {
switch field.Type { switch field.Type {
case "constructor": 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 // empty defaults to function according to the abi spec
case "function": case "function":
name := abi.methodName(field.Name) 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": case "fallback":
// New introduced function type in v0.6.0, check more detail // New introduced function type in v0.6.0, check more detail
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function // here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
if abi.HasFallback() { if abi.HasFallback() {
return errors.New("only single fallback is allowed") 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": case "receive":
// New introduced function type in v0.6.0, check more detail // New introduced function type in v0.6.0, check more detail
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function // 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" { if field.StateMutability != "payable" {
return errors.New("the statemutability of receive can only be 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": case "event":
name := abi.eventName(field.Name) name := abi.eventName(field.Name)
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs) 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. // HasFallback returns an indicator whether a fallback function is included.
func (abi *ABI) HasFallback() bool { func (abi *ABI) HasFallback() bool {
return abi.Fallback.IsFallback return abi.Fallback.Type == Fallback
} }
// HasReceive returns an indicator whether a receive function is included. // HasReceive returns an indicator whether a receive function is included.
func (abi *ABI) HasReceive() bool { func (abi *ABI) HasReceive() bool {
return abi.Receive.IsReceive return abi.Receive.Type == Receive
} }

View file

@ -60,8 +60,8 @@ func TestReader(t *testing.T) {
Uint256, _ := NewType("uint256", "", nil) Uint256, _ := NewType("uint256", "", nil)
abi := ABI{ abi := ABI{
Methods: map[string]Method{ Methods: map[string]Method{
"balance": NewMethod("balance", "balance", "view", true, false, false, false, nil, nil), "balance": NewMethod("balance", "balance", Function, "view", false, false, nil, nil),
"send": NewMethod("send", "send", "", false, false, false, false, []Argument{{"amount", Uint256, false}}, 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) { func TestMethodSignature(t *testing.T) {
String, _ := NewType("string", "", nil) 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)" exp := "foo(string,string)"
if m.Sig != exp { if m.Sig != exp {
t.Error("signature mismatch", exp, "!=", m.Sig) t.Error("signature mismatch", exp, "!=", m.Sig)
@ -179,7 +179,7 @@ func TestMethodSignature(t *testing.T) {
} }
uintt, _ := NewType("uint256", "", nil) 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)" exp = "foo(uint256)"
if m.Sig != exp { if m.Sig != exp {
t.Error("signature mismatch", exp, "!=", m.Sig) t.Error("signature mismatch", exp, "!=", m.Sig)
@ -198,7 +198,7 @@ func TestMethodSignature(t *testing.T) {
{Name: "y", Type: "int256"}, {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)" exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
if m.Sig != exp { if m.Sig != exp {
t.Error("signature mismatch", exp, "!=", m.Sig) t.Error("signature mismatch", exp, "!=", m.Sig)

View file

@ -639,9 +639,9 @@ func formatMethod(method abi.Method, structs map[string]*tmplStruct) string {
state = state + " " state = state + " "
} }
identity := fmt.Sprintf("function %v", method.RawName) identity := fmt.Sprintf("function %v", method.RawName)
if method.IsFallback { if method.Type == abi.Fallback {
identity = "fallback" identity = "fallback"
} else if method.IsReceive { } else if method.Type == abi.Receive {
identity = "receive" identity = "receive"
} }
return fmt.Sprintf("%s(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", ")) return fmt.Sprintf("%s(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", "))

View file

@ -23,6 +23,24 @@ import (
"github.com/ethereum/go-ethereum/crypto" "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. // 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 // 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. // particular Method call. It can easily be simulated using a local VM.
@ -44,6 +62,10 @@ type Method struct {
Name string Name string
RawName string // RawName is the raw method name parsed from ABI 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, // StateMutability indicates the mutability state of method,
// the default value is nonpayable. It can be empty if the abi // the default value is nonpayable. It can be empty if the abi
// is generated by legacy compiler. // is generated by legacy compiler.
@ -53,11 +75,6 @@ type Method struct {
Constant bool Constant bool
Payable 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 Inputs Arguments
Outputs Arguments Outputs Arguments
str string str string
@ -74,8 +91,7 @@ type Method struct {
// A method should always be created using NewMethod. // A method should always be created using NewMethod.
// It also precomputes the sig representation and the string representation // It also precomputes the sig representation and the string representation
// of the method. // of the method.
// A method should always be created using NewMethod. func NewMethod(name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments) Method {
func NewMethod(name string, rawName string, mutability string, isConst, isPayable, isFallback, isReceive bool, inputs Arguments, outputs Arguments) Method {
// inputs // inputs
inputNames := make([]string, len(inputs)) inputNames := make([]string, len(inputs))
types := 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) 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. // Extract meaningful state mutability of solidity method.
// If it's default value, never print it. // If it's default value, never print it.
state := mutability state := mutability
@ -101,24 +119,26 @@ func NewMethod(name string, rawName string, mutability string, isConst, isPayabl
state = state + " " state = state + " "
} }
identity := fmt.Sprintf("function %v", rawName) identity := fmt.Sprintf("function %v", rawName)
if isFallback { if funType == Fallback {
identity = "fallback" identity = "fallback"
} else if isReceive { // The fallback function does not have any meaningful signature.
sig = ""
} else if funType == Receive {
identity = "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, ", ")) 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] id := crypto.Keccak256([]byte(sig))[:4]
method := Method{ method := Method{
Name: name, Name: name,
RawName: rawName, RawName: rawName,
Type: funType,
StateMutability: mutability, StateMutability: mutability,
Constant: isConst, Constant: isConst,
Payable: isPayable, Payable: isPayable,
IsFallback: isFallback,
IsReceive: isReceive,
Inputs: inputs, Inputs: inputs,
Outputs: outputs, Outputs: outputs,
str: str, str: str,