github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/accounts/abi/abi.go (about)

     1  // Copyright 2019 The ebakus/go-ebakus Authors
     2  // This file is part of the ebakus/go-ebakus library.
     3  //
     4  // The ebakus/go-ebakus library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The ebakus/go-ebakus library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the ebakus/go-ebakus library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package abi
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  
    25  	"github.com/ebakus/go-ebakus/common"
    26  )
    27  
    28  type ArgumentsType int
    29  
    30  const (
    31  	InputsArgumentsType ArgumentsType = iota
    32  	OutputsArgumentsType
    33  )
    34  
    35  // The ABI holds information about a contract's context and available
    36  // invokable methods. It will allow you to type check function calls and
    37  // packs data accordingly.
    38  type ABI struct {
    39  	Constructor Method
    40  	Methods     map[string]Method
    41  	Events      map[string]Event
    42  	Tables      map[string]Table
    43  }
    44  
    45  // JSON returns a parsed ABI interface and error if it failed.
    46  func JSON(reader io.Reader) (ABI, error) {
    47  	dec := json.NewDecoder(reader)
    48  
    49  	var abi ABI
    50  	if err := dec.Decode(&abi); err != nil {
    51  		return ABI{}, err
    52  	}
    53  
    54  	return abi, nil
    55  }
    56  
    57  // Pack the given method name to conform the ABI. Method call's data
    58  // will consist of method_id, args0, arg1, ... argN. Method id consists
    59  // of 4 bytes and arguments are all 32 bytes.
    60  // Method ids are created from the first 4 bytes of the hash of the
    61  // methods string signature. (signature = baz(uint32,string32))
    62  func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    63  	return abi.PackWithArguments(name, InputsArgumentsType, args...)
    64  }
    65  
    66  // Unpack output in v according to the abi specification
    67  func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
    68  	return abi.UnpackWithArguments(v, name, output, OutputsArgumentsType)
    69  }
    70  
    71  // PackWithArguments the given method name to conform the ABI. Method call's data
    72  // will consist of method_id, args0, arg1, ... argN. Method id consists
    73  // of 4 bytes and arguments are all 32 bytes.
    74  // Method ids are created from the first 4 bytes of the hash of the
    75  // methods string signature. (signature = baz(uint32,string32))
    76  func (abi ABI) PackWithArguments(name string, argumentsType ArgumentsType, args ...interface{}) ([]byte, error) {
    77  	// Fetch the ABI of the requested method
    78  	if name == "" {
    79  		// constructor
    80  		arguments, err := abi.Constructor.Inputs.Pack(args...)
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  		return arguments, nil
    85  	}
    86  
    87  	var id, arguments []byte
    88  	var err error
    89  	if method, ok := abi.Methods[name]; ok {
    90  		id = method.ID()
    91  		switch argumentsType {
    92  		case InputsArgumentsType:
    93  			arguments, err = method.Inputs.Pack(args...)
    94  		case OutputsArgumentsType:
    95  			arguments, err = method.Outputs.Pack(args...)
    96  		}
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  	} else if table, ok := abi.Tables[name]; ok {
   101  		arguments, err = table.Pack(args...)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	} else {
   106  		return nil, fmt.Errorf("method or table '%s' not found", name)
   107  	}
   108  
   109  	// Pack up the method ID too if not a constructor and return
   110  	return append(id, arguments...), nil
   111  }
   112  
   113  // UnpackWithArguments output in v according to the abi specification
   114  func (abi ABI) UnpackWithArguments(v interface{}, name string, data []byte, argumentsType ArgumentsType) (err error) {
   115  	if len(data) == 0 {
   116  		return fmt.Errorf("abi: unmarshalling empty output")
   117  	}
   118  
   119  	// since there can't be naming collisions with contracts and events,
   120  	// we need to decide whether we're calling a method or an event
   121  	if method, ok := abi.Methods[name]; ok {
   122  		if len(data)%32 != 0 {
   123  			return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
   124  		}
   125  
   126  		switch argumentsType {
   127  		case InputsArgumentsType:
   128  			return method.Inputs.Unpack(v, data)
   129  		case OutputsArgumentsType:
   130  			return method.Outputs.Unpack(v, data)
   131  		}
   132  	} else if event, ok := abi.Events[name]; ok {
   133  		return event.Inputs.Unpack(v, data)
   134  	} else if table, ok := abi.Tables[name]; ok {
   135  		return table.Inputs.Unpack(v, data)
   136  	}
   137  	return fmt.Errorf("abi: could not locate named method, event or table")
   138  }
   139  
   140  // UnpackIntoMap unpacks a log into the provided map[string]interface{}
   141  func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
   142  	// since there can't be naming collisions with contracts and events,
   143  	// we need to decide whether we're calling a method or an event
   144  	if method, ok := abi.Methods[name]; ok {
   145  		if len(data)%32 != 0 {
   146  			return fmt.Errorf("abi: improperly formatted output")
   147  		}
   148  		return method.Outputs.UnpackIntoMap(v, data)
   149  	}
   150  	if event, ok := abi.Events[name]; ok {
   151  		return event.Inputs.UnpackIntoMap(v, data)
   152  	}
   153  	return fmt.Errorf("abi: could not locate named method, event or table")
   154  }
   155  
   156  // UnmarshalJSON implements json.Unmarshaler interface
   157  func (abi *ABI) UnmarshalJSON(data []byte) error {
   158  	var fields []struct {
   159  		Type      string
   160  		Name      string
   161  		Constant  bool
   162  		Anonymous bool
   163  		Inputs    []Argument
   164  		Outputs   []Argument
   165  	}
   166  	if err := json.Unmarshal(data, &fields); err != nil {
   167  		return err
   168  	}
   169  	abi.Methods = make(map[string]Method)
   170  	abi.Events = make(map[string]Event)
   171  	abi.Tables = make(map[string]Table)
   172  	for _, field := range fields {
   173  		switch field.Type {
   174  		case "constructor":
   175  			abi.Constructor = Method{
   176  				Inputs: field.Inputs,
   177  			}
   178  		// empty defaults to function according to the abi spec
   179  		case "function", "":
   180  			name := field.Name
   181  			_, ok := abi.Methods[name]
   182  			for idx := 0; ok; idx++ {
   183  				name = fmt.Sprintf("%s%d", field.Name, idx)
   184  				_, ok = abi.Methods[name]
   185  			}
   186  			abi.Methods[name] = Method{
   187  				Name:    name,
   188  				RawName: field.Name,
   189  				Const:   field.Constant,
   190  				Inputs:  field.Inputs,
   191  				Outputs: field.Outputs,
   192  			}
   193  		case "event":
   194  			name := field.Name
   195  			_, ok := abi.Events[name]
   196  			for idx := 0; ok; idx++ {
   197  				name = fmt.Sprintf("%s%d", field.Name, idx)
   198  				_, ok = abi.Events[name]
   199  			}
   200  			abi.Events[name] = Event{
   201  				Name:      name,
   202  				RawName:   field.Name,
   203  				Anonymous: field.Anonymous,
   204  				Inputs:    field.Inputs,
   205  			}
   206  		case "table":
   207  			abi.Tables[field.Name] = Table{
   208  				Name:   field.Name,
   209  				Inputs: field.Inputs,
   210  			}
   211  		}
   212  	}
   213  
   214  	return nil
   215  }
   216  
   217  // MethodById looks up a method by the 4-byte id
   218  // returns nil if none found
   219  func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
   220  	if len(sigdata) < 4 {
   221  		return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
   222  	}
   223  	for _, method := range abi.Methods {
   224  		if bytes.Equal(method.ID(), sigdata[:4]) {
   225  			return &method, nil
   226  		}
   227  	}
   228  	return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
   229  }
   230  
   231  // EventByID looks an event up by its topic hash in the
   232  // ABI and returns nil if none found.
   233  func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
   234  	for _, event := range abi.Events {
   235  		if bytes.Equal(event.ID().Bytes(), topic.Bytes()) {
   236  			return &event, nil
   237  		}
   238  	}
   239  	return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
   240  }
   241  
   242  // GetTableInstance returns a table instance
   243  func (abi *ABI) GetTableInstance(name string) (interface{}, error) {
   244  	if table, ok := abi.Tables[name]; ok {
   245  		return table.GetTableInstance()
   246  	}
   247  	return nil, fmt.Errorf("abi: could not locate named table")
   248  }
   249  
   250  // UnpackSingle fill single field in struct for this table from provided output of single value encoded abi
   251  func (abi *ABI) UnpackSingle(v interface{}, name string, field string, output []byte) (interface{}, error) {
   252  	if table, ok := abi.Tables[name]; ok {
   253  		return table.UnpackSingle(v, field, output)
   254  	}
   255  	return nil, fmt.Errorf("abi: could not locate named table")
   256  }