github.com/klaytn/klaytn@v1.10.2/accounts/abi/abi.go (about)

     1  // Modifications Copyright 2020 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from accounts/abi/abi_test.go (2020/12/14).
    19  // Modified and improved for the klaytn development.
    20  
    21  package abi
    22  
    23  import (
    24  	"bytes"
    25  	"encoding/json"
    26  	"errors"
    27  	"fmt"
    28  	"io"
    29  
    30  	"github.com/klaytn/klaytn/common"
    31  	"github.com/klaytn/klaytn/crypto"
    32  )
    33  
    34  // The ABI holds information about a contract's context and available
    35  // invokable methods. It will allow you to type check function calls and
    36  // packs data accordingly.
    37  type ABI struct {
    38  	Constructor Method
    39  	Methods     map[string]Method
    40  	Events      map[string]Event
    41  
    42  	// Additional "special" functions introduced in solidity v0.6.0.
    43  	// It's separated from the original default fallback. Each contract
    44  	// can only define one fallback and receive function.
    45  	Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
    46  	Receive  Method
    47  }
    48  
    49  // JSON returns a parsed ABI interface and error if it failed.
    50  func JSON(reader io.Reader) (ABI, error) {
    51  	dec := json.NewDecoder(reader)
    52  
    53  	var abi ABI
    54  	if err := dec.Decode(&abi); err != nil {
    55  		return ABI{}, err
    56  	}
    57  	return abi, nil
    58  }
    59  
    60  // Pack the given method name to conform the ABI. Method call's data
    61  // will consist of method_id, args0, arg1, ... argN. Method id consists
    62  // of 4 bytes and arguments are all 32 bytes.
    63  // Method ids are created from the first 4 bytes of the hash of the
    64  // methods string signature. (signature = baz(uint32,string32))
    65  func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    66  	// Fetch the ABI of the requested method
    67  	if name == "" {
    68  		// constructor
    69  		arguments, err := abi.Constructor.Inputs.Pack(args...)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		return arguments, nil
    74  	}
    75  	method, exist := abi.Methods[name]
    76  	if !exist {
    77  		return nil, fmt.Errorf("method '%s' not found", name)
    78  	}
    79  	arguments, err := method.Inputs.Pack(args...)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	// Pack up the method ID too if not a constructor and return
    84  	return append(method.ID, arguments...), nil
    85  }
    86  
    87  // Unpack output in v according to the abi specification
    88  func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
    89  	// since there can't be naming collisions with contracts and events,
    90  	// we need to decide whether we're calling a method or an event
    91  	if method, ok := abi.Methods[name]; ok {
    92  		if len(data)%32 != 0 {
    93  			return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
    94  		}
    95  		return method.Outputs.Unpack(v, data)
    96  	}
    97  	if event, ok := abi.Events[name]; ok {
    98  		return event.Inputs.Unpack(v, data)
    99  	}
   100  	return fmt.Errorf("abi: could not locate named method or event")
   101  }
   102  
   103  // UnpackIntoMap unpacks a log into the provided map[string]interface{}
   104  func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
   105  	// since there can't be naming collisions with contracts and events,
   106  	// we need to decide whether we're calling a method or an event
   107  	if method, ok := abi.Methods[name]; ok {
   108  		if len(data)%32 != 0 {
   109  			return fmt.Errorf("abi: improperly formatted output")
   110  		}
   111  		return method.Outputs.UnpackIntoMap(v, data)
   112  	}
   113  	if event, ok := abi.Events[name]; ok {
   114  		return event.Inputs.UnpackIntoMap(v, data)
   115  	}
   116  	return fmt.Errorf("abi: could not locate named method or event")
   117  }
   118  
   119  // UnmarshalJSON implements json.Unmarshaler interface
   120  func (abi *ABI) UnmarshalJSON(data []byte) error {
   121  	var fields []struct {
   122  		Type    string
   123  		Name    string
   124  		Inputs  []Argument
   125  		Outputs []Argument
   126  
   127  		// Status indicator which can be: "pure", "view",
   128  		// "nonpayable" or "payable".
   129  		StateMutability string
   130  
   131  		// Deprecated Status indicators, but removed in v0.6.0.
   132  		Constant bool // True if function is either pure or view
   133  		Payable  bool // True if function is payable
   134  
   135  		// Event relevant indicator represents the event is
   136  		// declared as anonymous.
   137  		Anonymous bool
   138  	}
   139  	if err := json.Unmarshal(data, &fields); err != nil {
   140  		return err
   141  	}
   142  	abi.Methods = make(map[string]Method)
   143  	abi.Events = make(map[string]Event)
   144  	for _, field := range fields {
   145  		switch field.Type {
   146  		case "constructor":
   147  			abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
   148  		case "function":
   149  			name := abi.overloadedMethodName(field.Name)
   150  			abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
   151  		case "fallback":
   152  			// New introduced function type in v0.6.0, check more detail
   153  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   154  			if abi.HasFallback() {
   155  				return errors.New("only single fallback is allowed")
   156  			}
   157  			abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
   158  		case "receive":
   159  			// New introduced function type in v0.6.0, check more detail
   160  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   161  			if abi.HasReceive() {
   162  				return errors.New("only single receive is allowed")
   163  			}
   164  			if field.StateMutability != "payable" {
   165  				return errors.New("the statemutability of receive can only be payable")
   166  			}
   167  			abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
   168  		case "event":
   169  			name := abi.overloadedEventName(field.Name)
   170  			abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
   171  		default:
   172  			return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
   173  		}
   174  	}
   175  	return nil
   176  }
   177  
   178  // overloadedMethodName returns the next available name for a given function.
   179  // Needed since solidity allows for function overload.
   180  //
   181  // e.g. if the abi contains Methods send, send1
   182  // overloadedMethodName would return send2 for input send.
   183  func (abi *ABI) overloadedMethodName(rawName string) string {
   184  	name := rawName
   185  	_, ok := abi.Methods[name]
   186  	for idx := 0; ok; idx++ {
   187  		name = fmt.Sprintf("%s%d", rawName, idx)
   188  		_, ok = abi.Methods[name]
   189  	}
   190  	return name
   191  }
   192  
   193  // overloadedEventName returns the next available name for a given event.
   194  // Needed since solidity allows for event overload.
   195  //
   196  // e.g. if the abi contains events received, received1
   197  // overloadedEventName would return received2 for input received.
   198  func (abi *ABI) overloadedEventName(rawName string) string {
   199  	name := rawName
   200  	_, ok := abi.Events[name]
   201  	for idx := 0; ok; idx++ {
   202  		name = fmt.Sprintf("%s%d", rawName, idx)
   203  		_, ok = abi.Events[name]
   204  	}
   205  	return name
   206  }
   207  
   208  // MethodById looks up a method by the 4-byte id
   209  // returns nil if none found
   210  func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
   211  	if len(sigdata) < 4 {
   212  		return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
   213  	}
   214  	for _, method := range abi.Methods {
   215  		if bytes.Equal(method.ID, sigdata[:4]) {
   216  			return &method, nil
   217  		}
   218  	}
   219  	return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
   220  }
   221  
   222  // EventByID looks an event up by its topic hash in the
   223  // ABI and returns nil if none found.
   224  func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
   225  	for _, event := range abi.Events {
   226  		if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
   227  			return &event, nil
   228  		}
   229  	}
   230  	return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
   231  }
   232  
   233  // HasFallback returns an indicator whether a fallback function is included.
   234  func (abi *ABI) HasFallback() bool {
   235  	return abi.Fallback.Type == Fallback
   236  }
   237  
   238  // HasReceive returns an indicator whether a receive function is included.
   239  func (abi *ABI) HasReceive() bool {
   240  	return abi.Receive.Type == Receive
   241  }
   242  
   243  // revertSelector is a special function selector for revert reason unpacking.
   244  var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
   245  
   246  // UnpackRevert resolves the abi-encoded revert reason. According to the solidity
   247  // spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
   248  // the provided revert reason is abi-encoded as if it were a call to a function
   249  // `Error(string)`. So it's a special tool for it.
   250  func UnpackRevert(data []byte) (string, error) {
   251  	if len(data) < 4 {
   252  		return "", errors.New("invalid data for unpacking")
   253  	}
   254  	if !bytes.Equal(data[:4], revertSelector) {
   255  		return "", errors.New("invalid data for unpacking")
   256  	}
   257  	var reason string
   258  	typ, _ := NewType("string", "", nil)
   259  	if err := (Arguments{{Type: typ}}).Unpack(&reason, data[4:]); err != nil {
   260  		return "", err
   261  	}
   262  	return reason, nil
   263  }