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 {
|
for _, field := range fields {
|
||||||
switch field.Type {
|
switch field.Type {
|
||||||
case "constructor":
|
case "constructor":
|
||||||
abi.Constructor = Method{
|
abi.Constructor = NewMethod("", "", field.StateMutability, field.Constant, field.Payable, false, false, field.Inputs, nil)
|
||||||
Inputs: field.Inputs,
|
// empty defaults to function according to the abi spec
|
||||||
|
case "function", "":
|
||||||
// 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":
|
|
||||||
name := field.Name
|
name := field.Name
|
||||||
_, ok := abi.Methods[name]
|
_, ok := abi.Methods[name]
|
||||||
for idx := 0; ok; idx++ {
|
for idx := 0; ok; idx++ {
|
||||||
name = fmt.Sprintf("%s%d", field.Name, idx)
|
name = fmt.Sprintf("%s%d", field.Name, idx)
|
||||||
_, ok = abi.Methods[name]
|
_, ok = abi.Methods[name]
|
||||||
}
|
}
|
||||||
abi.Methods[name] = Method{
|
isConst := field.Constant || field.StateMutability == "pure" || field.StateMutability == "view"
|
||||||
Name: name,
|
abi.Methods[name] = NewMethod(name, field.Name, field.StateMutability, isConst, field.Payable, false, false, field.Inputs, field.Outputs)
|
||||||
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,
|
|
||||||
}
|
|
||||||
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 = Method{
|
abi.Fallback = NewMethod("", "", field.StateMutability, field.Constant, field.Payable, true, false, nil, nil)
|
||||||
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,
|
|
||||||
}
|
|
||||||
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
|
||||||
|
|
@ -201,23 +166,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 = Method{
|
abi.Receive = NewMethod("", "", "payable", field.Constant, field.Payable, false, true, nil, nil)
|
||||||
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,
|
|
||||||
}
|
|
||||||
case "event":
|
case "event":
|
||||||
name := field.Name
|
name := field.Name
|
||||||
_, ok := abi.Events[name]
|
_, ok := abi.Events[name]
|
||||||
|
|
|
||||||
|
|
@ -58,20 +58,14 @@ const jsondata2 = `
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
Uint256, _ := NewType("uint256", "", nil)
|
Uint256, _ := NewType("uint256", "", nil)
|
||||||
exp := ABI{
|
abi := ABI{
|
||||||
Methods: map[string]Method{
|
Methods: map[string]Method{
|
||||||
"balance": {
|
"balance": NewMethod("balance", "balance", "view", true, false, false, false, nil, nil),
|
||||||
"balance", "balance", "view", false, false, false, false, nil, nil,
|
"send": NewMethod("send", "send", "", false, false, false, false, []Argument{{"amount", Uint256, false}}, nil),
|
||||||
},
|
|
||||||
"send": {
|
|
||||||
"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 {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
@ -173,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 := 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)"
|
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())
|
||||||
|
|
@ -185,7 +179,7 @@ func TestMethodSignature(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uintt, _ := NewType("uint256", "", nil)
|
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)"
|
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())
|
||||||
|
|
@ -204,7 +198,7 @@ func TestMethodSignature(t *testing.T) {
|
||||||
{Name: "y", Type: "int256"},
|
{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)"
|
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())
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,41 @@ type Method struct {
|
||||||
|
|
||||||
Inputs Arguments
|
Inputs Arguments
|
||||||
Outputs 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.
|
// 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 {
|
if method.IsFallback || method.IsReceive {
|
||||||
return ""
|
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))
|
types := make([]string, len(method.Inputs))
|
||||||
for i, input := range method.Inputs {
|
for i, input := range method.Inputs {
|
||||||
types[i] = input.Type.String()
|
types[i] = input.Type.String()
|
||||||
|
|
@ -82,7 +132,7 @@ func (method Method) Sig() string {
|
||||||
return fmt.Sprintf("%v(%v)", method.RawName, strings.Join(types, ","))
|
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))
|
inputs := make([]string, len(method.Inputs))
|
||||||
for i, input := range method.Inputs {
|
for i, input := range method.Inputs {
|
||||||
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
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, ", "))
|
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.
|
// IsConstant returns the indicator whether the method is read-only.
|
||||||
func (method Method) IsConstant() bool {
|
func (method Method) IsConstant() bool {
|
||||||
return method.StateMutability == "view" || method.StateMutability == "pure" || method.Constant
|
return method.StateMutability == "view" || method.StateMutability == "pure" || method.Constant
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue