aboutsummaryrefslogblamecommitdiffstats
path: root/accounts/abi/abi.go
blob: 067381a48d3907d8cf200ee190cc211000a24f2a (plain) (tree)


























































































































































                                                                                                          
package abi

import (
    "encoding/json"
    "fmt"
    "io"
    "strings"

    "github.com/ethereum/go-ethereum/crypto"
)

// Callable method given a `Name` and whether the method is a constant.
// If the method is `Const` no transaction needs to be created for this
// particular Method call. It can easily be simulated using a local VM.
// For example a `Balance()` method only needs to retrieve something
// from the storage and therefor requires no Tx to be send to the
// network. A method such as `Transact` does require a Tx and thus will
// be flagged `true`.
// Input specifies the required input parameters for this gives method.
type Method struct {
    Name   string
    Const  bool
    Input  []Argument
    Return Type // not yet implemented
}

// Returns the methods string signature according to the ABI spec.
//
// Example
//
//     function foo(uint32 a, int b)    =    "foo(uint32,int256)"
//
// Please note that "int" is substitute for its canonical representation "int256"
func (m Method) String() (out string) {
    out += m.Name
    types := make([]string, len(m.Input))
    i := 0
    for _, input := range m.Input {
        types[i] = input.Type.String()
        i++
    }
    out += "(" + strings.Join(types, ",") + ")"

    return
}

func (m Method) Id() []byte {
    return crypto.Sha3([]byte(m.String()))[:4]
}

// Argument holds the name of the argument and the corresponding type.
// Types are used when packing and testing arguments.
type Argument struct {
    Name string
    Type Type
}

func (a *Argument) UnmarshalJSON(data []byte) error {
    var extarg struct {
        Name string
        Type string
    }
    err := json.Unmarshal(data, &extarg)
    if err != nil {
        return fmt.Errorf("argument json err: %v", err)
    }

    a.Type, err = NewType(extarg.Type)
    if err != nil {
        return err
    }
    a.Name = extarg.Name

    return nil
}

// The ABI holds information about a contract's context and available
// invokable methods. It will allow you to type check function calls and
// packs data accordingly.
type ABI struct {
    Methods map[string]Method
}

// tests, tests whether the given input would result in a successful
// call. Checks argument list count and matches input to `input`.
func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
    method := abi.Methods[name]

    var ret []byte
    for i, a := range args {
        input := method.Input[i]

        packed, err := input.Type.pack(a)
        if err != nil {
            return nil, fmt.Errorf("`%s` %v", name, err)
        }
        ret = append(ret, packed...)

    }

    return ret, nil
}

// Pack the given method name to conform the ABI. Method call's data
// will consist of method_id, args0, arg1, ... argN. Method id consists
// of 4 bytes and arguments are all 32 bytes.
// Method ids are created from the first 4 bytes of the hash of the
// methods string signature. (signature = baz(uint32,string32))
func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    method, exist := abi.Methods[name]
    if !exist {
        return nil, fmt.Errorf("method '%s' not found", name)
    }

    // start with argument count match
    if len(args) != len(method.Input) {
        return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Input))
    }

    arguments, err := abi.pack(name, args...)
    if err != nil {
        return nil, err
    }

    // Set function id
    packed := abi.Methods[name].Id()
    packed = append(packed, arguments...)

    return packed, nil
}

func (abi *ABI) UnmarshalJSON(data []byte) error {
    var methods []Method
    if err := json.Unmarshal(data, &methods); err != nil {
        return err
    }

    abi.Methods = make(map[string]Method)
    for _, method := range methods {
        abi.Methods[method.Name] = method
    }

    return nil
}

func JSON(reader io.Reader) (ABI, error) {
    dec := json.NewDecoder(reader)

    var abi ABI
    if err := dec.Decode(&abi); err != nil {
        return ABI{}, err
    }

    return abi, nil
}