github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/accounts/abi/abi.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package abi
    13  
    14  import (
    15  	"bytes"
    16  	"encoding/json"
    17  	"fmt"
    18  	"io"
    19  )
    20  
    21  // The ABI holds information about a contract's context and available
    22  // invokable methods. It will allow you to type check function calls and
    23  // packs data accordingly.
    24  type ABI struct {
    25  	Constructor Method
    26  	Methods     map[string]Method
    27  	Events      map[string]Event
    28  }
    29  
    30  // JSON returns a parsed ABI interface and error if it failed.
    31  func JSON(reader io.Reader) (ABI, error) {
    32  	dec := json.NewDecoder(reader)
    33  
    34  	var abi ABI
    35  	if err := dec.Decode(&abi); err != nil {
    36  		return ABI{}, err
    37  	}
    38  
    39  	return abi, nil
    40  }
    41  
    42  // Pack the given method name to conform the ABI. Method call's data
    43  // will consist of method_id, args0, arg1, ... argN. Method id consists
    44  // of 4 bytes and arguments are all 32 bytes.
    45  // Method ids are created from the first 4 bytes of the hash of the
    46  // methods string signature. (signature = baz(uint32,string32))
    47  func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    48  	// Fetch the ABI of the requested method
    49  	if name == "" {
    50  		// constructor
    51  		arguments, err := abi.Constructor.Inputs.Pack(args...)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  		return arguments, nil
    56  
    57  	}
    58  	method, exist := abi.Methods[name]
    59  	if !exist {
    60  		return nil, fmt.Errorf("method '%s' not found", name)
    61  	}
    62  
    63  	arguments, err := method.Inputs.Pack(args...)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	// Pack up the method ID too if not a constructor and return
    68  	return append(method.Id(), arguments...), nil
    69  }
    70  
    71  // Unpack output in v according to the abi specification
    72  func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
    73  	if len(output) == 0 {
    74  		return fmt.Errorf("abi: unmarshalling empty output")
    75  	}
    76  	// since there can't be naming collisions with contracts and events,
    77  	// we need to decide whether we're calling a method or an event
    78  	if method, ok := abi.Methods[name]; ok {
    79  		if len(output)%32 != 0 {
    80  			return fmt.Errorf("abi: improperly formatted output")
    81  		}
    82  		return method.Outputs.Unpack(v, output)
    83  	} else if event, ok := abi.Events[name]; ok {
    84  		return event.Inputs.Unpack(v, output)
    85  	}
    86  	return fmt.Errorf("abi: could not locate named method or event")
    87  }
    88  
    89  // UnmarshalJSON implements json.Unmarshaler interface
    90  func (abi *ABI) UnmarshalJSON(data []byte) error {
    91  	var fields []struct {
    92  		Type      string
    93  		Name      string
    94  		Constant  bool
    95  		Anonymous bool
    96  		Inputs    []Argument
    97  		Outputs   []Argument
    98  	}
    99  
   100  	if err := json.Unmarshal(data, &fields); err != nil {
   101  		return err
   102  	}
   103  
   104  	abi.Methods = make(map[string]Method)
   105  	abi.Events = make(map[string]Event)
   106  	for _, field := range fields {
   107  		switch field.Type {
   108  		case "constructor":
   109  			abi.Constructor = Method{
   110  				Inputs: field.Inputs,
   111  			}
   112  		// empty defaults to function according to the abi spec
   113  		case "function", "":
   114  			abi.Methods[field.Name] = Method{
   115  				Name:    field.Name,
   116  				Const:   field.Constant,
   117  				Inputs:  field.Inputs,
   118  				Outputs: field.Outputs,
   119  			}
   120  		case "event":
   121  			abi.Events[field.Name] = Event{
   122  				Name:      field.Name,
   123  				Anonymous: field.Anonymous,
   124  				Inputs:    field.Inputs,
   125  			}
   126  		}
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  // MethodById looks up a method by the 4-byte id
   133  // returns nil if none found
   134  func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
   135  	for _, method := range abi.Methods {
   136  		if bytes.Equal(method.Id(), sigdata[:4]) {
   137  			return &method, nil
   138  		}
   139  	}
   140  	return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
   141  }