github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/accounts/abi/abi.go (about)

     1  package abi
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  
     9  	"github.com/jonasnick/go-ethereum/crypto"
    10  )
    11  
    12  // Callable method given a `Name` and whether the method is a constant.
    13  // If the method is `Const` no transaction needs to be created for this
    14  // particular Method call. It can easily be simulated using a local VM.
    15  // For example a `Balance()` method only needs to retrieve something
    16  // from the storage and therefor requires no Tx to be send to the
    17  // network. A method such as `Transact` does require a Tx and thus will
    18  // be flagged `true`.
    19  // Input specifies the required input parameters for this gives method.
    20  type Method struct {
    21  	Name   string
    22  	Const  bool
    23  	Input  []Argument
    24  	Return Type // not yet implemented
    25  }
    26  
    27  // Returns the methods string signature according to the ABI spec.
    28  //
    29  // Example
    30  //
    31  //     function foo(uint32 a, int b)    =    "foo(uint32,int256)"
    32  //
    33  // Please note that "int" is substitute for its canonical representation "int256"
    34  func (m Method) String() (out string) {
    35  	out += m.Name
    36  	types := make([]string, len(m.Input))
    37  	i := 0
    38  	for _, input := range m.Input {
    39  		types[i] = input.Type.String()
    40  		i++
    41  	}
    42  	out += "(" + strings.Join(types, ",") + ")"
    43  
    44  	return
    45  }
    46  
    47  func (m Method) Id() []byte {
    48  	return crypto.Sha3([]byte(m.String()))[:4]
    49  }
    50  
    51  // Argument holds the name of the argument and the corresponding type.
    52  // Types are used when packing and testing arguments.
    53  type Argument struct {
    54  	Name string
    55  	Type Type
    56  }
    57  
    58  func (a *Argument) UnmarshalJSON(data []byte) error {
    59  	var extarg struct {
    60  		Name string
    61  		Type string
    62  	}
    63  	err := json.Unmarshal(data, &extarg)
    64  	if err != nil {
    65  		return fmt.Errorf("argument json err: %v", err)
    66  	}
    67  
    68  	a.Type, err = NewType(extarg.Type)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	a.Name = extarg.Name
    73  
    74  	return nil
    75  }
    76  
    77  // The ABI holds information about a contract's context and available
    78  // invokable methods. It will allow you to type check function calls and
    79  // packs data accordingly.
    80  type ABI struct {
    81  	Methods map[string]Method
    82  }
    83  
    84  // tests, tests whether the given input would result in a successful
    85  // call. Checks argument list count and matches input to `input`.
    86  func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
    87  	method := abi.Methods[name]
    88  
    89  	var ret []byte
    90  	for i, a := range args {
    91  		input := method.Input[i]
    92  
    93  		packed, err := input.Type.pack(a)
    94  		if err != nil {
    95  			return nil, fmt.Errorf("`%s` %v", name, err)
    96  		}
    97  		ret = append(ret, packed...)
    98  
    99  	}
   100  
   101  	return ret, nil
   102  }
   103  
   104  // Pack the given method name to conform the ABI. Method call's data
   105  // will consist of method_id, args0, arg1, ... argN. Method id consists
   106  // of 4 bytes and arguments are all 32 bytes.
   107  // Method ids are created from the first 4 bytes of the hash of the
   108  // methods string signature. (signature = baz(uint32,string32))
   109  func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
   110  	method, exist := abi.Methods[name]
   111  	if !exist {
   112  		return nil, fmt.Errorf("method '%s' not found", name)
   113  	}
   114  
   115  	// start with argument count match
   116  	if len(args) != len(method.Input) {
   117  		return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Input))
   118  	}
   119  
   120  	arguments, err := abi.pack(name, args...)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	// Set function id
   126  	packed := abi.Methods[name].Id()
   127  	packed = append(packed, arguments...)
   128  
   129  	return packed, nil
   130  }
   131  
   132  func (abi *ABI) UnmarshalJSON(data []byte) error {
   133  	var methods []Method
   134  	if err := json.Unmarshal(data, &methods); err != nil {
   135  		return err
   136  	}
   137  
   138  	abi.Methods = make(map[string]Method)
   139  	for _, method := range methods {
   140  		abi.Methods[method.Name] = method
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  func JSON(reader io.Reader) (ABI, error) {
   147  	dec := json.NewDecoder(reader)
   148  
   149  	var abi ABI
   150  	if err := dec.Decode(&abi); err != nil {
   151  		return ABI{}, err
   152  	}
   153  
   154  	return abi, nil
   155  }