mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
accounts/abi: prevent recalculation of ID, Sig and String
This commit is contained in:
parent
00064ddcfb
commit
ad60f0cec0
3 changed files with 65 additions and 78 deletions
|
|
@ -139,59 +139,24 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
|||
for _, field := range fields {
|
||||
switch field.Type {
|
||||
case "constructor":
|
||||
abi.Constructor = Method{
|
||||
Inputs: field.Inputs,
|
||||
|
||||
// Note for constructor the `StateMutability` can only
|
||||
// be payable or nonpayable according to the output of
|
||||
// compiler. So constant is always false.
|
||||
StateMutability: field.StateMutability,
|
||||
|
||||
// Legacy fields, keep them for backward compatibility
|
||||
Constant: field.Constant,
|
||||
Payable: field.Payable,
|
||||
}
|
||||
case "function":
|
||||
abi.Constructor = NewMethod("", "", field.StateMutability, field.Constant, field.Payable, false, false, field.Inputs, nil)
|
||||
// empty defaults to function according to the abi spec
|
||||
case "function", "":
|
||||
name := field.Name
|
||||
_, ok := abi.Methods[name]
|
||||
for idx := 0; ok; idx++ {
|
||||
name = fmt.Sprintf("%s%d", field.Name, idx)
|
||||
_, ok = abi.Methods[name]
|
||||
}
|
||||
abi.Methods[name] = Method{
|
||||
Name: name,
|
||||
RawName: field.Name,
|
||||
StateMutability: field.StateMutability,
|
||||
Inputs: field.Inputs,
|
||||
Outputs: field.Outputs,
|
||||
|
||||
// Legacy fields, keep them for backward compatibility
|
||||
Constant: field.Constant,
|
||||
Payable: field.Payable,
|
||||
}
|
||||
isConst := field.Constant || field.StateMutability == "pure" || field.StateMutability == "view"
|
||||
abi.Methods[name] = NewMethod(name, field.Name, field.StateMutability, isConst, field.Payable, false, false, 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 = Method{
|
||||
Name: "",
|
||||
RawName: "",
|
||||
|
||||
// The `StateMutability` can only be payable or nonpayable,
|
||||
// so the constant is always false.
|
||||
StateMutability: field.StateMutability,
|
||||
IsFallback: true,
|
||||
|
||||
// Fallback doesn't have any input or output
|
||||
Inputs: nil,
|
||||
Outputs: nil,
|
||||
|
||||
// Legacy fields, keep them for backward compatibility
|
||||
Constant: field.Constant,
|
||||
Payable: field.Payable,
|
||||
}
|
||||
abi.Fallback = NewMethod("", "", field.StateMutability, field.Constant, field.Payable, true, false, 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
|
||||
|
|
@ -201,23 +166,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 = Method{
|
||||
Name: "",
|
||||
RawName: "",
|
||||
|
||||
// The `StateMutability` can only be payable, so constant
|
||||
// is always true while payable is always false.
|
||||
StateMutability: field.StateMutability,
|
||||
IsReceive: true,
|
||||
|
||||
// Receive doesn't have any input or output
|
||||
Inputs: nil,
|
||||
Outputs: nil,
|
||||
|
||||
// Legacy fields, keep them for backward compatibility
|
||||
Constant: field.Constant,
|
||||
Payable: field.Payable,
|
||||
}
|
||||
abi.Receive = NewMethod("", "", "payable", field.Constant, field.Payable, false, true, nil, nil)
|
||||
case "event":
|
||||
name := field.Name
|
||||
_, ok := abi.Events[name]
|
||||
|
|
|
|||
|
|
@ -58,20 +58,14 @@ const jsondata2 = `
|
|||
|
||||
func TestReader(t *testing.T) {
|
||||
Uint256, _ := NewType("uint256", "", nil)
|
||||
exp := ABI{
|
||||
abi := ABI{
|
||||
Methods: map[string]Method{
|
||||
"balance": {
|
||||
"balance", "balance", "view", false, false, false, false, nil, nil,
|
||||
},
|
||||
"send": {
|
||||
"send", "send", "", false, false, false, false, []Argument{
|
||||
{"amount", Uint256, false},
|
||||
}, nil,
|
||||
},
|
||||
"balance": NewMethod("balance", "balance", "view", true, false, false, false, nil, nil),
|
||||
"send": NewMethod("send", "send", "", false, false, false, false, []Argument{{"amount", Uint256, false}}, nil),
|
||||
},
|
||||
}
|
||||
|
||||
abi, err := JSON(strings.NewReader(jsondata))
|
||||
exp, err := JSON(strings.NewReader(jsondata))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
|
@ -173,7 +167,7 @@ func TestTestSlice(t *testing.T) {
|
|||
|
||||
func TestMethodSignature(t *testing.T) {
|
||||
String, _ := NewType("string", "", nil)
|
||||
m := Method{"foo", "foo", "", false, false, false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
|
||||
m := NewMethod("foo", "foo", "", false, false, 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())
|
||||
|
|
@ -185,7 +179,7 @@ func TestMethodSignature(t *testing.T) {
|
|||
}
|
||||
|
||||
uintt, _ := NewType("uint256", "", nil)
|
||||
m = Method{"foo", "foo", "", false, false, false, false, []Argument{{"bar", uintt, false}}, nil}
|
||||
m = NewMethod("foo", "foo", "", false, false, false, false, []Argument{{"bar", uintt, false}}, nil)
|
||||
exp = "foo(uint256)"
|
||||
if m.Sig() != exp {
|
||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
||||
|
|
@ -204,7 +198,7 @@ func TestMethodSignature(t *testing.T) {
|
|||
{Name: "y", Type: "int256"},
|
||||
}},
|
||||
})
|
||||
m = Method{"foo", "foo", "", false, false, false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
|
||||
m = NewMethod("foo", "foo", "", false, false, 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())
|
||||
|
|
|
|||
|
|
@ -60,6 +60,41 @@ type Method struct {
|
|||
|
||||
Inputs Arguments
|
||||
Outputs Arguments
|
||||
// internal fields to prevent recalculation
|
||||
sig string
|
||||
id []byte
|
||||
str string
|
||||
}
|
||||
|
||||
// NewMethod creates a new Method.
|
||||
// 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 {
|
||||
method := Method{
|
||||
Name: name,
|
||||
RawName: rawName,
|
||||
StateMutability: mutability,
|
||||
Constant: isConst,
|
||||
Payable: isPayable,
|
||||
IsFallback: isFallback,
|
||||
IsReceive: isReceive,
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
}
|
||||
method.initFields()
|
||||
return method
|
||||
}
|
||||
|
||||
// initFields should only be used in the unit tests
|
||||
// to create valid Method objects from json.
|
||||
func (method *Method) initFields() {
|
||||
// Calculate and set Signature
|
||||
method.sig = method.calcSig()
|
||||
// Calculate the method id as the first 4 bytes of the hash of sig.
|
||||
method.id = crypto.Keccak256([]byte(method.sig))[:4]
|
||||
// Calculate and set the String representation
|
||||
method.str = method.calcString()
|
||||
}
|
||||
|
||||
// Sig returns the methods string signature according to the ABI spec.
|
||||
|
|
@ -75,6 +110,21 @@ func (method Method) Sig() string {
|
|||
if method.IsFallback || method.IsReceive {
|
||||
return ""
|
||||
}
|
||||
return method.sig
|
||||
}
|
||||
|
||||
func (method Method) String() string {
|
||||
return method.str
|
||||
}
|
||||
|
||||
// ID returns the canonical representation of the method's signature used by the
|
||||
// abi definition to identify method names and types.
|
||||
func (method Method) ID() []byte {
|
||||
return method.id
|
||||
}
|
||||
|
||||
// calcSig calculates the method string signature.
|
||||
func (method Method) calcSig() string {
|
||||
types := make([]string, len(method.Inputs))
|
||||
for i, input := range method.Inputs {
|
||||
types[i] = input.Type.String()
|
||||
|
|
@ -82,7 +132,7 @@ func (method Method) Sig() string {
|
|||
return fmt.Sprintf("%v(%v)", method.RawName, strings.Join(types, ","))
|
||||
}
|
||||
|
||||
func (method Method) String() string {
|
||||
func (method Method) calcString() string {
|
||||
inputs := make([]string, len(method.Inputs))
|
||||
for i, input := range method.Inputs {
|
||||
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||
|
|
@ -112,12 +162,6 @@ func (method Method) String() string {
|
|||
return fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputs, ", "), state, strings.Join(outputs, ", "))
|
||||
}
|
||||
|
||||
// ID returns the canonical representation of the method's signature used by the
|
||||
// abi definition to identify method names and types.
|
||||
func (method Method) ID() []byte {
|
||||
return crypto.Keccak256([]byte(method.Sig()))[:4]
|
||||
}
|
||||
|
||||
// IsConstant returns the indicator whether the method is read-only.
|
||||
func (method Method) IsConstant() bool {
|
||||
return method.StateMutability == "view" || method.StateMutability == "pure" || method.Constant
|
||||
|
|
|
|||
Loading…
Reference in a new issue