mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 20:26:41 +00:00
Merge remote-tracking branch 'upstream/develop' into p2p.crypto2
This commit is contained in:
commit
93699acf2c
58 changed files with 5574 additions and 85 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -15,3 +15,5 @@
|
||||||
.#*
|
.#*
|
||||||
*#
|
*#
|
||||||
*~
|
*~
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ RUN apt-get update && apt-get upgrade -y
|
||||||
RUN apt-get install -y git mercurial build-essential software-properties-common pkg-config libgmp3-dev libreadline6-dev libpcre3-dev libpcre++-dev
|
RUN apt-get install -y git mercurial build-essential software-properties-common pkg-config libgmp3-dev libreadline6-dev libpcre3-dev libpcre++-dev
|
||||||
|
|
||||||
## Build and install Go
|
## Build and install Go
|
||||||
RUN hg clone -u release https://code.google.com/p/go
|
RUN git clone https://go.googlesource.com/go
|
||||||
RUN cd go && hg update go1.4
|
RUN cd go && git checkout go1.4.1
|
||||||
RUN cd go/src && ./all.bash && go version
|
RUN cd go/src && ./all.bash && go version
|
||||||
|
|
||||||
## Install GUI dependencies
|
## Install GUI dependencies
|
||||||
|
|
|
||||||
155
accounts/abi/abi.go
Normal file
155
accounts/abi/abi.go
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
330
accounts/abi/abi_test.go
Normal file
330
accounts/abi/abi_test.go
Normal file
|
|
@ -0,0 +1,330 @@
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const jsondata = `
|
||||||
|
[
|
||||||
|
{ "name" : "balance", "const" : true },
|
||||||
|
{ "name" : "send", "const" : false, "input" : [ { "name" : "amount", "type" : "uint256" } ] }
|
||||||
|
]`
|
||||||
|
|
||||||
|
const jsondata2 = `
|
||||||
|
[
|
||||||
|
{ "name" : "balance", "const" : true },
|
||||||
|
{ "name" : "send", "const" : false, "input" : [ { "name" : "amount", "type" : "uint256" } ] },
|
||||||
|
{ "name" : "test", "const" : false, "input" : [ { "name" : "number", "type" : "uint32" } ] },
|
||||||
|
{ "name" : "string", "const" : false, "input" : [ { "name" : "input", "type" : "string" } ] },
|
||||||
|
{ "name" : "bool", "const" : false, "input" : [ { "name" : "input", "type" : "bool" } ] },
|
||||||
|
{ "name" : "address", "const" : false, "input" : [ { "name" : "input", "type" : "address" } ] },
|
||||||
|
{ "name" : "string32", "const" : false, "input" : [ { "name" : "input", "type" : "string32" } ] },
|
||||||
|
{ "name" : "uint64[2]", "const" : false, "input" : [ { "name" : "input", "type" : "uint64[2]" } ] },
|
||||||
|
{ "name" : "uint64[]", "const" : false, "input" : [ { "name" : "input", "type" : "uint64[]" } ] },
|
||||||
|
{ "name" : "foo", "const" : false, "input" : [ { "name" : "input", "type" : "uint32" } ] },
|
||||||
|
{ "name" : "bar", "const" : false, "input" : [ { "name" : "input", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
|
||||||
|
{ "name" : "slice", "const" : false, "input" : [ { "name" : "input", "type" : "uint32[2]" } ] },
|
||||||
|
{ "name" : "slice256", "const" : false, "input" : [ { "name" : "input", "type" : "uint256[2]" } ] }
|
||||||
|
]`
|
||||||
|
|
||||||
|
func TestType(t *testing.T) {
|
||||||
|
typ, err := NewType("uint32")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if typ.Kind != reflect.Ptr {
|
||||||
|
t.Error("expected uint32 to have kind Ptr")
|
||||||
|
}
|
||||||
|
|
||||||
|
typ, err = NewType("uint32[]")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if typ.Kind != reflect.Slice {
|
||||||
|
t.Error("expected uint32[] to have type slice")
|
||||||
|
}
|
||||||
|
if typ.Type != ubig_ts {
|
||||||
|
t.Error("expcted uith32[] to have type uint64")
|
||||||
|
}
|
||||||
|
|
||||||
|
typ, err = NewType("uint32[2]")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if typ.Kind != reflect.Slice {
|
||||||
|
t.Error("expected uint32[2] to have kind slice")
|
||||||
|
}
|
||||||
|
if typ.Type != ubig_ts {
|
||||||
|
t.Error("expcted uith32[2] to have type uint64")
|
||||||
|
}
|
||||||
|
if typ.Size != 2 {
|
||||||
|
t.Error("expected uint32[2] to have a size of 2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReader(t *testing.T) {
|
||||||
|
Uint256, _ := NewType("uint256")
|
||||||
|
exp := ABI{
|
||||||
|
Methods: map[string]Method{
|
||||||
|
"balance": Method{
|
||||||
|
"balance", true, nil, Type{},
|
||||||
|
},
|
||||||
|
"send": Method{
|
||||||
|
"send", false, []Argument{
|
||||||
|
Argument{"amount", Uint256},
|
||||||
|
}, Type{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deep equal fails for some reason
|
||||||
|
t.Skip()
|
||||||
|
if !reflect.DeepEqual(abi, exp) {
|
||||||
|
t.Errorf("\nabi: %v\ndoes not match exp: %v", abi, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestNumbers(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("balance"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("balance", 1); err == nil {
|
||||||
|
t.Error("expected error for balance(1)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("doesntexist", nil); err == nil {
|
||||||
|
t.Errorf("doesntexist shouldn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("doesntexist", 1); err == nil {
|
||||||
|
t.Errorf("doesntexist(1) shouldn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("send", big.NewInt(1000)); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := new(int)
|
||||||
|
*i = 1000
|
||||||
|
if _, err := abi.Pack("send", i); err == nil {
|
||||||
|
t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("send", 1000); err != nil {
|
||||||
|
t.Error("expected send(1000) to cast to big")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("test", uint32(1000)); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestString(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("string", "hello world"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
str10 := string(make([]byte, 10))
|
||||||
|
if _, err := abi.Pack("string32", str10); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
str32 := string(make([]byte, 32))
|
||||||
|
if _, err := abi.Pack("string32", str32); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
str33 := string(make([]byte, 33))
|
||||||
|
if _, err := abi.Pack("string32", str33); err == nil {
|
||||||
|
t.Error("expected str33 to throw out of bound error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestBool(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("bool", true); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestSlice(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := make([]byte, 20)
|
||||||
|
if _, err := abi.Pack("address", addr); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = make([]byte, 21)
|
||||||
|
if _, err := abi.Pack("address", addr); err == nil {
|
||||||
|
t.Error("expected address of 21 width to throw")
|
||||||
|
}
|
||||||
|
|
||||||
|
slice := make([]byte, 2)
|
||||||
|
if _, err := abi.Pack("uint64[2]", slice); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := abi.Pack("uint64[]", slice); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestAddress(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := make([]byte, 20)
|
||||||
|
if _, err := abi.Pack("address", addr); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMethodSignature(t *testing.T) {
|
||||||
|
String, _ := NewType("string")
|
||||||
|
String32, _ := NewType("string32")
|
||||||
|
m := Method{"foo", false, []Argument{Argument{"bar", String32}, Argument{"baz", String}}, Type{}}
|
||||||
|
exp := "foo(string32,string)"
|
||||||
|
if m.String() != exp {
|
||||||
|
t.Error("signature mismatch", exp, "!=", m.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
idexp := crypto.Sha3([]byte(exp))[:4]
|
||||||
|
if !bytes.Equal(m.Id(), idexp) {
|
||||||
|
t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
|
||||||
|
}
|
||||||
|
|
||||||
|
uintt, _ := NewType("uint")
|
||||||
|
m = Method{"foo", false, []Argument{Argument{"bar", uintt}}, Type{}}
|
||||||
|
exp = "foo(uint256)"
|
||||||
|
if m.String() != exp {
|
||||||
|
t.Error("signature mismatch", exp, "!=", m.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPack(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := crypto.Sha3([]byte("foo(uint32)"))[:4]
|
||||||
|
sig = append(sig, make([]byte, 32)...)
|
||||||
|
sig[35] = 10
|
||||||
|
|
||||||
|
packed, err := abi.Pack("foo", uint32(10))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(packed, sig) {
|
||||||
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiPack(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := crypto.Sha3([]byte("bar(uint32,uint16)"))[:4]
|
||||||
|
sig = append(sig, make([]byte, 64)...)
|
||||||
|
sig[35] = 10
|
||||||
|
sig[67] = 11
|
||||||
|
|
||||||
|
packed, err := abi.Pack("bar", uint32(10), uint16(11))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(packed, sig) {
|
||||||
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPackSlice(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := crypto.Sha3([]byte("slice(uint32[2])"))[:4]
|
||||||
|
sig = append(sig, make([]byte, 64)...)
|
||||||
|
sig[35] = 1
|
||||||
|
sig[67] = 2
|
||||||
|
|
||||||
|
packed, err := abi.Pack("slice", []uint32{1, 2})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(packed, sig) {
|
||||||
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPackSliceBig(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := crypto.Sha3([]byte("slice256(uint256[2])"))[:4]
|
||||||
|
sig = append(sig, make([]byte, 64)...)
|
||||||
|
sig[35] = 1
|
||||||
|
sig[67] = 2
|
||||||
|
|
||||||
|
packed, err := abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(packed, sig) {
|
||||||
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
|
}
|
||||||
|
}
|
||||||
10
accounts/abi/doc.go
Normal file
10
accounts/abi/doc.go
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Package abi implements the Ethereum ABI (Application Binary
|
||||||
|
// Interface).
|
||||||
|
//
|
||||||
|
// The Ethereum ABI is strongly typed, known at compile time
|
||||||
|
// and static. This ABI will handle basic type casting; unsigned
|
||||||
|
// to signed and visa versa. It does not handle slice casting such
|
||||||
|
// as unsigned slice to signed slice. Bit size type casting is also
|
||||||
|
// handled. ints with a bit size of 32 will be properly cast to int256,
|
||||||
|
// etc.
|
||||||
|
package abi
|
||||||
106
accounts/abi/numbers.go
Normal file
106
accounts/abi/numbers.go
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var big_t = reflect.TypeOf(&big.Int{})
|
||||||
|
var ubig_t = reflect.TypeOf(&big.Int{})
|
||||||
|
var byte_t = reflect.TypeOf(byte(0))
|
||||||
|
var byte_ts = reflect.TypeOf([]byte(nil))
|
||||||
|
var uint_t = reflect.TypeOf(uint(0))
|
||||||
|
var uint8_t = reflect.TypeOf(uint8(0))
|
||||||
|
var uint16_t = reflect.TypeOf(uint16(0))
|
||||||
|
var uint32_t = reflect.TypeOf(uint32(0))
|
||||||
|
var uint64_t = reflect.TypeOf(uint64(0))
|
||||||
|
var int_t = reflect.TypeOf(int(0))
|
||||||
|
var int8_t = reflect.TypeOf(int8(0))
|
||||||
|
var int16_t = reflect.TypeOf(int16(0))
|
||||||
|
var int32_t = reflect.TypeOf(int32(0))
|
||||||
|
var int64_t = reflect.TypeOf(int64(0))
|
||||||
|
|
||||||
|
var uint_ts = reflect.TypeOf([]uint(nil))
|
||||||
|
var uint8_ts = reflect.TypeOf([]uint8(nil))
|
||||||
|
var uint16_ts = reflect.TypeOf([]uint16(nil))
|
||||||
|
var uint32_ts = reflect.TypeOf([]uint32(nil))
|
||||||
|
var uint64_ts = reflect.TypeOf([]uint64(nil))
|
||||||
|
var ubig_ts = reflect.TypeOf([]*big.Int(nil))
|
||||||
|
|
||||||
|
var int_ts = reflect.TypeOf([]int(nil))
|
||||||
|
var int8_ts = reflect.TypeOf([]int8(nil))
|
||||||
|
var int16_ts = reflect.TypeOf([]int16(nil))
|
||||||
|
var int32_ts = reflect.TypeOf([]int32(nil))
|
||||||
|
var int64_ts = reflect.TypeOf([]int64(nil))
|
||||||
|
var big_ts = reflect.TypeOf([]*big.Int(nil))
|
||||||
|
|
||||||
|
// U256 will ensure unsigned 256bit on big nums
|
||||||
|
func U256(n *big.Int) []byte {
|
||||||
|
return ethutil.LeftPadBytes(ethutil.U256(n).Bytes(), 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func S256(n *big.Int) []byte {
|
||||||
|
sint := ethutil.S256(n)
|
||||||
|
ret := ethutil.LeftPadBytes(sint.Bytes(), 32)
|
||||||
|
if sint.Cmp(ethutil.Big0) < 0 {
|
||||||
|
for i, b := range ret {
|
||||||
|
if b == 0 {
|
||||||
|
ret[i] = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// S256 will ensure signed 256bit on big nums
|
||||||
|
func U2U256(n uint64) []byte {
|
||||||
|
return U256(big.NewInt(int64(n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func S2S256(n int64) []byte {
|
||||||
|
return S256(big.NewInt(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
|
||||||
|
func packNum(value reflect.Value, to byte) []byte {
|
||||||
|
switch kind := value.Kind(); kind {
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
if to == UintTy {
|
||||||
|
return U2U256(value.Uint())
|
||||||
|
} else {
|
||||||
|
return S2S256(int64(value.Uint()))
|
||||||
|
}
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
if to == UintTy {
|
||||||
|
return U2U256(uint64(value.Int()))
|
||||||
|
} else {
|
||||||
|
return S2S256(value.Int())
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
// This only takes care of packing and casting. No type checking is done here. It should be done prior to using this function.
|
||||||
|
if to == UintTy {
|
||||||
|
return U256(value.Interface().(*big.Int))
|
||||||
|
} else {
|
||||||
|
return S256(value.Interface().(*big.Int))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks whether the given reflect value is signed. This also works for slices with a number type
|
||||||
|
func isSigned(v reflect.Value) bool {
|
||||||
|
switch v.Type() {
|
||||||
|
case ubig_ts, big_ts, big_t, ubig_t:
|
||||||
|
return true
|
||||||
|
case int_ts, int8_ts, int16_ts, int32_ts, int64_ts, int_t, int8_t, int16_t, int32_t, int64_t:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
72
accounts/abi/numbers_test.go
Normal file
72
accounts/abi/numbers_test.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNumberTypes(t *testing.T) {
|
||||||
|
ubytes := make([]byte, 32)
|
||||||
|
ubytes[31] = 1
|
||||||
|
sbytesmin := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||||
|
|
||||||
|
unsigned := U256(big.NewInt(1))
|
||||||
|
if !bytes.Equal(unsigned, ubytes) {
|
||||||
|
t.Error("expected %x got %x", ubytes, unsigned)
|
||||||
|
}
|
||||||
|
|
||||||
|
signed := S256(big.NewInt(1))
|
||||||
|
if !bytes.Equal(signed, ubytes) {
|
||||||
|
t.Error("expected %x got %x", ubytes, unsigned)
|
||||||
|
}
|
||||||
|
|
||||||
|
signed = S256(big.NewInt(-1))
|
||||||
|
if !bytes.Equal(signed, sbytesmin) {
|
||||||
|
t.Error("expected %x got %x", ubytes, unsigned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPackNumber(t *testing.T) {
|
||||||
|
ubytes := make([]byte, 32)
|
||||||
|
ubytes[31] = 1
|
||||||
|
sbytesmin := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||||
|
maxunsigned := []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
|
||||||
|
|
||||||
|
packed := packNum(reflect.ValueOf(1), IntTy)
|
||||||
|
if !bytes.Equal(packed, ubytes) {
|
||||||
|
t.Errorf("expected %x got %x", ubytes, packed)
|
||||||
|
}
|
||||||
|
packed = packNum(reflect.ValueOf(-1), IntTy)
|
||||||
|
if !bytes.Equal(packed, sbytesmin) {
|
||||||
|
t.Errorf("expected %x got %x", ubytes, packed)
|
||||||
|
}
|
||||||
|
packed = packNum(reflect.ValueOf(1), UintTy)
|
||||||
|
if !bytes.Equal(packed, ubytes) {
|
||||||
|
t.Errorf("expected %x got %x", ubytes, packed)
|
||||||
|
}
|
||||||
|
packed = packNum(reflect.ValueOf(-1), UintTy)
|
||||||
|
if !bytes.Equal(packed, maxunsigned) {
|
||||||
|
t.Errorf("expected %x got %x", maxunsigned, packed)
|
||||||
|
}
|
||||||
|
|
||||||
|
packed = packNum(reflect.ValueOf("string"), UintTy)
|
||||||
|
if packed != nil {
|
||||||
|
t.Errorf("expected 'string' to pack to nil. got %x instead", packed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSigned(t *testing.T) {
|
||||||
|
if isSigned(reflect.ValueOf(uint(10))) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isSigned(reflect.ValueOf(int(10))) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isSigned(reflect.ValueOf(big.NewInt(10))) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
106
accounts/abi/profile.cov
Normal file
106
accounts/abi/profile.cov
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
mode: set
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:27.39,31.32 4 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:35.2,37.8 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:31.32,34.3 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:40.29,42.2 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:51.53,57.16 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:61.2,62.16 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:65.2,67.12 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:57.16,59.3 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:62.16,64.3 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:79.71,83.25 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:94.2,94.17 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:83.25,87.17 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:90.3,90.31 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:87.17,89.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:102.71,104.12 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:109.2,109.36 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:113.2,114.16 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:119.2,122.20 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:104.12,106.3 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:109.36,111.3 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:114.16,116.3 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:125.50,127.55 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:131.2,132.33 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:136.2,136.12 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:127.55,129.3 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:132.33,134.3 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:139.42,143.41 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:147.2,147.17 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/abi.go:143.41,145.3 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:39.30,41.2 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:43.30,46.32 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:56.2,56.12 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:46.32,47.25 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:47.25,48.14 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:52.4,52.9 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:48.14,50.13 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:59.30,61.2 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:63.29,65.2 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:67.51,68.36 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:91.2,91.12 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:69.2,70.19 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:75.2,76.19 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:81.2,83.19 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:70.19,72.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:72.4,74.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:76.19,78.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:78.4,80.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:83.19,85.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:85.4,87.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:94.37,95.18 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:101.2,101.14 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:96.2,97.14 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/numbers.go:98.2,99.14 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:32.46,35.16 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:38.2,43.9 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:55.2,56.16 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:60.2,64.55 4 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:69.2,69.13 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:111.2,113.8 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:35.16,37.3 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:44.2,47.17 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:48.2,50.12 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:51.2,52.60 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:56.16,58.3 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:64.55,67.3 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:69.13,72.16 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:73.3,74.21 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:75.3,76.22 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:77.3,78.66 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:80.3,81.16 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:82.3,86.17 4 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:87.3,91.18 4 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:92.3,93.27 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:94.3,95.30 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:96.3,100.21 4 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:101.3,104.17 3 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:107.3,108.60 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:104.17,106.5 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:116.37,118.2 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:126.51,128.36 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:179.2,179.20 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:129.2,130.23 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:133.3,133.34 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:134.2,135.23 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:138.3,138.34 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:139.2,142.49 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:145.3,145.34 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:146.2,147.42 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:150.3,150.60 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:151.2,152.42 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:157.3,157.23 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:162.3,162.78 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:166.3,167.36 2 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:170.3,170.21 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:171.2,172.19 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:130.23,132.4 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:135.23,137.4 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:142.49,144.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:147.42,149.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:152.42,154.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:157.23,159.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:162.78,164.4 1 0
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:167.36,169.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:172.19,174.4 1 1
|
||||||
|
github.com/ethereum/go-ethereum/abi/type.go:174.4,176.4 1 0
|
||||||
190
accounts/abi/type.go
Normal file
190
accounts/abi/type.go
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IntTy byte = iota
|
||||||
|
UintTy
|
||||||
|
BoolTy
|
||||||
|
SliceTy
|
||||||
|
AddressTy
|
||||||
|
RealTy
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type is the reflection of the supported argument type
|
||||||
|
type Type struct {
|
||||||
|
Kind reflect.Kind
|
||||||
|
Type reflect.Type
|
||||||
|
Size int
|
||||||
|
T byte // Our own type checking
|
||||||
|
stringKind string // holds the unparsed string for deriving signatures
|
||||||
|
}
|
||||||
|
|
||||||
|
// New type returns a fully parsed Type given by the input string or an error if it can't be parsed.
|
||||||
|
//
|
||||||
|
// Strings can be in the format of:
|
||||||
|
//
|
||||||
|
// Input = Type [ "[" [ Number ] "]" ] Name .
|
||||||
|
// Type = [ "u" ] "int" [ Number ] .
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// string int uint real
|
||||||
|
// string32 int8 uint8 uint[]
|
||||||
|
// address int256 uint256 real[2]
|
||||||
|
func NewType(t string) (typ Type, err error) {
|
||||||
|
// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
|
||||||
|
freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
|
||||||
|
if err != nil {
|
||||||
|
return Type{}, err
|
||||||
|
}
|
||||||
|
res := freg.FindAllStringSubmatch(t, -1)[0]
|
||||||
|
var (
|
||||||
|
isslice bool
|
||||||
|
size int
|
||||||
|
)
|
||||||
|
switch {
|
||||||
|
case res[3] != "":
|
||||||
|
// err is ignored. Already checked for number through the regexp
|
||||||
|
size, _ = strconv.Atoi(res[3])
|
||||||
|
isslice = true
|
||||||
|
case res[2] != "":
|
||||||
|
isslice = true
|
||||||
|
size = -1
|
||||||
|
case res[0] == "":
|
||||||
|
return Type{}, fmt.Errorf("type parse error for `%s`", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?")
|
||||||
|
if err != nil {
|
||||||
|
return Type{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedType := treg.FindAllStringSubmatch(res[1], -1)[0]
|
||||||
|
vsize, _ := strconv.Atoi(parsedType[2])
|
||||||
|
vtype := parsedType[1]
|
||||||
|
// substitute canonical representation
|
||||||
|
if vsize == 0 && (vtype == "int" || vtype == "uint") {
|
||||||
|
vsize = 256
|
||||||
|
t += "256"
|
||||||
|
}
|
||||||
|
|
||||||
|
if isslice {
|
||||||
|
typ.Kind = reflect.Slice
|
||||||
|
typ.Size = size
|
||||||
|
switch vtype {
|
||||||
|
case "int":
|
||||||
|
typ.Type = big_ts
|
||||||
|
case "uint":
|
||||||
|
typ.Type = ubig_ts
|
||||||
|
default:
|
||||||
|
return Type{}, fmt.Errorf("unsupported arg slice type: %s", t)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch vtype {
|
||||||
|
case "int":
|
||||||
|
typ.Kind = reflect.Ptr
|
||||||
|
typ.Type = big_t
|
||||||
|
typ.Size = 256
|
||||||
|
typ.T = IntTy
|
||||||
|
case "uint":
|
||||||
|
typ.Kind = reflect.Ptr
|
||||||
|
typ.Type = ubig_t
|
||||||
|
typ.Size = 256
|
||||||
|
typ.T = UintTy
|
||||||
|
case "bool":
|
||||||
|
typ.Kind = reflect.Bool
|
||||||
|
case "real": // TODO
|
||||||
|
typ.Kind = reflect.Invalid
|
||||||
|
case "address":
|
||||||
|
typ.Kind = reflect.Slice
|
||||||
|
typ.Type = byte_ts
|
||||||
|
typ.Size = 20
|
||||||
|
typ.T = AddressTy
|
||||||
|
case "string":
|
||||||
|
typ.Kind = reflect.String
|
||||||
|
typ.Size = -1
|
||||||
|
if vsize > 0 {
|
||||||
|
typ.Size = 32
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typ.stringKind = t
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Type) String() (out string) {
|
||||||
|
return t.stringKind
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the given input parameter `v` and checks if it matches certain
|
||||||
|
// criteria
|
||||||
|
// * Big integers are checks for ptr types and if the given value is
|
||||||
|
// assignable
|
||||||
|
// * Integer are checked for size
|
||||||
|
// * Strings, addresses and bytes are checks for type and size
|
||||||
|
func (t Type) pack(v interface{}) ([]byte, error) {
|
||||||
|
value := reflect.ValueOf(v)
|
||||||
|
switch kind := value.Kind(); kind {
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
if t.Type != ubig_t {
|
||||||
|
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
|
||||||
|
}
|
||||||
|
return packNum(value, t.T), nil
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
if t.Type != ubig_t {
|
||||||
|
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
|
||||||
|
}
|
||||||
|
return packNum(value, t.T), nil
|
||||||
|
case reflect.Ptr:
|
||||||
|
// If the value is a ptr do a assign check (only used by
|
||||||
|
// big.Int for now)
|
||||||
|
if t.Type == ubig_t && value.Type() != ubig_t {
|
||||||
|
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
|
||||||
|
}
|
||||||
|
return packNum(value, t.T), nil
|
||||||
|
case reflect.String:
|
||||||
|
if t.Size > -1 && value.Len() > t.Size {
|
||||||
|
return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
|
||||||
|
}
|
||||||
|
return []byte(ethutil.LeftPadString(t.String(), 32)), nil
|
||||||
|
case reflect.Slice:
|
||||||
|
if t.Size > -1 && value.Len() > t.Size {
|
||||||
|
return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address is a special slice. The slice acts as one rather than a list of elements.
|
||||||
|
if t.T == AddressTy {
|
||||||
|
return ethutil.LeftPadBytes(v.([]byte), 32), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signed / Unsigned check
|
||||||
|
if (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) {
|
||||||
|
return nil, fmt.Errorf("slice of incompatible types.")
|
||||||
|
}
|
||||||
|
|
||||||
|
var packed []byte
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
packed = append(packed, packNum(value.Index(i), t.T)...)
|
||||||
|
}
|
||||||
|
return packed, nil
|
||||||
|
case reflect.Bool:
|
||||||
|
if value.Bool() {
|
||||||
|
return ethutil.LeftPadBytes(ethutil.Big1.Bytes(), 32), nil
|
||||||
|
} else {
|
||||||
|
return ethutil.LeftPadBytes(ethutil.Big0.Bytes(), 32), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unreached")
|
||||||
|
}
|
||||||
95
accounts/account_manager.go
Normal file
95
accounts/account_manager.go
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
This file is part of go-ethereum
|
||||||
|
|
||||||
|
go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @authors
|
||||||
|
* Gustav Simonsson <gustav.simonsson@gmail.com>
|
||||||
|
* @date 2015
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
|
||||||
|
This abstracts part of a user's interaction with an account she controls.
|
||||||
|
It's not an abstraction of core Ethereum accounts data type / logic -
|
||||||
|
for that see the core processing code of blocks / txs.
|
||||||
|
|
||||||
|
Currently this is pretty much a passthrough to the KeyStore2 interface,
|
||||||
|
and accounts persistence is derived from stored keys' addresses
|
||||||
|
|
||||||
|
*/
|
||||||
|
package accounts
|
||||||
|
|
||||||
|
import (
|
||||||
|
crand "crypto/rand"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: better name for this struct?
|
||||||
|
type Account struct {
|
||||||
|
Address []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountManager struct {
|
||||||
|
keyStore crypto.KeyStore2
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: get key by addr - modify KeyStore2 GetKey to work with addr
|
||||||
|
|
||||||
|
// TODO: pass through passphrase for APIs which require access to private key?
|
||||||
|
func NewAccountManager(keyStore crypto.KeyStore2) AccountManager {
|
||||||
|
am := &AccountManager{
|
||||||
|
keyStore: keyStore,
|
||||||
|
}
|
||||||
|
return *am
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *AccountManager) Sign(fromAccount *Account, keyAuth string, toSign []byte) (signature []byte, err error) {
|
||||||
|
key, err := am.keyStore.GetKey(fromAccount.Address, keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signature, err = crypto.Sign(toSign, key.PrivateKey)
|
||||||
|
return signature, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AccountManager) NewAccount(auth string) (*Account, error) {
|
||||||
|
key, err := am.keyStore.GenerateNewKey(crand.Reader, auth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ua := &Account{
|
||||||
|
Address: key.Address,
|
||||||
|
}
|
||||||
|
return ua, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set of accounts == set of keys in given key store
|
||||||
|
// TODO: do we need persistence of accounts as well?
|
||||||
|
func (am *AccountManager) Accounts() ([]Account, error) {
|
||||||
|
addresses, err := am.keyStore.GetKeyAddresses()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts := make([]Account, len(addresses))
|
||||||
|
|
||||||
|
for i, addr := range addresses {
|
||||||
|
accounts[i] = Account{
|
||||||
|
Address: addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accounts, err
|
||||||
|
}
|
||||||
18
accounts/accounts_test.go
Normal file
18
accounts/accounts_test.go
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package accounts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccountManager(t *testing.T) {
|
||||||
|
ks := crypto.NewKeyStorePlain(crypto.DefaultDataDir())
|
||||||
|
am := NewAccountManager(ks)
|
||||||
|
pass := "" // not used but required by API
|
||||||
|
a1, err := am.NewAccount(pass)
|
||||||
|
toSign := crypto.GetEntropyCSPRNG(32)
|
||||||
|
_, err = am.Sign(a1, pass, toSign)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
5
cmd/mist/assets/ext/.bowerrc
Normal file
5
cmd/mist/assets/ext/.bowerrc
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"directory": "example/js/",
|
||||||
|
"cwd": "./",
|
||||||
|
"analytics": false
|
||||||
|
}
|
||||||
12
cmd/mist/assets/ext/.editorconfig
Normal file
12
cmd/mist/assets/ext/.editorconfig
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
18
cmd/mist/assets/ext/.gitignore
vendored
Normal file
18
cmd/mist/assets/ext/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
#
|
||||||
|
# If you find yourself ignoring temporary files generated by your text editor
|
||||||
|
# or operating system, you probably want to add a global ignore instead:
|
||||||
|
# git config --global core.excludesfile ~/.gitignore_global
|
||||||
|
|
||||||
|
*.swp
|
||||||
|
/tmp
|
||||||
|
*/**/*un~
|
||||||
|
*un~
|
||||||
|
.DS_Store
|
||||||
|
*/**/.DS_Store
|
||||||
|
ethereum/ethereum
|
||||||
|
ethereal/ethereal
|
||||||
|
example/js
|
||||||
|
node_modules
|
||||||
|
bower_components
|
||||||
|
npm-debug.log
|
||||||
50
cmd/mist/assets/ext/.jshintrc
Normal file
50
cmd/mist/assets/ext/.jshintrc
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"predef": [
|
||||||
|
"console",
|
||||||
|
"require",
|
||||||
|
"equal",
|
||||||
|
"test",
|
||||||
|
"testBoth",
|
||||||
|
"testWithDefault",
|
||||||
|
"raises",
|
||||||
|
"deepEqual",
|
||||||
|
"start",
|
||||||
|
"stop",
|
||||||
|
"ok",
|
||||||
|
"strictEqual",
|
||||||
|
"module",
|
||||||
|
"expect",
|
||||||
|
"reject",
|
||||||
|
"impl"
|
||||||
|
],
|
||||||
|
|
||||||
|
"esnext": true,
|
||||||
|
"proto": true,
|
||||||
|
"node" : true,
|
||||||
|
"browser" : true,
|
||||||
|
"browserify" : true,
|
||||||
|
|
||||||
|
"boss" : true,
|
||||||
|
"curly": false,
|
||||||
|
"debug": true,
|
||||||
|
"devel": true,
|
||||||
|
"eqeqeq": true,
|
||||||
|
"evil": true,
|
||||||
|
"forin": false,
|
||||||
|
"immed": false,
|
||||||
|
"laxbreak": false,
|
||||||
|
"newcap": true,
|
||||||
|
"noarg": true,
|
||||||
|
"noempty": false,
|
||||||
|
"nonew": false,
|
||||||
|
"nomen": false,
|
||||||
|
"onevar": false,
|
||||||
|
"plusplus": false,
|
||||||
|
"regexp": false,
|
||||||
|
"undef": true,
|
||||||
|
"sub": true,
|
||||||
|
"strict": false,
|
||||||
|
"white": false,
|
||||||
|
"shadow": true,
|
||||||
|
"eqnull": true
|
||||||
|
}
|
||||||
9
cmd/mist/assets/ext/.npmignore
Normal file
9
cmd/mist/assets/ext/.npmignore
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
example/js
|
||||||
|
node_modules
|
||||||
|
test
|
||||||
|
.gitignore
|
||||||
|
.editorconfig
|
||||||
|
.travis.yml
|
||||||
|
.npmignore
|
||||||
|
component.json
|
||||||
|
testling.html
|
||||||
13
cmd/mist/assets/ext/.travis.yml
Normal file
13
cmd/mist/assets/ext/.travis.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "0.11"
|
||||||
|
- "0.10"
|
||||||
|
before_script:
|
||||||
|
- npm install
|
||||||
|
- npm install jshint
|
||||||
|
script:
|
||||||
|
- "jshint *.js lib"
|
||||||
|
after_script:
|
||||||
|
- npm run-script build
|
||||||
|
- npm test
|
||||||
|
|
||||||
14
cmd/mist/assets/ext/LICENSE
Normal file
14
cmd/mist/assets/ext/LICENSE
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
This file is part of ethereum.js.
|
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
96
cmd/mist/assets/ext/README.md
Normal file
96
cmd/mist/assets/ext/README.md
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# Ethereum JavaScript API
|
||||||
|
|
||||||
|
This is the Ethereum compatible [JavaScript API](https://github.com/ethereum/wiki/wiki/JavaScript-API)
|
||||||
|
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js
|
||||||
|
|
||||||
|
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url]
|
||||||
|
|
||||||
|
<!-- [](https://ci.testling.com/ethereum/ethereum.js) -->
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Node.js
|
||||||
|
|
||||||
|
npm install ethereum.js
|
||||||
|
|
||||||
|
### For browser
|
||||||
|
Bower
|
||||||
|
|
||||||
|
bower install ethereum.js
|
||||||
|
|
||||||
|
Component
|
||||||
|
|
||||||
|
component install ethereum/ethereum.js
|
||||||
|
|
||||||
|
* Include `ethereum.min.js` in your html file.
|
||||||
|
* Include [bignumber.js](https://github.com/MikeMcl/bignumber.js/)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Require the library:
|
||||||
|
|
||||||
|
var web3 = require('web3');
|
||||||
|
|
||||||
|
Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider)
|
||||||
|
|
||||||
|
var web3.setProvider(new web3.providers.WebSocketProvider('ws://localhost:40404/eth'));
|
||||||
|
|
||||||
|
There you go, now you can use it:
|
||||||
|
|
||||||
|
```
|
||||||
|
var coinbase = web3.eth.coinbase;
|
||||||
|
var balance = web3.eth.balanceAt(coinbase);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
For another example see `example/index.html`.
|
||||||
|
|
||||||
|
## Contribute!
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
* Node.js
|
||||||
|
* npm
|
||||||
|
* gulp (build)
|
||||||
|
* mocha (tests)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install nodejs
|
||||||
|
sudo apt-get install npm
|
||||||
|
sudo apt-get install nodejs-legacy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building (gulp)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run-script build
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Testing (mocha)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Please note this repo is in it's early stage.**
|
||||||
|
|
||||||
|
If you'd like to run a WebSocket ethereum node check out
|
||||||
|
[go-ethereum](https://github.com/ethereum/go-ethereum).
|
||||||
|
|
||||||
|
To install ethereum and spawn a node:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/ethereum/go-ethereum/ethereum
|
||||||
|
ethereum -ws -loglevel=4
|
||||||
|
```
|
||||||
|
|
||||||
|
[npm-image]: https://badge.fury.io/js/ethereum.js.png
|
||||||
|
[npm-url]: https://npmjs.org/package/ethereum.js
|
||||||
|
[travis-image]: https://travis-ci.org/ethereum/ethereum.js.svg
|
||||||
|
[travis-url]: https://travis-ci.org/ethereum/ethereum.js
|
||||||
|
[dep-image]: https://david-dm.org/ethereum/ethereum.js.svg
|
||||||
|
[dep-url]: https://david-dm.org/ethereum/ethereum.js
|
||||||
|
[dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg
|
||||||
|
[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies
|
||||||
|
|
||||||
51
cmd/mist/assets/ext/bower.json
Normal file
51
cmd/mist/assets/ext/bower.json
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"name": "ethereum.js",
|
||||||
|
"namespace": "ethereum",
|
||||||
|
"version": "0.0.10",
|
||||||
|
"description": "Ethereum Compatible JavaScript API",
|
||||||
|
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
|
||||||
|
"dependencies": {
|
||||||
|
"bignumber.js": ">=2.0.0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ethereum/ethereum.js.git"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/ethereum/ethereum.js",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/ethereum/ethereum.js/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"ethereum",
|
||||||
|
"javascript",
|
||||||
|
"API"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Marek Kotewicz",
|
||||||
|
"email": "marek@ethdev.com",
|
||||||
|
"homepage": "https://github.com/debris"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Marian Oancea",
|
||||||
|
"email": "marian@ethdev.com",
|
||||||
|
"homepage": "https://github.com/cubedro"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0",
|
||||||
|
"ignore": [
|
||||||
|
"example",
|
||||||
|
"lib",
|
||||||
|
"node_modules",
|
||||||
|
"package.json",
|
||||||
|
".bowerrc",
|
||||||
|
".editorconfig",
|
||||||
|
".gitignore",
|
||||||
|
".jshintrc",
|
||||||
|
".npmignore",
|
||||||
|
".travis.yml",
|
||||||
|
"gulpfile.js",
|
||||||
|
"index.js",
|
||||||
|
"**/*.txt"
|
||||||
|
]
|
||||||
|
}
|
||||||
1198
cmd/mist/assets/ext/dist/ethereum.js
vendored
Normal file
1198
cmd/mist/assets/ext/dist/ethereum.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
29
cmd/mist/assets/ext/dist/ethereum.js.map
vendored
Normal file
29
cmd/mist/assets/ext/dist/ethereum.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
cmd/mist/assets/ext/dist/ethereum.min.js
vendored
Normal file
1
cmd/mist/assets/ext/dist/ethereum.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
39
cmd/mist/assets/ext/example/balance.html
Normal file
39
cmd/mist/assets/ext/example/balance.html
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!doctype>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../dist/ethereum.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var web3 = require('web3');
|
||||||
|
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080'));
|
||||||
|
|
||||||
|
function watchBalance() {
|
||||||
|
var coinbase = web3.eth.coinbase;
|
||||||
|
var originalBalance = 0;
|
||||||
|
|
||||||
|
var balance = web3.eth.balanceAt(coinbase);
|
||||||
|
var originalBalance = web3.toDecimal(balance);
|
||||||
|
document.getElementById('original').innerText = 'original balance: ' + originalBalance + ' watching...';
|
||||||
|
|
||||||
|
web3.eth.watch({altered: coinbase}).changed(function() {
|
||||||
|
balance = web3.eth.balanceAt(coinbase)
|
||||||
|
var currentBalance = web3.toDecimal(balance);
|
||||||
|
document.getElementById("current").innerText = 'current: ' + currentBalance;
|
||||||
|
document.getElementById("diff").innerText = 'diff: ' + (currentBalance - originalBalance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>coinbase balance</h1>
|
||||||
|
<button type="button" onClick="watchBalance();">watch balance</button>
|
||||||
|
<div></div>
|
||||||
|
<div id="original"></div>
|
||||||
|
<div id="current"></div>
|
||||||
|
<div id="diff"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
73
cmd/mist/assets/ext/example/contract.html
Normal file
73
cmd/mist/assets/ext/example/contract.html
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
<!doctype>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../dist/ethereum.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var web3 = require('web3');
|
||||||
|
web3.setProvider(new web3.providers.HttpSyncProvider());
|
||||||
|
|
||||||
|
// solidity source code
|
||||||
|
var source = "" +
|
||||||
|
"contract test {\n" +
|
||||||
|
" function multiply(uint a) returns(uint d) {\n" +
|
||||||
|
" return a * 7;\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
// contract description, this will be autogenerated somehow
|
||||||
|
var desc = [{
|
||||||
|
"name": "multiply(uint256)",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "d",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}];
|
||||||
|
|
||||||
|
var contract;
|
||||||
|
|
||||||
|
function createExampleContract() {
|
||||||
|
// hide create button
|
||||||
|
document.getElementById('create').style.visibility = 'hidden';
|
||||||
|
document.getElementById('source').innerText = source;
|
||||||
|
|
||||||
|
// create contract
|
||||||
|
var address = web3.eth.transact({code: web3.eth.solidity(source)});
|
||||||
|
contract = web3.eth.contract(address, desc);
|
||||||
|
document.getElementById('call').style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
|
||||||
|
function callExampleContract() {
|
||||||
|
// this should be generated by ethereum
|
||||||
|
var param = parseInt(document.getElementById('value').value);
|
||||||
|
|
||||||
|
// call the contract
|
||||||
|
var res = contract.call().multiply(param);
|
||||||
|
document.getElementById('result').innerText = res.toString(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>contract</h1>
|
||||||
|
<div id="source"></div>
|
||||||
|
<div id='create'>
|
||||||
|
<button type="button" onClick="createExampleContract();">create example contract</button>
|
||||||
|
</div>
|
||||||
|
<div id='call' style='visibility: hidden;'>
|
||||||
|
<input type="number" id="value" onkeyup='callExampleContract()'></input>
|
||||||
|
</div>
|
||||||
|
<div id="result"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
76
cmd/mist/assets/ext/example/natspec_contract.html
Normal file
76
cmd/mist/assets/ext/example/natspec_contract.html
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
<!doctype>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../dist/ethereum.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var web3 = require('web3');
|
||||||
|
web3.setProvider(new web3.providers.QtSyncProvider());
|
||||||
|
|
||||||
|
// solidity source code
|
||||||
|
var source = "" +
|
||||||
|
"contract test {\n" +
|
||||||
|
" /// @notice Will multiply `a` by 7. \n" +
|
||||||
|
" function multiply(uint a) returns(uint d) {\n" +
|
||||||
|
" return a * 7;\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
// contract description, this will be autogenerated somehow
|
||||||
|
var desc = [{
|
||||||
|
"name": "multiply(uint256)",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "d",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}];
|
||||||
|
|
||||||
|
var contract;
|
||||||
|
|
||||||
|
function createExampleContract() {
|
||||||
|
// hide create button
|
||||||
|
document.getElementById('create').style.visibility = 'hidden';
|
||||||
|
document.getElementById('source').innerText = source;
|
||||||
|
|
||||||
|
// create contract
|
||||||
|
var address = web3.eth.transact({code: web3.eth.solidity(source)});
|
||||||
|
contract = web3.eth.contract(address, desc);
|
||||||
|
document.getElementById('call').style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
|
||||||
|
function callExampleContract() {
|
||||||
|
// this should be generated by ethereum
|
||||||
|
var param = parseInt(document.getElementById('value').value);
|
||||||
|
|
||||||
|
// transaction does not return any result, cause it's not synchronous and we don't know,
|
||||||
|
// when it will be processed
|
||||||
|
contract.transact().multiply(param);
|
||||||
|
document.getElementById('result').innerText = 'transaction made';
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>contract</h1>
|
||||||
|
<div id="source"></div>
|
||||||
|
<div id='create'>
|
||||||
|
<button type="button" onClick="createExampleContract();">create example contract</button>
|
||||||
|
</div>
|
||||||
|
<div id='call' style='visibility: hidden;'>
|
||||||
|
<input type="number" id="value"></input>
|
||||||
|
<button type="button" onClick="callExampleContract()">Call Contract</button>
|
||||||
|
</div>
|
||||||
|
<div id="result"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
12
cmd/mist/assets/ext/example/node-app.js
Normal file
12
cmd/mist/assets/ext/example/node-app.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var web3 = require("../index.js");
|
||||||
|
|
||||||
|
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080'));
|
||||||
|
|
||||||
|
var coinbase = web3.eth.coinbase;
|
||||||
|
console.log(coinbase);
|
||||||
|
|
||||||
|
var balance = web3.eth.balanceAt(coinbase);
|
||||||
|
console.log(balance);
|
||||||
|
|
||||||
104
cmd/mist/assets/ext/gulpfile.js
Normal file
104
cmd/mist/assets/ext/gulpfile.js
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var del = require('del');
|
||||||
|
var gulp = require('gulp');
|
||||||
|
var browserify = require('browserify');
|
||||||
|
var jshint = require('gulp-jshint');
|
||||||
|
var uglify = require('gulp-uglify');
|
||||||
|
var rename = require('gulp-rename');
|
||||||
|
var envify = require('envify/custom');
|
||||||
|
var unreach = require('unreachable-branch-transform');
|
||||||
|
var source = require('vinyl-source-stream');
|
||||||
|
var exorcist = require('exorcist');
|
||||||
|
var bower = require('bower');
|
||||||
|
|
||||||
|
var DEST = './dist/';
|
||||||
|
|
||||||
|
var build = function(src, dst, ugly) {
|
||||||
|
var result = browserify({
|
||||||
|
debug: true,
|
||||||
|
insert_global_vars: false,
|
||||||
|
detectGlobals: false,
|
||||||
|
bundleExternal: false
|
||||||
|
})
|
||||||
|
.require('./' + src + '.js', {expose: 'web3'})
|
||||||
|
.add('./' + src + '.js')
|
||||||
|
.transform('envify', {
|
||||||
|
NODE_ENV: 'build'
|
||||||
|
})
|
||||||
|
.transform('unreachable-branch-transform');
|
||||||
|
|
||||||
|
if (ugly) {
|
||||||
|
result = result.transform('uglifyify', {
|
||||||
|
mangle: false,
|
||||||
|
compress: {
|
||||||
|
dead_code: false,
|
||||||
|
conditionals: true,
|
||||||
|
unused: false,
|
||||||
|
hoist_funs: true,
|
||||||
|
hoist_vars: true,
|
||||||
|
negate_iife: false
|
||||||
|
},
|
||||||
|
beautify: true,
|
||||||
|
warnings: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.bundle()
|
||||||
|
.pipe(exorcist(path.join( DEST, dst + '.js.map')))
|
||||||
|
.pipe(source(dst + '.js'))
|
||||||
|
.pipe(gulp.dest( DEST ));
|
||||||
|
};
|
||||||
|
|
||||||
|
var uglifyFile = function(file) {
|
||||||
|
return gulp.src( DEST + file + '.js')
|
||||||
|
.pipe(uglify())
|
||||||
|
.pipe(rename(file + '.min.js'))
|
||||||
|
.pipe(gulp.dest( DEST ));
|
||||||
|
};
|
||||||
|
|
||||||
|
gulp.task('bower', function(cb){
|
||||||
|
bower.commands.install().on('end', function (installed){
|
||||||
|
console.log(installed);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('clean', ['lint'], function(cb) {
|
||||||
|
del([ DEST ], cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('lint', function(){
|
||||||
|
return gulp.src(['./*.js', './lib/*.js'])
|
||||||
|
.pipe(jshint())
|
||||||
|
.pipe(jshint.reporter('default'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('build', ['clean'], function () {
|
||||||
|
return build('index', 'ethereum', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('buildDev', ['clean'], function () {
|
||||||
|
return build('index', 'ethereum', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('uglify', ['build'], function(){
|
||||||
|
return uglifyFile('ethereum');
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('uglifyDev', ['buildDev'], function(){
|
||||||
|
return uglifyFile('ethereum');
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('watch', function() {
|
||||||
|
gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('release', ['bower', 'lint', 'build', 'uglify']);
|
||||||
|
gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglifyDev']);
|
||||||
|
gulp.task('default', ['dev']);
|
||||||
|
|
||||||
11
cmd/mist/assets/ext/index.js
Normal file
11
cmd/mist/assets/ext/index.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
var web3 = require('./lib/web3');
|
||||||
|
var ProviderManager = require('./lib/providermanager');
|
||||||
|
web3.provider = new ProviderManager();
|
||||||
|
web3.filter = require('./lib/filter');
|
||||||
|
web3.providers.HttpSyncProvider = require('./lib/httpsync');
|
||||||
|
web3.providers.QtSyncProvider = require('./lib/qtsync');
|
||||||
|
web3.eth.contract = require('./lib/contract');
|
||||||
|
web3.abi = require('./lib/abi');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = web3;
|
||||||
410
cmd/mist/assets/ext/lib/abi.js
Normal file
410
cmd/mist/assets/ext/lib/abi.js
Normal file
|
|
@ -0,0 +1,410 @@
|
||||||
|
/*
|
||||||
|
This file is part of ethereum.js.
|
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file abi.js
|
||||||
|
* @authors:
|
||||||
|
* Marek Kotewicz <marek@ethdev.com>
|
||||||
|
* Gav Wood <g@ethdev.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: is these line is supposed to be here?
|
||||||
|
if (process.env.NODE_ENV !== 'build') {
|
||||||
|
var BigNumber = require('bignumber.js'); // jshint ignore:line
|
||||||
|
}
|
||||||
|
|
||||||
|
var web3 = require('./web3'); // jshint ignore:line
|
||||||
|
|
||||||
|
BigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_DOWN });
|
||||||
|
|
||||||
|
var ETH_PADDING = 32;
|
||||||
|
|
||||||
|
/// method signature length in bytes
|
||||||
|
var ETH_METHOD_SIGNATURE_LENGTH = 4;
|
||||||
|
|
||||||
|
/// Finds first index of array element matching pattern
|
||||||
|
/// @param array
|
||||||
|
/// @param callback pattern
|
||||||
|
/// @returns index of element
|
||||||
|
var findIndex = function (array, callback) {
|
||||||
|
var end = false;
|
||||||
|
var i = 0;
|
||||||
|
for (; i < array.length && !end; i++) {
|
||||||
|
end = callback(array[i]);
|
||||||
|
}
|
||||||
|
return end ? i - 1 : -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns a function that is used as a pattern for 'findIndex'
|
||||||
|
var findMethodIndex = function (json, methodName) {
|
||||||
|
return findIndex(json, function (method) {
|
||||||
|
return method.name === methodName;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns method with given method name
|
||||||
|
var getMethodWithName = function (json, methodName) {
|
||||||
|
var index = findMethodIndex(json, methodName);
|
||||||
|
if (index === -1) {
|
||||||
|
console.error('method ' + methodName + ' not found in the abi');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return json[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @param string string to be padded
|
||||||
|
/// @param number of characters that result string should have
|
||||||
|
/// @param sign, by default 0
|
||||||
|
/// @returns right aligned string
|
||||||
|
var padLeft = function (string, chars, sign) {
|
||||||
|
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @param expected type prefix (string)
|
||||||
|
/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false
|
||||||
|
var prefixedType = function (prefix) {
|
||||||
|
return function (type) {
|
||||||
|
return type.indexOf(prefix) === 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @param expected type name (string)
|
||||||
|
/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false
|
||||||
|
var namedType = function (name) {
|
||||||
|
return function (type) {
|
||||||
|
return name === type;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var arrayType = function (type) {
|
||||||
|
return type.slice(-2) === '[]';
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Formats input value to byte representation of int
|
||||||
|
/// If value is negative, return it's two's complement
|
||||||
|
/// If the value is floating point, round it down
|
||||||
|
/// @returns right-aligned byte representation of int
|
||||||
|
var formatInputInt = function (value) {
|
||||||
|
var padding = ETH_PADDING * 2;
|
||||||
|
if (value instanceof BigNumber || typeof value === 'number') {
|
||||||
|
if (typeof value === 'number')
|
||||||
|
value = new BigNumber(value);
|
||||||
|
value = value.round();
|
||||||
|
|
||||||
|
if (value.lessThan(0))
|
||||||
|
value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1);
|
||||||
|
value = value.toString(16);
|
||||||
|
}
|
||||||
|
else if (value.indexOf('0x') === 0)
|
||||||
|
value = value.substr(2);
|
||||||
|
else if (typeof value === 'string')
|
||||||
|
value = formatInputInt(new BigNumber(value));
|
||||||
|
else
|
||||||
|
value = (+value).toString(16);
|
||||||
|
return padLeft(value, padding);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Formats input value to byte representation of string
|
||||||
|
/// @returns left-algined byte representation of string
|
||||||
|
var formatInputString = function (value) {
|
||||||
|
return web3.fromAscii(value, ETH_PADDING).substr(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Formats input value to byte representation of bool
|
||||||
|
/// @returns right-aligned byte representation bool
|
||||||
|
var formatInputBool = function (value) {
|
||||||
|
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Formats input value to byte representation of real
|
||||||
|
/// Values are multiplied by 2^m and encoded as integers
|
||||||
|
/// @returns byte representation of real
|
||||||
|
var formatInputReal = function (value) {
|
||||||
|
return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128)));
|
||||||
|
};
|
||||||
|
|
||||||
|
var dynamicTypeBytes = function (type, value) {
|
||||||
|
// TODO: decide what to do with array of strings
|
||||||
|
if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.
|
||||||
|
return formatInputInt(value.length);
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Setups input formatters for solidity types
|
||||||
|
/// @returns an array of input formatters
|
||||||
|
var setupInputTypes = function () {
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ type: prefixedType('uint'), format: formatInputInt },
|
||||||
|
{ type: prefixedType('int'), format: formatInputInt },
|
||||||
|
{ type: prefixedType('hash'), format: formatInputInt },
|
||||||
|
{ type: prefixedType('string'), format: formatInputString },
|
||||||
|
{ type: prefixedType('real'), format: formatInputReal },
|
||||||
|
{ type: prefixedType('ureal'), format: formatInputReal },
|
||||||
|
{ type: namedType('address'), format: formatInputInt },
|
||||||
|
{ type: namedType('bool'), format: formatInputBool }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
var inputTypes = setupInputTypes();
|
||||||
|
|
||||||
|
/// Formats input params to bytes
|
||||||
|
/// @param contract json abi
|
||||||
|
/// @param name of the method that we want to use
|
||||||
|
/// @param array of params that will be formatted to bytes
|
||||||
|
/// @returns bytes representation of input params
|
||||||
|
var toAbiInput = function (json, methodName, params) {
|
||||||
|
var bytes = "";
|
||||||
|
|
||||||
|
var method = getMethodWithName(json, methodName);
|
||||||
|
var padding = ETH_PADDING * 2;
|
||||||
|
|
||||||
|
/// first we iterate in search for dynamic
|
||||||
|
method.inputs.forEach(function (input, index) {
|
||||||
|
bytes += dynamicTypeBytes(input.type, params[index]);
|
||||||
|
});
|
||||||
|
|
||||||
|
method.inputs.forEach(function (input, i) {
|
||||||
|
var typeMatch = false;
|
||||||
|
for (var j = 0; j < inputTypes.length && !typeMatch; j++) {
|
||||||
|
typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);
|
||||||
|
}
|
||||||
|
if (!typeMatch) {
|
||||||
|
console.error('input parser does not support type: ' + method.inputs[i].type);
|
||||||
|
}
|
||||||
|
|
||||||
|
var formatter = inputTypes[j - 1].format;
|
||||||
|
var toAppend = "";
|
||||||
|
|
||||||
|
if (arrayType(method.inputs[i].type))
|
||||||
|
toAppend = params[i].reduce(function (acc, curr) {
|
||||||
|
return acc + formatter(curr);
|
||||||
|
}, "");
|
||||||
|
else
|
||||||
|
toAppend = formatter(params[i]);
|
||||||
|
|
||||||
|
bytes += toAppend;
|
||||||
|
});
|
||||||
|
return bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Check if input value is negative
|
||||||
|
/// @param value is hex format
|
||||||
|
/// @returns true if it is negative, otherwise false
|
||||||
|
var signedIsNegative = function (value) {
|
||||||
|
return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Formats input right-aligned input bytes to int
|
||||||
|
/// @returns right-aligned input bytes formatted to int
|
||||||
|
var formatOutputInt = function (value) {
|
||||||
|
value = value || "0";
|
||||||
|
// check if it's negative number
|
||||||
|
// it it is, return two's complement
|
||||||
|
if (signedIsNegative(value)) {
|
||||||
|
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);
|
||||||
|
}
|
||||||
|
return new BigNumber(value, 16);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Formats big right-aligned input bytes to uint
|
||||||
|
/// @returns right-aligned input bytes formatted to uint
|
||||||
|
var formatOutputUInt = function (value) {
|
||||||
|
value = value || "0";
|
||||||
|
return new BigNumber(value, 16);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns input bytes formatted to real
|
||||||
|
var formatOutputReal = function (value) {
|
||||||
|
return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128));
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns input bytes formatted to ureal
|
||||||
|
var formatOutputUReal = function (value) {
|
||||||
|
return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128));
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns right-aligned input bytes formatted to hex
|
||||||
|
var formatOutputHash = function (value) {
|
||||||
|
return "0x" + value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns right-aligned input bytes formatted to bool
|
||||||
|
var formatOutputBool = function (value) {
|
||||||
|
return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns left-aligned input bytes formatted to ascii string
|
||||||
|
var formatOutputString = function (value) {
|
||||||
|
return web3.toAscii(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns right-aligned input bytes formatted to address
|
||||||
|
var formatOutputAddress = function (value) {
|
||||||
|
return "0x" + value.slice(value.length - 40, value.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
var dynamicBytesLength = function (type) {
|
||||||
|
if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.
|
||||||
|
return ETH_PADDING * 2;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Setups output formaters for solidity types
|
||||||
|
/// @returns an array of output formatters
|
||||||
|
var setupOutputTypes = function () {
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ type: prefixedType('uint'), format: formatOutputUInt },
|
||||||
|
{ type: prefixedType('int'), format: formatOutputInt },
|
||||||
|
{ type: prefixedType('hash'), format: formatOutputHash },
|
||||||
|
{ type: prefixedType('string'), format: formatOutputString },
|
||||||
|
{ type: prefixedType('real'), format: formatOutputReal },
|
||||||
|
{ type: prefixedType('ureal'), format: formatOutputUReal },
|
||||||
|
{ type: namedType('address'), format: formatOutputAddress },
|
||||||
|
{ type: namedType('bool'), format: formatOutputBool }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
var outputTypes = setupOutputTypes();
|
||||||
|
|
||||||
|
/// Formats output bytes back to param list
|
||||||
|
/// @param contract json abi
|
||||||
|
/// @param name of the method that we want to use
|
||||||
|
/// @param bytes representtion of output
|
||||||
|
/// @returns array of output params
|
||||||
|
var fromAbiOutput = function (json, methodName, output) {
|
||||||
|
|
||||||
|
output = output.slice(2);
|
||||||
|
var result = [];
|
||||||
|
var method = getMethodWithName(json, methodName);
|
||||||
|
var padding = ETH_PADDING * 2;
|
||||||
|
|
||||||
|
var dynamicPartLength = method.outputs.reduce(function (acc, curr) {
|
||||||
|
return acc + dynamicBytesLength(curr.type);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
var dynamicPart = output.slice(0, dynamicPartLength);
|
||||||
|
output = output.slice(dynamicPartLength);
|
||||||
|
|
||||||
|
method.outputs.forEach(function (out, i) {
|
||||||
|
var typeMatch = false;
|
||||||
|
for (var j = 0; j < outputTypes.length && !typeMatch; j++) {
|
||||||
|
typeMatch = outputTypes[j].type(method.outputs[i].type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!typeMatch) {
|
||||||
|
console.error('output parser does not support type: ' + method.outputs[i].type);
|
||||||
|
}
|
||||||
|
|
||||||
|
var formatter = outputTypes[j - 1].format;
|
||||||
|
if (arrayType(method.outputs[i].type)) {
|
||||||
|
var size = formatOutputUInt(dynamicPart.slice(0, padding));
|
||||||
|
dynamicPart = dynamicPart.slice(padding);
|
||||||
|
var array = [];
|
||||||
|
for (var k = 0; k < size; k++) {
|
||||||
|
array.push(formatter(output.slice(0, padding)));
|
||||||
|
output = output.slice(padding);
|
||||||
|
}
|
||||||
|
result.push(array);
|
||||||
|
}
|
||||||
|
else if (prefixedType('string')(method.outputs[i].type)) {
|
||||||
|
dynamicPart = dynamicPart.slice(padding);
|
||||||
|
result.push(formatter(output.slice(0, padding)));
|
||||||
|
output = output.slice(padding);
|
||||||
|
} else {
|
||||||
|
result.push(formatter(output.slice(0, padding)));
|
||||||
|
output = output.slice(padding);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns display name for method eg. multiply(uint256) -> multiply
|
||||||
|
var methodDisplayName = function (method) {
|
||||||
|
var length = method.indexOf('(');
|
||||||
|
return length !== -1 ? method.substr(0, length) : method;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns overloaded part of method's name
|
||||||
|
var methodTypeName = function (method) {
|
||||||
|
/// TODO: make it not vulnerable
|
||||||
|
var length = method.indexOf('(');
|
||||||
|
return length !== -1 ? method.substr(length + 1, method.length - 1 - (length + 1)) : "";
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @param json abi for contract
|
||||||
|
/// @returns input parser object for given json abi
|
||||||
|
var inputParser = function (json) {
|
||||||
|
var parser = {};
|
||||||
|
json.forEach(function (method) {
|
||||||
|
var displayName = methodDisplayName(method.name);
|
||||||
|
var typeName = methodTypeName(method.name);
|
||||||
|
|
||||||
|
var impl = function () {
|
||||||
|
var params = Array.prototype.slice.call(arguments);
|
||||||
|
return toAbiInput(json, method.name, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parser[displayName] === undefined) {
|
||||||
|
parser[displayName] = impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser[displayName][typeName] = impl;
|
||||||
|
});
|
||||||
|
|
||||||
|
return parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @param json abi for contract
|
||||||
|
/// @returns output parser for given json abi
|
||||||
|
var outputParser = function (json) {
|
||||||
|
var parser = {};
|
||||||
|
json.forEach(function (method) {
|
||||||
|
|
||||||
|
var displayName = methodDisplayName(method.name);
|
||||||
|
var typeName = methodTypeName(method.name);
|
||||||
|
|
||||||
|
var impl = function (output) {
|
||||||
|
return fromAbiOutput(json, method.name, output);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parser[displayName] === undefined) {
|
||||||
|
parser[displayName] = impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser[displayName][typeName] = impl;
|
||||||
|
});
|
||||||
|
|
||||||
|
return parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @param method name for which we want to get method signature
|
||||||
|
/// @returns (promise) contract method signature for method with given name
|
||||||
|
var methodSignature = function (name) {
|
||||||
|
return web3.sha3(web3.fromAscii(name)).slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
inputParser: inputParser,
|
||||||
|
outputParser: outputParser,
|
||||||
|
methodSignature: methodSignature,
|
||||||
|
methodDisplayName: methodDisplayName,
|
||||||
|
methodTypeName: methodTypeName,
|
||||||
|
getMethodWithName: getMethodWithName
|
||||||
|
};
|
||||||
|
|
||||||
145
cmd/mist/assets/ext/lib/contract.js
Normal file
145
cmd/mist/assets/ext/lib/contract.js
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
This file is part of ethereum.js.
|
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file contract.js
|
||||||
|
* @authors:
|
||||||
|
* Marek Kotewicz <marek@ethdev.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
var web3 = require('./web3'); // jshint ignore:line
|
||||||
|
var abi = require('./abi');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should be called when we want to call / transact some solidity method from javascript
|
||||||
|
* it returns an object which has same methods available as solidity contract description
|
||||||
|
* usage example:
|
||||||
|
*
|
||||||
|
* var abi = [{
|
||||||
|
* name: 'myMethod',
|
||||||
|
* inputs: [{ name: 'a', type: 'string' }],
|
||||||
|
* outputs: [{name: 'd', type: 'string' }]
|
||||||
|
* }]; // contract abi
|
||||||
|
*
|
||||||
|
* var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object
|
||||||
|
*
|
||||||
|
* myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)
|
||||||
|
* myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)
|
||||||
|
* myContract.transact().myMethod('this is test string param for transact'); // myMethod transact
|
||||||
|
*
|
||||||
|
* @param address - address of the contract, which should be called
|
||||||
|
* @param desc - abi json description of the contract, which is being created
|
||||||
|
* @returns contract object
|
||||||
|
*/
|
||||||
|
|
||||||
|
var contract = function (address, desc) {
|
||||||
|
|
||||||
|
desc.forEach(function (method) {
|
||||||
|
// workaround for invalid assumption that method.name is the full anonymous prototype of the method.
|
||||||
|
// it's not. it's just the name. the rest of the code assumes it's actually the anonymous
|
||||||
|
// prototype, so we make it so as a workaround.
|
||||||
|
if (method.name.indexOf('(') === -1) {
|
||||||
|
var displayName = method.name;
|
||||||
|
var typeName = method.inputs.map(function(i){return i.type; }).join();
|
||||||
|
method.name = displayName + '(' + typeName + ')';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var inputParser = abi.inputParser(desc);
|
||||||
|
var outputParser = abi.outputParser(desc);
|
||||||
|
|
||||||
|
var result = {};
|
||||||
|
|
||||||
|
result.call = function (options) {
|
||||||
|
result._isTransact = false;
|
||||||
|
result._options = options;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
result.transact = function (options) {
|
||||||
|
result._isTransact = true;
|
||||||
|
result._options = options;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
result._options = {};
|
||||||
|
['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {
|
||||||
|
result[p] = function (v) {
|
||||||
|
result._options[p] = v;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
desc.forEach(function (method) {
|
||||||
|
|
||||||
|
var displayName = abi.methodDisplayName(method.name);
|
||||||
|
var typeName = abi.methodTypeName(method.name);
|
||||||
|
|
||||||
|
var impl = function () {
|
||||||
|
var params = Array.prototype.slice.call(arguments);
|
||||||
|
var signature = abi.methodSignature(method.name);
|
||||||
|
var parsed = inputParser[displayName][typeName].apply(null, params);
|
||||||
|
|
||||||
|
var options = result._options || {};
|
||||||
|
options.to = address;
|
||||||
|
options.data = signature + parsed;
|
||||||
|
|
||||||
|
var isTransact = result._isTransact === true || (result._isTransact !== false && !method.constant);
|
||||||
|
var collapse = options.collapse !== false;
|
||||||
|
|
||||||
|
// reset
|
||||||
|
result._options = {};
|
||||||
|
result._isTransact = null;
|
||||||
|
|
||||||
|
if (isTransact) {
|
||||||
|
// it's used byt natspec.js
|
||||||
|
// TODO: figure out better way to solve this
|
||||||
|
web3._currentContractAbi = desc;
|
||||||
|
web3._currentContractAddress = address;
|
||||||
|
web3._currentContractMethodName = method.name;
|
||||||
|
web3._currentContractMethodParams = params;
|
||||||
|
|
||||||
|
// transactions do not have any output, cause we do not know, when they will be processed
|
||||||
|
web3.eth.transact(options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = web3.eth.call(options);
|
||||||
|
var ret = outputParser[displayName][typeName](output);
|
||||||
|
if (collapse)
|
||||||
|
{
|
||||||
|
if (ret.length === 1)
|
||||||
|
ret = ret[0];
|
||||||
|
else if (ret.length === 0)
|
||||||
|
ret = null;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (result[displayName] === undefined) {
|
||||||
|
result[displayName] = impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[displayName][typeName] = impl;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = contract;
|
||||||
|
|
||||||
73
cmd/mist/assets/ext/lib/filter.js
Normal file
73
cmd/mist/assets/ext/lib/filter.js
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
This file is part of ethereum.js.
|
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file filter.js
|
||||||
|
* @authors:
|
||||||
|
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||||
|
* Marek Kotewicz <marek@ethdev.com>
|
||||||
|
* Marian Oancea <marian@ethdev.com>
|
||||||
|
* Gav Wood <g@ethdev.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
var web3 = require('./web3'); // jshint ignore:line
|
||||||
|
|
||||||
|
/// should be used when we want to watch something
|
||||||
|
/// it's using inner polling mechanism and is notified about changes
|
||||||
|
var Filter = function(options, impl) {
|
||||||
|
this.impl = impl;
|
||||||
|
this.callbacks = [];
|
||||||
|
|
||||||
|
this.id = impl.newFilter(options);
|
||||||
|
web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
/// alias for changed*
|
||||||
|
Filter.prototype.arrived = function(callback) {
|
||||||
|
this.changed(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// gets called when there is new eth/shh message
|
||||||
|
Filter.prototype.changed = function(callback) {
|
||||||
|
this.callbacks.push(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// trigger calling new message from people
|
||||||
|
Filter.prototype.trigger = function(messages) {
|
||||||
|
for (var i = 0; i < this.callbacks.length; i++) {
|
||||||
|
for (var j = 0; j < messages.length; j++) {
|
||||||
|
this.callbacks[i].call(this, messages[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// should be called to uninstall current filter
|
||||||
|
Filter.prototype.uninstall = function() {
|
||||||
|
this.impl.uninstallFilter(this.id);
|
||||||
|
web3.provider.stopPolling(this.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// should be called to manually trigger getting latest messages from the client
|
||||||
|
Filter.prototype.messages = function() {
|
||||||
|
return this.impl.getMessages(this.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// alias for messages
|
||||||
|
Filter.prototype.logs = function () {
|
||||||
|
return this.messages();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Filter;
|
||||||
70
cmd/mist/assets/ext/lib/httpsync.js
Normal file
70
cmd/mist/assets/ext/lib/httpsync.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
This file is part of ethereum.js.
|
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file httpsync.js
|
||||||
|
* @authors:
|
||||||
|
* Marek Kotewicz <marek@ethdev.com>
|
||||||
|
* Marian Oancea <marian@ethdev.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'build') {
|
||||||
|
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
|
||||||
|
}
|
||||||
|
|
||||||
|
var HttpSyncProvider = function (host) {
|
||||||
|
this.handlers = [];
|
||||||
|
this.host = host || 'http://localhost:8080';
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Transforms inner message to proper jsonrpc object
|
||||||
|
/// @param inner message object
|
||||||
|
/// @returns jsonrpc object
|
||||||
|
function formatJsonRpcObject(object) {
|
||||||
|
return {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
method: object.call,
|
||||||
|
params: object.args,
|
||||||
|
id: object._id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms jsonrpc object to inner message
|
||||||
|
/// @param incoming jsonrpc message
|
||||||
|
/// @returns inner message object
|
||||||
|
function formatJsonRpcMessage(message) {
|
||||||
|
var object = JSON.parse(message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: object.id,
|
||||||
|
data: object.result,
|
||||||
|
error: object.error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpSyncProvider.prototype.send = function (payload) {
|
||||||
|
var data = formatJsonRpcObject(payload);
|
||||||
|
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.open('POST', this.host, false);
|
||||||
|
request.send(JSON.stringify(data));
|
||||||
|
|
||||||
|
// check request.status
|
||||||
|
return request.responseText;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = HttpSyncProvider;
|
||||||
|
|
||||||
18
cmd/mist/assets/ext/lib/local.js
Normal file
18
cmd/mist/assets/ext/lib/local.js
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
var addressName = {"0x12378912345789": "Gav", "0x57835893478594739854": "Jeff"};
|
||||||
|
var nameAddress = {};
|
||||||
|
|
||||||
|
for (var prop in addressName) {
|
||||||
|
if (addressName.hasOwnProperty(prop)) {
|
||||||
|
nameAddress[addressName[prop]] = prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var local = {
|
||||||
|
addressBook:{
|
||||||
|
byName: addressName,
|
||||||
|
byAddress: nameAddress
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof(module) !== "undefined")
|
||||||
|
module.exports = local;
|
||||||
110
cmd/mist/assets/ext/lib/providermanager.js
Normal file
110
cmd/mist/assets/ext/lib/providermanager.js
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
This file is part of ethereum.js.
|
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file providermanager.js
|
||||||
|
* @authors:
|
||||||
|
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||||
|
* Marek Kotewicz <marek@ethdev.com>
|
||||||
|
* Marian Oancea <marian@ethdev.com>
|
||||||
|
* Gav Wood <g@ethdev.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
var web3 = require('./web3'); // jshint ignore:line
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider manager object prototype
|
||||||
|
* It's responsible for passing messages to providers
|
||||||
|
* If no provider is set it's responsible for queuing requests
|
||||||
|
* It's also responsible for polling the ethereum node for incoming messages
|
||||||
|
* Default poll timeout is 12 seconds
|
||||||
|
* If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,
|
||||||
|
* and provider manager polling mechanism is not used
|
||||||
|
*/
|
||||||
|
var ProviderManager = function() {
|
||||||
|
this.polls = [];
|
||||||
|
this.provider = undefined;
|
||||||
|
this.id = 1;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var poll = function () {
|
||||||
|
if (self.provider) {
|
||||||
|
self.polls.forEach(function (data) {
|
||||||
|
data.data._id = self.id;
|
||||||
|
self.id++;
|
||||||
|
var result = self.provider.send(data.data);
|
||||||
|
|
||||||
|
result = JSON.parse(result);
|
||||||
|
|
||||||
|
// dont call the callback if result is not an array, or empty one
|
||||||
|
if (result.error || !(result.result instanceof Array) || result.result.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.callback(result.result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setTimeout(poll, 1000);
|
||||||
|
};
|
||||||
|
poll();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// sends outgoing requests
|
||||||
|
ProviderManager.prototype.send = function(data) {
|
||||||
|
|
||||||
|
data.args = data.args || [];
|
||||||
|
data._id = this.id++;
|
||||||
|
|
||||||
|
if (this.provider === undefined) {
|
||||||
|
console.error('provider is not set');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: handle error here?
|
||||||
|
var result = this.provider.send(data);
|
||||||
|
result = JSON.parse(result);
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
console.log(result.error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// setups provider, which will be used for sending messages
|
||||||
|
ProviderManager.prototype.set = function(provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// this method is only used, when we do not have native qt bindings and have to do polling on our own
|
||||||
|
/// should be callled, on start watching for eth/shh changes
|
||||||
|
ProviderManager.prototype.startPolling = function (data, pollId, callback) {
|
||||||
|
this.polls.push({data: data, id: pollId, callback: callback});
|
||||||
|
};
|
||||||
|
|
||||||
|
/// should be called to stop polling for certain watch changes
|
||||||
|
ProviderManager.prototype.stopPolling = function (pollId) {
|
||||||
|
for (var i = this.polls.length; i--;) {
|
||||||
|
var poll = this.polls[i];
|
||||||
|
if (poll.id === pollId) {
|
||||||
|
this.polls.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ProviderManager;
|
||||||
|
|
||||||
32
cmd/mist/assets/ext/lib/qtsync.js
Normal file
32
cmd/mist/assets/ext/lib/qtsync.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
This file is part of ethereum.js.
|
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file qtsync.js
|
||||||
|
* @authors:
|
||||||
|
* Marek Kotewicz <marek@ethdev.com>
|
||||||
|
* Marian Oancea <marian@ethdev.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
var QtSyncProvider = function () {
|
||||||
|
};
|
||||||
|
|
||||||
|
QtSyncProvider.prototype.send = function (payload) {
|
||||||
|
return navigator.qt.callMethod(JSON.stringify(payload));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = QtSyncProvider;
|
||||||
|
|
||||||
327
cmd/mist/assets/ext/lib/web3.js
Normal file
327
cmd/mist/assets/ext/lib/web3.js
Normal file
|
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
This file is part of ethereum.js.
|
||||||
|
|
||||||
|
ethereum.js is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
ethereum.js is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file web3.js
|
||||||
|
* @authors:
|
||||||
|
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||||
|
* Marek Kotewicz <marek@ethdev.com>
|
||||||
|
* Marian Oancea <marian@ethdev.com>
|
||||||
|
* Gav Wood <g@ethdev.com>
|
||||||
|
* @date 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'build') {
|
||||||
|
var BigNumber = require('bignumber.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
var ETH_UNITS = [
|
||||||
|
'wei',
|
||||||
|
'Kwei',
|
||||||
|
'Mwei',
|
||||||
|
'Gwei',
|
||||||
|
'szabo',
|
||||||
|
'finney',
|
||||||
|
'ether',
|
||||||
|
'grand',
|
||||||
|
'Mether',
|
||||||
|
'Gether',
|
||||||
|
'Tether',
|
||||||
|
'Pether',
|
||||||
|
'Eether',
|
||||||
|
'Zether',
|
||||||
|
'Yether',
|
||||||
|
'Nether',
|
||||||
|
'Dether',
|
||||||
|
'Vether',
|
||||||
|
'Uether'
|
||||||
|
];
|
||||||
|
|
||||||
|
/// @returns an array of objects describing web3 api methods
|
||||||
|
var web3Methods = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'sha3', call: 'web3_sha3' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns an array of objects describing web3.eth api methods
|
||||||
|
var ethMethods = function () {
|
||||||
|
var blockCall = function (args) {
|
||||||
|
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
|
||||||
|
};
|
||||||
|
|
||||||
|
var transactionCall = function (args) {
|
||||||
|
return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber';
|
||||||
|
};
|
||||||
|
|
||||||
|
var uncleCall = function (args) {
|
||||||
|
return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber';
|
||||||
|
};
|
||||||
|
|
||||||
|
var methods = [
|
||||||
|
{ name: 'balanceAt', call: 'eth_balanceAt' },
|
||||||
|
{ name: 'stateAt', call: 'eth_stateAt' },
|
||||||
|
{ name: 'storageAt', call: 'eth_storageAt' },
|
||||||
|
{ name: 'countAt', call: 'eth_countAt'},
|
||||||
|
{ name: 'codeAt', call: 'eth_codeAt' },
|
||||||
|
{ name: 'transact', call: 'eth_transact' },
|
||||||
|
{ name: 'call', call: 'eth_call' },
|
||||||
|
{ name: 'block', call: blockCall },
|
||||||
|
{ name: 'transaction', call: transactionCall },
|
||||||
|
{ name: 'uncle', call: uncleCall },
|
||||||
|
{ name: 'compilers', call: 'eth_compilers' },
|
||||||
|
{ name: 'flush', call: 'eth_flush' },
|
||||||
|
{ name: 'lll', call: 'eth_lll' },
|
||||||
|
{ name: 'solidity', call: 'eth_solidity' },
|
||||||
|
{ name: 'serpent', call: 'eth_serpent' },
|
||||||
|
{ name: 'logs', call: 'eth_logs' }
|
||||||
|
];
|
||||||
|
return methods;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns an array of objects describing web3.eth api properties
|
||||||
|
var ethProperties = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
|
||||||
|
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
|
||||||
|
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
|
||||||
|
{ name: 'gasPrice', getter: 'eth_gasPrice' },
|
||||||
|
{ name: 'accounts', getter: 'eth_accounts' },
|
||||||
|
{ name: 'peerCount', getter: 'eth_peerCount' },
|
||||||
|
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
|
||||||
|
{ name: 'number', getter: 'eth_number'}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns an array of objects describing web3.db api methods
|
||||||
|
var dbMethods = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'put', call: 'db_put' },
|
||||||
|
{ name: 'get', call: 'db_get' },
|
||||||
|
{ name: 'putString', call: 'db_putString' },
|
||||||
|
{ name: 'getString', call: 'db_getString' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns an array of objects describing web3.shh api methods
|
||||||
|
var shhMethods = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'post', call: 'shh_post' },
|
||||||
|
{ name: 'newIdentity', call: 'shh_newIdentity' },
|
||||||
|
{ name: 'haveIdentity', call: 'shh_haveIdentity' },
|
||||||
|
{ name: 'newGroup', call: 'shh_newGroup' },
|
||||||
|
{ name: 'addToGroup', call: 'shh_addToGroup' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns an array of objects describing web3.eth.watch api methods
|
||||||
|
var ethWatchMethods = function () {
|
||||||
|
var newFilter = function (args) {
|
||||||
|
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ name: 'newFilter', call: newFilter },
|
||||||
|
{ name: 'uninstallFilter', call: 'eth_uninstallFilter' },
|
||||||
|
{ name: 'getMessages', call: 'eth_filterLogs' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns an array of objects describing web3.shh.watch api methods
|
||||||
|
var shhWatchMethods = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'newFilter', call: 'shh_newFilter' },
|
||||||
|
{ name: 'uninstallFilter', call: 'shh_uninstallFilter' },
|
||||||
|
{ name: 'getMessages', call: 'shh_getMessages' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// creates methods in a given object based on method description on input
|
||||||
|
/// setups api calls for these methods
|
||||||
|
var setupMethods = function (obj, methods) {
|
||||||
|
methods.forEach(function (method) {
|
||||||
|
obj[method.name] = function () {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
var call = typeof method.call === 'function' ? method.call(args) : method.call;
|
||||||
|
return web3.provider.send({
|
||||||
|
call: call,
|
||||||
|
args: args
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/// creates properties in a given object based on properties description on input
|
||||||
|
/// setups api calls for these properties
|
||||||
|
var setupProperties = function (obj, properties) {
|
||||||
|
properties.forEach(function (property) {
|
||||||
|
var proto = {};
|
||||||
|
proto.get = function () {
|
||||||
|
return web3.provider.send({
|
||||||
|
call: property.getter
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (property.setter) {
|
||||||
|
proto.set = function (val) {
|
||||||
|
return web3.provider.send({
|
||||||
|
call: property.setter,
|
||||||
|
args: [val]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Object.defineProperty(obj, property.name, proto);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/// setups web3 object, and it's in-browser executed methods
|
||||||
|
var web3 = {
|
||||||
|
_callbacks: {},
|
||||||
|
_events: {},
|
||||||
|
providers: {},
|
||||||
|
|
||||||
|
toHex: function(str) {
|
||||||
|
var hex = "";
|
||||||
|
for(var i = 0; i < str.length; i++) {
|
||||||
|
var n = str.charCodeAt(i).toString(16);
|
||||||
|
hex += n.length < 2 ? '0' + n : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
},
|
||||||
|
|
||||||
|
/// @returns ascii string representation of hex value prefixed with 0x
|
||||||
|
toAscii: function(hex) {
|
||||||
|
// Find termination
|
||||||
|
var str = "";
|
||||||
|
var i = 0, l = hex.length;
|
||||||
|
if (hex.substring(0, 2) === '0x')
|
||||||
|
i = 2;
|
||||||
|
for(; i < l; i+=2) {
|
||||||
|
var code = parseInt(hex.substr(i, 2), 16);
|
||||||
|
if(code === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
str += String.fromCharCode(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
|
||||||
|
/// @returns hex representation (prefixed by 0x) of ascii string
|
||||||
|
fromAscii: function(str, pad) {
|
||||||
|
pad = pad === undefined ? 0 : pad;
|
||||||
|
var hex = this.toHex(str);
|
||||||
|
while(hex.length < pad*2)
|
||||||
|
hex += "00";
|
||||||
|
return "0x" + hex;
|
||||||
|
},
|
||||||
|
|
||||||
|
/// @returns decimal representaton of hex value prefixed by 0x
|
||||||
|
toDecimal: function (val) {
|
||||||
|
// remove 0x and place 0, if it's required
|
||||||
|
val = val.length > 2 ? val.substring(2) : "0";
|
||||||
|
return (new BigNumber(val, 16).toString(10));
|
||||||
|
},
|
||||||
|
|
||||||
|
/// @returns hex representation (prefixed by 0x) of decimal value
|
||||||
|
fromDecimal: function (val) {
|
||||||
|
return "0x" + (new BigNumber(val).toString(16));
|
||||||
|
},
|
||||||
|
|
||||||
|
/// used to transform value/string to eth string
|
||||||
|
/// TODO: use BigNumber.js to parse int
|
||||||
|
toEth: function(str) {
|
||||||
|
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
|
||||||
|
var unit = 0;
|
||||||
|
var units = ETH_UNITS;
|
||||||
|
while (val > 3000 && unit < units.length - 1)
|
||||||
|
{
|
||||||
|
val /= 1000;
|
||||||
|
unit++;
|
||||||
|
}
|
||||||
|
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
|
||||||
|
var replaceFunction = function($0, $1, $2) {
|
||||||
|
return $1 + ',' + $2;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var o = s;
|
||||||
|
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction);
|
||||||
|
if (o === s)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return s + ' ' + units[unit];
|
||||||
|
},
|
||||||
|
|
||||||
|
/// eth object prototype
|
||||||
|
eth: {
|
||||||
|
contractFromAbi: function (abi) {
|
||||||
|
return function(addr) {
|
||||||
|
// Default to address of Config. TODO: rremove prior to genesis.
|
||||||
|
addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b';
|
||||||
|
var ret = web3.eth.contract(addr, abi);
|
||||||
|
ret.address = addr;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: function (params) {
|
||||||
|
return new web3.filter(params, ethWatch);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/// db object prototype
|
||||||
|
db: {},
|
||||||
|
|
||||||
|
/// shh object prototype
|
||||||
|
shh: {
|
||||||
|
watch: function (params) {
|
||||||
|
return new web3.filter(params, shhWatch);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/// @returns true if provider is installed
|
||||||
|
haveProvider: function() {
|
||||||
|
return !!web3.provider.provider;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// setups all api methods
|
||||||
|
setupMethods(web3, web3Methods());
|
||||||
|
setupMethods(web3.eth, ethMethods());
|
||||||
|
setupProperties(web3.eth, ethProperties());
|
||||||
|
setupMethods(web3.db, dbMethods());
|
||||||
|
setupMethods(web3.shh, shhMethods());
|
||||||
|
|
||||||
|
var ethWatch = {
|
||||||
|
changed: 'eth_changed'
|
||||||
|
};
|
||||||
|
|
||||||
|
setupMethods(ethWatch, ethWatchMethods());
|
||||||
|
|
||||||
|
var shhWatch = {
|
||||||
|
changed: 'shh_changed'
|
||||||
|
};
|
||||||
|
|
||||||
|
setupMethods(shhWatch, shhWatchMethods());
|
||||||
|
|
||||||
|
web3.setProvider = function(provider) {
|
||||||
|
//provider.onmessage = messageHandler; // there will be no async calls, to remove
|
||||||
|
web3.provider.set(provider);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = web3;
|
||||||
|
|
||||||
69
cmd/mist/assets/ext/package.json
Normal file
69
cmd/mist/assets/ext/package.json
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
{
|
||||||
|
"name": "ethereum.js",
|
||||||
|
"namespace": "ethereum",
|
||||||
|
"version": "0.0.10",
|
||||||
|
"description": "Ethereum Compatible JavaScript API",
|
||||||
|
"main": "./index.js",
|
||||||
|
"directories": {
|
||||||
|
"lib": "./lib"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ws": "*",
|
||||||
|
"xmlhttprequest": "*",
|
||||||
|
"bignumber.js": ">=2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"bower": ">=1.3.0",
|
||||||
|
"browserify": ">=6.0",
|
||||||
|
"del": ">=0.1.1",
|
||||||
|
"envify": "^3.0.0",
|
||||||
|
"exorcist": "^0.1.6",
|
||||||
|
"gulp": ">=3.4.0",
|
||||||
|
"gulp-jshint": ">=1.5.0",
|
||||||
|
"gulp-rename": ">=1.2.0",
|
||||||
|
"gulp-uglify": ">=1.0.0",
|
||||||
|
"jshint": ">=2.5.0",
|
||||||
|
"uglifyify": "^2.6.0",
|
||||||
|
"unreachable-branch-transform": "^0.1.0",
|
||||||
|
"vinyl-source-stream": "^1.0.0",
|
||||||
|
"mocha": ">=2.1.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp",
|
||||||
|
"watch": "gulp watch",
|
||||||
|
"lint": "gulp lint",
|
||||||
|
"test": "mocha"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ethereum/ethereum.js.git"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/ethereum/ethereum.js",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/ethereum/ethereum.js/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"ethereum",
|
||||||
|
"javascript",
|
||||||
|
"API"
|
||||||
|
],
|
||||||
|
"author": "ethdev.com",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jeffery Wilcke",
|
||||||
|
"email": "jeff@ethdev.com",
|
||||||
|
"url": "https://github.com/obscuren"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Marek Kotewicz",
|
||||||
|
"email": "marek@ethdev.com",
|
||||||
|
"url": "https://github.com/debris"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Marian Oancea",
|
||||||
|
"email": "marian@ethdev.com",
|
||||||
|
"url": "https://github.com/cubedro"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0"
|
||||||
|
}
|
||||||
860
cmd/mist/assets/ext/test/abi.parsers.js
Normal file
860
cmd/mist/assets/ext/test/abi.parsers.js
Normal file
|
|
@ -0,0 +1,860 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var BigNumber = require('bignumber.js');
|
||||||
|
var abi = require('../lib/abi.js');
|
||||||
|
var clone = function (object) { return JSON.parse(JSON.stringify(object)); };
|
||||||
|
|
||||||
|
var description = [{
|
||||||
|
"name": "test",
|
||||||
|
"inputs": [{
|
||||||
|
"name": "a",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "d",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}];
|
||||||
|
|
||||||
|
describe('abi', function() {
|
||||||
|
describe('inputParser', function() {
|
||||||
|
it('should parse input uint', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "uint" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input uint128', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "uint128" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input uint256', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "uint256" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input int', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "int" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
|
||||||
|
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||||
|
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
|
||||||
|
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input int128', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "int128" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
|
||||||
|
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||||
|
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
|
||||||
|
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input int256', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "int256" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
|
||||||
|
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||||
|
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
|
||||||
|
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
);
|
||||||
|
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input bool', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: 'bool' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test(true), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(parser.test(false), "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input hash', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "hash" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input hash256', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "hash256" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should parse input hash160', function() {
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "hash160" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input address', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "address" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d)
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input string', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "string" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(
|
||||||
|
parser.test('hello'),
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test('world'),
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use proper method name', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
d[0].name = 'helloworld(int)';
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "int" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.helloworld(1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(parser.helloworld['int'](1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse multiple methods', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = [{
|
||||||
|
name: "test",
|
||||||
|
inputs: [{ type: "int" }],
|
||||||
|
outputs: [{ type: "int" }]
|
||||||
|
},{
|
||||||
|
name: "test2",
|
||||||
|
inputs: [{ type: "string" }],
|
||||||
|
outputs: [{ type: "string" }]
|
||||||
|
}];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
//then
|
||||||
|
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
|
||||||
|
assert.equal(
|
||||||
|
parser.test2('hello'),
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000"
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input array of ints', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: "int[]" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(
|
||||||
|
parser.test([5, 6]),
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input real', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: 'real' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test([1]), "0000000000000000000000000000000100000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test([2.125]), "0000000000000000000000000000000220000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test([8.5]), "0000000000000000000000000000000880000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test([-1]), "ffffffffffffffffffffffffffffffff00000000000000000000000000000000");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse input ureal', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].inputs = [
|
||||||
|
{ type: 'ureal' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.inputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test([1]), "0000000000000000000000000000000100000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test([2.125]), "0000000000000000000000000000000220000000000000000000000000000000");
|
||||||
|
assert.equal(parser.test([8.5]), "0000000000000000000000000000000880000000000000000000000000000000");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('outputParser', function() {
|
||||||
|
it('should parse output string', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: "string" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0x" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
|
||||||
|
'hello'
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0x" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
|
||||||
|
'world'
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output uint', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'uint' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
|
||||||
|
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
|
||||||
|
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output uint256', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'uint256' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
|
||||||
|
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
|
||||||
|
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output uint128', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'uint128' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
|
||||||
|
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
|
||||||
|
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output int', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'int' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
|
||||||
|
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
|
||||||
|
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output int256', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'int256' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
|
||||||
|
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
|
||||||
|
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output int128', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'int128' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
|
||||||
|
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
|
||||||
|
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output hash', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'hash' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
|
||||||
|
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output hash256', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'hash256' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
|
||||||
|
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output hash160', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'hash160' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
|
||||||
|
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
|
||||||
|
);
|
||||||
|
// TODO shouldnt' the expected hash be shorter?
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output address', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'address' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
|
||||||
|
"0x407d73d8a49eeb85d32cf465507dd71d507100c1"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output bool', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'bool' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], true);
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000000")[0], false);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output real', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'real' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
|
||||||
|
assert.equal(parser.test("0xffffffffffffffffffffffffffffffff00000000000000000000000000000000")[0], -1);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output ureal', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'ureal' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
|
||||||
|
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should parse multiple output strings', function() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: "string" },
|
||||||
|
{ type: "string" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0x" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
|
||||||
|
'hello'
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
parser.test("0x" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"776f726c64000000000000000000000000000000000000000000000000000000")[1],
|
||||||
|
'world'
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use proper method name', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
d[0].name = 'helloworld(int)';
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: "int" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.helloworld("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
assert.equal(parser.helloworld['int']("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should parse multiple methods', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = [{
|
||||||
|
name: "test",
|
||||||
|
inputs: [{ type: "int" }],
|
||||||
|
outputs: [{ type: "int" }]
|
||||||
|
},{
|
||||||
|
name: "test2",
|
||||||
|
inputs: [{ type: "string" }],
|
||||||
|
outputs: [{ type: "string" }]
|
||||||
|
}];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
//then
|
||||||
|
assert.equal(parser.test("0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
|
||||||
|
assert.equal(parser.test2("0x" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
|
||||||
|
"hello"
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output array', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'int[]' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006")[0][0],
|
||||||
|
5
|
||||||
|
);
|
||||||
|
assert.equal(parser.test("0x" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006")[0][1],
|
||||||
|
6
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse 0x value', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'int' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x")[0], 0);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse 0x value', function () {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var d = clone(description);
|
||||||
|
d[0].outputs = [
|
||||||
|
{ type: 'uint' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// when
|
||||||
|
var parser = abi.outputParser(d);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert.equal(parser.test("0x")[0], 0);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
14
cmd/mist/assets/ext/test/db.methods.js
Normal file
14
cmd/mist/assets/ext/test/db.methods.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
|
var web3 = require('../index.js');
|
||||||
|
var u = require('./utils.js');
|
||||||
|
|
||||||
|
describe('web3', function() {
|
||||||
|
describe('db', function() {
|
||||||
|
u.methodExists(web3.db, 'put');
|
||||||
|
u.methodExists(web3.db, 'get');
|
||||||
|
u.methodExists(web3.db, 'putString');
|
||||||
|
u.methodExists(web3.db, 'getString');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
34
cmd/mist/assets/ext/test/eth.methods.js
Normal file
34
cmd/mist/assets/ext/test/eth.methods.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var web3 = require('../index.js');
|
||||||
|
var u = require('./utils.js');
|
||||||
|
|
||||||
|
describe('web3', function() {
|
||||||
|
describe('eth', function() {
|
||||||
|
u.methodExists(web3.eth, 'balanceAt');
|
||||||
|
u.methodExists(web3.eth, 'stateAt');
|
||||||
|
u.methodExists(web3.eth, 'storageAt');
|
||||||
|
u.methodExists(web3.eth, 'countAt');
|
||||||
|
u.methodExists(web3.eth, 'codeAt');
|
||||||
|
u.methodExists(web3.eth, 'transact');
|
||||||
|
u.methodExists(web3.eth, 'call');
|
||||||
|
u.methodExists(web3.eth, 'block');
|
||||||
|
u.methodExists(web3.eth, 'transaction');
|
||||||
|
u.methodExists(web3.eth, 'uncle');
|
||||||
|
u.methodExists(web3.eth, 'compilers');
|
||||||
|
u.methodExists(web3.eth, 'lll');
|
||||||
|
u.methodExists(web3.eth, 'solidity');
|
||||||
|
u.methodExists(web3.eth, 'serpent');
|
||||||
|
u.methodExists(web3.eth, 'logs');
|
||||||
|
|
||||||
|
u.propertyExists(web3.eth, 'coinbase');
|
||||||
|
u.propertyExists(web3.eth, 'listening');
|
||||||
|
u.propertyExists(web3.eth, 'mining');
|
||||||
|
u.propertyExists(web3.eth, 'gasPrice');
|
||||||
|
u.propertyExists(web3.eth, 'accounts');
|
||||||
|
u.propertyExists(web3.eth, 'peerCount');
|
||||||
|
u.propertyExists(web3.eth, 'defaultBlock');
|
||||||
|
u.propertyExists(web3.eth, 'number');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
2
cmd/mist/assets/ext/test/mocha.opts
Normal file
2
cmd/mist/assets/ext/test/mocha.opts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
--reporter spec
|
||||||
|
|
||||||
14
cmd/mist/assets/ext/test/shh.methods.js
Normal file
14
cmd/mist/assets/ext/test/shh.methods.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var web3 = require('../index.js');
|
||||||
|
var u = require('./utils.js');
|
||||||
|
|
||||||
|
describe('web3', function() {
|
||||||
|
describe('shh', function() {
|
||||||
|
u.methodExists(web3.shh, 'post');
|
||||||
|
u.methodExists(web3.shh, 'newIdentity');
|
||||||
|
u.methodExists(web3.shh, 'haveIdentity');
|
||||||
|
u.methodExists(web3.shh, 'newGroup');
|
||||||
|
u.methodExists(web3.shh, 'addToGroup');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
19
cmd/mist/assets/ext/test/utils.js
Normal file
19
cmd/mist/assets/ext/test/utils.js
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
var methodExists = function (object, method) {
|
||||||
|
it('should have method ' + method + ' implemented', function() {
|
||||||
|
assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var propertyExists = function (object, property) {
|
||||||
|
it('should have property ' + property + ' implemented', function() {
|
||||||
|
assert.notEqual('undefined', typeof object[property], 'property ' + property + ' is not implemented');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
methodExists: methodExists,
|
||||||
|
propertyExists: propertyExists
|
||||||
|
};
|
||||||
|
|
||||||
10
cmd/mist/assets/ext/test/web3.methods.js
Normal file
10
cmd/mist/assets/ext/test/web3.methods.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
var assert = require('assert');
|
||||||
|
var web3 = require('../index.js');
|
||||||
|
var u = require('./utils.js');
|
||||||
|
|
||||||
|
describe('web3', function() {
|
||||||
|
u.methodExists(web3, 'sha3');
|
||||||
|
u.methodExists(web3, 'toAscii');
|
||||||
|
u.methodExists(web3, 'fromAscii');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -251,7 +251,13 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain
|
||||||
|
|
||||||
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
|
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
|
||||||
for i := uint64(0); i < max; i++ {
|
for i := uint64(0); i < max; i++ {
|
||||||
block = self.GetBlock(block.Header().ParentHash)
|
parentHash := block.Header().ParentHash
|
||||||
|
block = self.GetBlock(parentHash)
|
||||||
|
if block == nil {
|
||||||
|
chainlogger.Infof("GetBlockHashesFromHash Parent UNKNOWN %x\n", parentHash)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
chain = append(chain, block.Hash())
|
chain = append(chain, block.Hash())
|
||||||
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
|
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func GenesisBlock(db ethutil.Database) *types.Block {
|
||||||
statedb := state.New(genesis.Root(), db)
|
statedb := state.New(genesis.Root(), db)
|
||||||
//statedb := state.New(genesis.Trie())
|
//statedb := state.New(genesis.Trie())
|
||||||
for _, addr := range []string{
|
for _, addr := range []string{
|
||||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6",
|
||||||
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
||||||
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
|
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
|
||||||
"6c386a4b26f73c802f34673f7248bb118f97424a",
|
"6c386a4b26f73c802f34673f7248bb118f97424a",
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,7 @@ func ImportPreSaleKey(keyStore KeyStore2, keyJSON []byte, password string) (*Key
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
id := uuid.NewRandom()
|
key.Id = uuid.NewRandom()
|
||||||
key.Id = &id
|
|
||||||
err = keyStore.StoreKey(key, password)
|
err = keyStore.StoreKey(key, password)
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
|
|
@ -167,9 +166,10 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
|
||||||
ecKey := ToECDSA(ethPriv)
|
ecKey := ToECDSA(ethPriv)
|
||||||
key = &Key{
|
key = &Key{
|
||||||
Id: nil,
|
Id: nil,
|
||||||
|
Address: PubkeyToAddress(ecKey.PublicKey),
|
||||||
PrivateKey: ecKey,
|
PrivateKey: ecKey,
|
||||||
}
|
}
|
||||||
derivedAddr := ethutil.Bytes2Hex(key.Address())
|
derivedAddr := ethutil.Bytes2Hex(key.Address)
|
||||||
expectedAddr := preSaleKeyStruct.EthAddr
|
expectedAddr := preSaleKeyStruct.EthAddr
|
||||||
if derivedAddr != expectedAddr {
|
if derivedAddr != expectedAddr {
|
||||||
err = errors.New("decrypted addr not equal to expected addr")
|
err = errors.New("decrypted addr not equal to expected addr")
|
||||||
|
|
@ -223,3 +223,8 @@ func PKCS7Unpad(in []byte) []byte {
|
||||||
}
|
}
|
||||||
return in[:len(in)-int(padding)]
|
return in[:len(in)-int(padding)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PubkeyToAddress(p ecdsa.PublicKey) []byte {
|
||||||
|
pubBytes := FromECDSAPub(&p)
|
||||||
|
return Sha3(pubBytes[1:])[12:]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Key struct {
|
type Key struct {
|
||||||
Id *uuid.UUID // Version 4 "random" for unique id not derived from key data
|
Id uuid.UUID // Version 4 "random" for unique id not derived from key data
|
||||||
|
// to simplify lookups we also store the address
|
||||||
|
Address []byte
|
||||||
// we only store privkey as pubkey/address can be derived from it
|
// we only store privkey as pubkey/address can be derived from it
|
||||||
// privkey in this struct is always in plaintext
|
// privkey in this struct is always in plaintext
|
||||||
PrivateKey *ecdsa.PrivateKey
|
PrivateKey *ecdsa.PrivateKey
|
||||||
|
|
@ -41,6 +43,7 @@ type Key struct {
|
||||||
|
|
||||||
type plainKeyJSON struct {
|
type plainKeyJSON struct {
|
||||||
Id []byte
|
Id []byte
|
||||||
|
Address []byte
|
||||||
PrivateKey []byte
|
PrivateKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,17 +55,14 @@ type cipherJSON struct {
|
||||||
|
|
||||||
type encryptedKeyJSON struct {
|
type encryptedKeyJSON struct {
|
||||||
Id []byte
|
Id []byte
|
||||||
|
Address []byte
|
||||||
Crypto cipherJSON
|
Crypto cipherJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Key) Address() []byte {
|
|
||||||
pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey)
|
|
||||||
return Sha3(pubBytes[1:])[12:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *Key) MarshalJSON() (j []byte, err error) {
|
func (k *Key) MarshalJSON() (j []byte, err error) {
|
||||||
jStruct := plainKeyJSON{
|
jStruct := plainKeyJSON{
|
||||||
*k.Id,
|
k.Id,
|
||||||
|
k.Address,
|
||||||
FromECDSA(k.PrivateKey),
|
FromECDSA(k.PrivateKey),
|
||||||
}
|
}
|
||||||
j, err = json.Marshal(jStruct)
|
j, err = json.Marshal(jStruct)
|
||||||
|
|
@ -78,8 +78,8 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
|
||||||
|
|
||||||
u := new(uuid.UUID)
|
u := new(uuid.UUID)
|
||||||
*u = keyJSON.Id
|
*u = keyJSON.Id
|
||||||
k.Id = u
|
k.Id = *u
|
||||||
|
k.Address = keyJSON.Address
|
||||||
k.PrivateKey = ToECDSA(keyJSON.PrivateKey)
|
k.PrivateKey = ToECDSA(keyJSON.PrivateKey)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
@ -101,7 +101,8 @@ func NewKey(rand io.Reader) *Key {
|
||||||
|
|
||||||
id := uuid.NewRandom()
|
id := uuid.NewRandom()
|
||||||
key := &Key{
|
key := &Key{
|
||||||
Id: &id,
|
Id: id,
|
||||||
|
Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
|
||||||
PrivateKey: privateKeyECDSA,
|
PrivateKey: privateKeyECDSA,
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -96,21 +97,26 @@ func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *K
|
||||||
return GenerateNewKeyDefault(ks, rand, auth)
|
return GenerateNewKeyDefault(ks, rand, auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) {
|
func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *Key, err error) {
|
||||||
keyBytes, err := DecryptKey(ks, keyId, auth)
|
keyBytes, keyId, err := DecryptKey(ks, keyAddr, auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
key = &Key{
|
key = &Key{
|
||||||
Id: keyId,
|
Id: uuid.UUID(keyId),
|
||||||
|
Address: keyAddr,
|
||||||
PrivateKey: ToECDSA(keyBytes),
|
PrivateKey: ToECDSA(keyBytes),
|
||||||
}
|
}
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ks keyStorePassphrase) GetKeyAddresses() (addresses [][]byte, err error) {
|
||||||
|
return GetKeyAddresses(ks.keysDirPath)
|
||||||
|
}
|
||||||
|
|
||||||
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
||||||
authArray := []byte(auth)
|
authArray := []byte(auth)
|
||||||
salt := getEntropyCSPRNG(32)
|
salt := GetEntropyCSPRNG(32)
|
||||||
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
|
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -125,7 +131,7 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
iv := getEntropyCSPRNG(aes.BlockSize) // 16
|
iv := GetEntropyCSPRNG(aes.BlockSize) // 16
|
||||||
AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv)
|
AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv)
|
||||||
cipherText := make([]byte, len(toEncrypt))
|
cipherText := make([]byte, len(toEncrypt))
|
||||||
AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
|
AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
|
||||||
|
|
@ -136,7 +142,8 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
||||||
cipherText,
|
cipherText,
|
||||||
}
|
}
|
||||||
keyStruct := encryptedKeyJSON{
|
keyStruct := encryptedKeyJSON{
|
||||||
*key.Id,
|
key.Id,
|
||||||
|
key.Address,
|
||||||
cipherStruct,
|
cipherStruct,
|
||||||
}
|
}
|
||||||
keyJSON, err := json.Marshal(keyStruct)
|
keyJSON, err := json.Marshal(keyStruct)
|
||||||
|
|
@ -144,54 +151,53 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON)
|
return WriteKeyFile(key.Address, ks.keysDirPath, keyJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) {
|
func (ks keyStorePassphrase) DeleteKey(keyAddr []byte, auth string) (err error) {
|
||||||
// only delete if correct passphrase is given
|
// only delete if correct passphrase is given
|
||||||
_, err = DecryptKey(ks, keyId, auth)
|
_, _, err = DecryptKey(ks, keyAddr, auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
keyDirPath := path.Join(ks.keysDirPath, keyId.String())
|
keyDirPath := path.Join(ks.keysDirPath, hex.EncodeToString(keyAddr))
|
||||||
return os.RemoveAll(keyDirPath)
|
return os.RemoveAll(keyDirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, err error) {
|
func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||||
fileContent, err := GetKeyFile(ks.keysDirPath, keyId)
|
fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keyProtected := new(encryptedKeyJSON)
|
keyProtected := new(encryptedKeyJSON)
|
||||||
err = json.Unmarshal(fileContent, keyProtected)
|
err = json.Unmarshal(fileContent, keyProtected)
|
||||||
|
|
||||||
|
keyId = keyProtected.Id
|
||||||
salt := keyProtected.Crypto.Salt
|
salt := keyProtected.Crypto.Salt
|
||||||
|
|
||||||
iv := keyProtected.Crypto.IV
|
iv := keyProtected.Crypto.IV
|
||||||
|
|
||||||
cipherText := keyProtected.Crypto.CipherText
|
cipherText := keyProtected.Crypto.CipherText
|
||||||
|
|
||||||
authArray := []byte(auth)
|
authArray := []byte(auth)
|
||||||
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
|
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
|
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
keyBytes = plainText[:len(plainText)-32]
|
keyBytes = plainText[:len(plainText)-32]
|
||||||
keyBytesHash := plainText[len(plainText)-32:]
|
keyBytesHash := plainText[len(plainText)-32:]
|
||||||
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
|
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
|
||||||
err = errors.New("Decryption failed: checksum mismatch")
|
err = errors.New("Decryption failed: checksum mismatch")
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return keyBytes, err
|
return keyBytes, keyId, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEntropyCSPRNG(n int) []byte {
|
func GetEntropyCSPRNG(n int) []byte {
|
||||||
mainBuff := make([]byte, n)
|
mainBuff := make([]byte, n)
|
||||||
_, err := io.ReadFull(crand.Reader, mainBuff)
|
_, err := io.ReadFull(crand.Reader, mainBuff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/go-uuid/uuid"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -38,9 +38,10 @@ import (
|
||||||
type KeyStore2 interface {
|
type KeyStore2 interface {
|
||||||
// create new key using io.Reader entropy source and optionally using auth string
|
// create new key using io.Reader entropy source and optionally using auth string
|
||||||
GenerateNewKey(io.Reader, string) (*Key, error)
|
GenerateNewKey(io.Reader, string) (*Key, error)
|
||||||
GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string
|
GetKey([]byte, string) (*Key, error) // key from addr and auth string
|
||||||
|
GetKeyAddresses() ([][]byte, error) // get all addresses
|
||||||
StoreKey(*Key, string) error // store key optionally using auth string
|
StoreKey(*Key, string) error // store key optionally using auth string
|
||||||
DeleteKey(*uuid.UUID, string) error // delete key by id and auth string
|
DeleteKey([]byte, string) error // delete key by addr and auth string
|
||||||
}
|
}
|
||||||
|
|
||||||
type keyStorePlain struct {
|
type keyStorePlain struct {
|
||||||
|
|
@ -72,8 +73,8 @@ func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key,
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) {
|
func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error) {
|
||||||
fileContent, err := GetKeyFile(ks.keysDirPath, keyId)
|
fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -83,32 +84,50 @@ func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err err
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ks keyStorePlain) GetKeyAddresses() (addresses [][]byte, err error) {
|
||||||
|
return GetKeyAddresses(ks.keysDirPath)
|
||||||
|
}
|
||||||
|
|
||||||
func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
|
func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
|
||||||
keyJSON, err := json.Marshal(key)
|
keyJSON, err := json.Marshal(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON)
|
err = WriteKeyFile(key.Address, ks.keysDirPath, keyJSON)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ks keyStorePlain) DeleteKey(keyId *uuid.UUID, auth string) (err error) {
|
func (ks keyStorePlain) DeleteKey(keyAddr []byte, auth string) (err error) {
|
||||||
keyDirPath := path.Join(ks.keysDirPath, keyId.String())
|
keyDirPath := path.Join(ks.keysDirPath, hex.EncodeToString(keyAddr))
|
||||||
err = os.RemoveAll(keyDirPath)
|
err = os.RemoveAll(keyDirPath)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) {
|
func GetKeyFile(keysDirPath string, keyAddr []byte) (fileContent []byte, err error) {
|
||||||
id := keyId.String()
|
fileName := hex.EncodeToString(keyAddr)
|
||||||
return ioutil.ReadFile(path.Join(keysDirPath, id, id))
|
return ioutil.ReadFile(path.Join(keysDirPath, fileName, fileName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func WriteKeyFile(id string, keysDirPath string, content []byte) (err error) {
|
func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) {
|
||||||
keyDirPath := path.Join(keysDirPath, id)
|
addrHex := hex.EncodeToString(addr)
|
||||||
keyFilePath := path.Join(keyDirPath, id)
|
keyDirPath := path.Join(keysDirPath, addrHex)
|
||||||
|
keyFilePath := path.Join(keyDirPath, addrHex)
|
||||||
err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
|
err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
|
return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) {
|
||||||
|
fileInfos, err := ioutil.ReadDir(keysDirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
addresses = make([][]byte, len(fileInfos))
|
||||||
|
for i, fileInfo := range fileInfos {
|
||||||
|
addresses[i] = make([]byte, 40)
|
||||||
|
addresses[i] = []byte(fileInfo.Name())
|
||||||
|
}
|
||||||
|
return addresses, err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@ func TestKeyStorePlain(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
k2 := new(Key)
|
k2 := new(Key)
|
||||||
k2, err = ks.GetKey(k1.Id, pass)
|
k2, err = ks.GetKey(k1.Address, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(k1.Id, k2.Id) {
|
if !reflect.DeepEqual(k1.Address, k2.Address) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ func TestKeyStorePlain(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ks.DeleteKey(k2.Id, pass)
|
err = ks.DeleteKey(k2.Address, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -42,11 +42,11 @@ func TestKeyStorePassphrase(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
k2 := new(Key)
|
k2 := new(Key)
|
||||||
k2, err = ks.GetKey(k1.Id, pass)
|
k2, err = ks.GetKey(k1.Address, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(k1.Id, k2.Id) {
|
if !reflect.DeepEqual(k1.Address, k2.Address) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ func TestKeyStorePassphrase(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ks.DeleteKey(k2.Id, pass) // also to clean up created files
|
err = ks.DeleteKey(k2.Address, pass) // also to clean up created files
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -68,17 +68,17 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = ks.GetKey(k1.Id, "bar") // wrong passphrase
|
_, err = ks.GetKey(k1.Address, "bar") // wrong passphrase
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase
|
err = ks.DeleteKey(k1.Address, "bar") // wrong passphrase
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ks.DeleteKey(k1.Id, pass) // to clean up
|
err = ks.DeleteKey(k1.Address, pass) // to clean up
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1098,7 +1098,7 @@ func (self *BlockPool) requestBlocks(attempts int, hashes [][]byte) {
|
||||||
poolLogger.Debugf("request %v missing blocks from %v/%v peers: chosen %v", len(hashes), repetitions, peerCount, indexes)
|
poolLogger.Debugf("request %v missing blocks from %v/%v peers: chosen %v", len(hashes), repetitions, peerCount, indexes)
|
||||||
for _, peer := range self.peers {
|
for _, peer := range self.peers {
|
||||||
if i == indexes[0] {
|
if i == indexes[0] {
|
||||||
poolLogger.Debugf("request %v missing blocks from peer %s", len(hashes), peer.id)
|
poolLogger.Debugf("request %v missing blocks [%x/%x] from peer %s", len(hashes), hashes[0][:4], hashes[len(hashes)-1][:4], peer.id)
|
||||||
peer.requestBlocks(hashes)
|
peer.requestBlocks(hashes)
|
||||||
indexes = indexes[1:]
|
indexes = indexes[1:]
|
||||||
if len(indexes) == 0 {
|
if len(indexes) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProtocolVersion = 51
|
ProtocolVersion = 52
|
||||||
NetworkId = 0
|
NetworkId = 0
|
||||||
ProtocolLength = uint64(8)
|
ProtocolLength = uint64(8)
|
||||||
ProtocolMaxMsgSize = 10 * 1024 * 1024
|
ProtocolMaxMsgSize = 10 * 1024 * 1024
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ if ls $dir/*.go &> /dev/null; then
|
||||||
# echo $dir
|
# echo $dir
|
||||||
if [[ $dir != "./tests/vm" ]]
|
if [[ $dir != "./tests/vm" ]]
|
||||||
then
|
then
|
||||||
go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
$GOROOT/bin/go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
||||||
fi
|
fi
|
||||||
if [ -f $dir/profile.tmp ]
|
if [ -f $dir/profile.tmp ]
|
||||||
then
|
then
|
||||||
|
|
@ -25,7 +25,7 @@ if ls $dir/*.go &> /dev/null; then
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
go tool cover -func profile.cov
|
$GOROOT/bin/go tool cover -func profile.cov
|
||||||
|
|
||||||
# To submit the test coverage result to coveralls.io,
|
# To submit the test coverage result to coveralls.io,
|
||||||
# use goveralls (https://github.com/mattn/goveralls)
|
# use goveralls (https://github.com/mattn/goveralls)
|
||||||
|
|
|
||||||
|
|
@ -172,47 +172,47 @@ func RunVmTest(p string, t *testing.T) {
|
||||||
|
|
||||||
// I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail.
|
// I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail.
|
||||||
func TestVMArithmetic(t *testing.T) {
|
func TestVMArithmetic(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmArithmeticTest.json"
|
const fn = "../files/VMTests/vmArithmeticTest.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBitwiseLogicOperation(t *testing.T) {
|
func TestBitwiseLogicOperation(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmBitwiseLogicOperationTest.json"
|
const fn = "../files/VMTests/vmBitwiseLogicOperationTest.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlockInfo(t *testing.T) {
|
func TestBlockInfo(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmBlockInfoTest.json"
|
const fn = "../files/VMTests/vmBlockInfoTest.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnvironmentalInfo(t *testing.T) {
|
func TestEnvironmentalInfo(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmEnvironmentalInfoTest.json"
|
const fn = "../files/VMTests/vmEnvironmentalInfoTest.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFlowOperation(t *testing.T) {
|
func TestFlowOperation(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmIOandFlowOperationsTest.json"
|
const fn = "../files/VMTests/vmIOandFlowOperationsTest.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPushDupSwap(t *testing.T) {
|
func TestPushDupSwap(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmPushDupSwapTest.json"
|
const fn = "../files/VMTests/vmPushDupSwapTest.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVMSha3(t *testing.T) {
|
func TestVMSha3(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmSha3Test.json"
|
const fn = "../files/VMTests/vmSha3Test.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVm(t *testing.T) {
|
func TestVm(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmtests.json"
|
const fn = "../files/VMTests/vmtests.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVmLog(t *testing.T) {
|
func TestVmLog(t *testing.T) {
|
||||||
const fn = "../files/vmtests/vmLogTest.json"
|
const fn = "../files/VMTests/vmLogTest.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
356
vm/vm_jit.go
356
vm/vm_jit.go
|
|
@ -1,31 +1,371 @@
|
||||||
|
// +build evmjit
|
||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "math/big"
|
/*
|
||||||
|
|
||||||
|
void* evmjit_create();
|
||||||
|
int evmjit_run(void* _jit, void* _data, void* _env);
|
||||||
|
void evmjit_destroy(void* _jit);
|
||||||
|
|
||||||
|
// Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib
|
||||||
|
// More: https://github.com/ethereum/evmjit
|
||||||
|
#cgo LDFLAGS: -levmjit
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"math/big"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
type JitVm struct {
|
type JitVm struct {
|
||||||
env Environment
|
env Environment
|
||||||
backup *Vm
|
me ContextRef
|
||||||
|
callerAddr []byte
|
||||||
|
price *big.Int
|
||||||
|
data RuntimeData
|
||||||
|
}
|
||||||
|
|
||||||
|
type i256 [32]byte
|
||||||
|
|
||||||
|
type RuntimeData struct {
|
||||||
|
gas int64
|
||||||
|
gasPrice int64
|
||||||
|
callData *byte
|
||||||
|
callDataSize uint64
|
||||||
|
address i256
|
||||||
|
caller i256
|
||||||
|
origin i256
|
||||||
|
callValue i256
|
||||||
|
coinBase i256
|
||||||
|
difficulty i256
|
||||||
|
gasLimit i256
|
||||||
|
number uint64
|
||||||
|
timestamp int64
|
||||||
|
code *byte
|
||||||
|
codeSize uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash2llvm(h []byte) i256 {
|
||||||
|
var m i256
|
||||||
|
copy(m[len(m)-len(h):], h) // right aligned copy
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func llvm2hash(m *i256) []byte {
|
||||||
|
return C.GoBytes(unsafe.Pointer(m), C.int(len(m)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func llvm2hashRef(m *i256) []byte {
|
||||||
|
return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func address2llvm(addr []byte) i256 {
|
||||||
|
n := hash2llvm(addr)
|
||||||
|
bswap(&n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// bswap swap bytes of the 256-bit integer on LLVM side
|
||||||
|
// TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations
|
||||||
|
func bswap(m *i256) *i256 {
|
||||||
|
for i, l := 0, len(m); i < l/2; i++ {
|
||||||
|
m[i], m[l-i-1] = m[l-i-1], m[i]
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func trim(m []byte) []byte {
|
||||||
|
skip := 0
|
||||||
|
for i := 0; i < len(m); i++ {
|
||||||
|
if m[i] == 0 {
|
||||||
|
skip++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m[skip:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDataPtr(m []byte) *byte {
|
||||||
|
var p *byte
|
||||||
|
if len(m) > 0 {
|
||||||
|
p = &m[0]
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func big2llvm(n *big.Int) i256 {
|
||||||
|
m := hash2llvm(n.Bytes())
|
||||||
|
bswap(&m)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func llvm2big(m *i256) *big.Int {
|
||||||
|
n := big.NewInt(0)
|
||||||
|
for i := 0; i < len(m); i++ {
|
||||||
|
b := big.NewInt(int64(m[i]))
|
||||||
|
b.Lsh(b, uint(i)*8)
|
||||||
|
n.Add(n, b)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC)
|
||||||
|
// User must asure that referenced memory is available to Go until the data is copied or not needed any more
|
||||||
|
func llvm2bytesRef(data *byte, length uint64) []byte {
|
||||||
|
if length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if data == nil {
|
||||||
|
panic("Unexpected nil data pointer")
|
||||||
|
}
|
||||||
|
return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]
|
||||||
|
}
|
||||||
|
|
||||||
|
func untested(condition bool, message string) {
|
||||||
|
if condition {
|
||||||
|
panic("Condition `" + message + "` tested. Remove assert.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assert(condition bool, message string) {
|
||||||
|
if !condition {
|
||||||
|
panic("Assert `" + message + "` failed!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJitVm(env Environment) *JitVm {
|
func NewJitVm(env Environment) *JitVm {
|
||||||
backupVm := New(env)
|
return &JitVm{env: env}
|
||||||
return &JitVm{env: env, backup: backupVm}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
|
func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
|
||||||
return self.backup.Run(me, caller, code, value, gas, price, callData)
|
// TODO: depth is increased but never checked by VM. VM should not know about it at all.
|
||||||
|
self.env.SetDepth(self.env.Depth() + 1)
|
||||||
|
|
||||||
|
// TODO: Move it to Env.Call() or sth
|
||||||
|
if Precompiled[string(me.Address())] != nil {
|
||||||
|
// if it's address of precopiled contract
|
||||||
|
// fallback to standard VM
|
||||||
|
stdVm := New(self.env)
|
||||||
|
return stdVm.Run(me, caller, code, value, gas, price, callData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.me != nil {
|
||||||
|
panic("JitVm.Run() can be called only once per JitVm instance")
|
||||||
|
}
|
||||||
|
|
||||||
|
self.me = me
|
||||||
|
self.callerAddr = caller.Address()
|
||||||
|
self.price = price
|
||||||
|
|
||||||
|
self.data.gas = gas.Int64()
|
||||||
|
self.data.gasPrice = price.Int64()
|
||||||
|
self.data.callData = getDataPtr(callData)
|
||||||
|
self.data.callDataSize = uint64(len(callData))
|
||||||
|
self.data.address = address2llvm(self.me.Address())
|
||||||
|
self.data.caller = address2llvm(caller.Address())
|
||||||
|
self.data.origin = address2llvm(self.env.Origin())
|
||||||
|
self.data.callValue = big2llvm(value)
|
||||||
|
self.data.coinBase = address2llvm(self.env.Coinbase())
|
||||||
|
self.data.difficulty = big2llvm(self.env.Difficulty())
|
||||||
|
self.data.gasLimit = big2llvm(self.env.GasLimit())
|
||||||
|
self.data.number = self.env.BlockNumber().Uint64()
|
||||||
|
self.data.timestamp = self.env.Time()
|
||||||
|
self.data.code = getDataPtr(code)
|
||||||
|
self.data.codeSize = uint64(len(code))
|
||||||
|
|
||||||
|
jit := C.evmjit_create()
|
||||||
|
retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self))
|
||||||
|
|
||||||
|
if retCode < 0 {
|
||||||
|
err = errors.New("OOG from JIT")
|
||||||
|
gas.SetInt64(0) // Set gas to 0, JIT does not bother
|
||||||
|
} else {
|
||||||
|
gas.SetInt64(self.data.gas)
|
||||||
|
if retCode == 1 { // RETURN
|
||||||
|
ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize))
|
||||||
|
} else if retCode == 2 { // SUICIDE
|
||||||
|
// TODO: Suicide support logic should be moved to Env to be shared by VM implementations
|
||||||
|
state := self.Env().State()
|
||||||
|
receiverAddr := llvm2hashRef(bswap(&self.data.address))
|
||||||
|
receiver := state.GetOrNewStateObject(receiverAddr)
|
||||||
|
balance := state.GetBalance(me.Address())
|
||||||
|
receiver.AddAmount(balance)
|
||||||
|
state.Delete(me.Address())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
C.evmjit_destroy(jit);
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine {
|
func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine {
|
||||||
return self.backup.Printf(format, v)
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *JitVm) Endl() VirtualMachine {
|
func (self *JitVm) Endl() VirtualMachine {
|
||||||
return self.backup.Endl()
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *JitVm) Env() Environment {
|
func (self *JitVm) Env() Environment {
|
||||||
return self.env
|
return self.env
|
||||||
}
|
}
|
||||||
|
|
||||||
//go is nice
|
//export env_sha3
|
||||||
|
func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) {
|
||||||
|
data := llvm2bytesRef(dataPtr, length)
|
||||||
|
hash := crypto.Sha3(data)
|
||||||
|
result := (*i256)(resultPtr)
|
||||||
|
*result = hash2llvm(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export env_sstore
|
||||||
|
func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) {
|
||||||
|
vm := (*JitVm)(vmPtr)
|
||||||
|
index := llvm2hash(bswap((*i256)(indexPtr)))
|
||||||
|
value := llvm2hash(bswap((*i256)(valuePtr)))
|
||||||
|
value = trim(value)
|
||||||
|
if len(value) == 0 {
|
||||||
|
prevValue := vm.env.State().GetState(vm.me.Address(), index)
|
||||||
|
if len(prevValue) != 0 {
|
||||||
|
vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.env.State().SetState(vm.me.Address(), index, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export env_sload
|
||||||
|
func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) {
|
||||||
|
vm := (*JitVm)(vmPtr)
|
||||||
|
index := llvm2hash(bswap((*i256)(indexPtr)))
|
||||||
|
value := vm.env.State().GetState(vm.me.Address(), index)
|
||||||
|
result := (*i256)(resultPtr)
|
||||||
|
*result = hash2llvm(value)
|
||||||
|
bswap(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export env_balance
|
||||||
|
func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) {
|
||||||
|
vm := (*JitVm)(_vm)
|
||||||
|
addr := llvm2hash((*i256)(_addr))
|
||||||
|
balance := vm.Env().State().GetBalance(addr)
|
||||||
|
result := (*i256)(_result)
|
||||||
|
*result = big2llvm(balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export env_blockhash
|
||||||
|
func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) {
|
||||||
|
vm := (*JitVm)(_vm)
|
||||||
|
number := llvm2big((*i256)(_number))
|
||||||
|
result := (*i256)(_result)
|
||||||
|
|
||||||
|
currNumber := vm.Env().BlockNumber()
|
||||||
|
limit := big.NewInt(0).Sub(currNumber, big.NewInt(256))
|
||||||
|
if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 {
|
||||||
|
hash := vm.Env().GetHash(uint64(number.Int64()))
|
||||||
|
*result = hash2llvm(hash)
|
||||||
|
} else {
|
||||||
|
*result = i256{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//export env_call
|
||||||
|
func env_call(_vm unsafe.Pointer, _gas unsafe.Pointer, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool {
|
||||||
|
vm := (*JitVm)(_vm)
|
||||||
|
|
||||||
|
//fmt.Printf("env_call (depth %d)\n", vm.Env().Depth())
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
balance := vm.Env().State().GetBalance(vm.me.Address())
|
||||||
|
value := llvm2big((*i256)(_value))
|
||||||
|
|
||||||
|
if balance.Cmp(value) >= 0 {
|
||||||
|
receiveAddr := llvm2hash((*i256)(_receiveAddr))
|
||||||
|
inData := C.GoBytes(inDataPtr, C.int(inDataLen))
|
||||||
|
outData := llvm2bytesRef(outDataPtr, outDataLen)
|
||||||
|
codeAddr := llvm2hash((*i256)(_codeAddr))
|
||||||
|
llvmGas := (*i256)(_gas)
|
||||||
|
gas := llvm2big(llvmGas)
|
||||||
|
var out []byte
|
||||||
|
var err error
|
||||||
|
if bytes.Equal(codeAddr, receiveAddr) {
|
||||||
|
out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value)
|
||||||
|
} else {
|
||||||
|
out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value)
|
||||||
|
}
|
||||||
|
*llvmGas = big2llvm(gas)
|
||||||
|
if err == nil {
|
||||||
|
copy(outData, out)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//export env_create
|
||||||
|
func env_create(_vm unsafe.Pointer, _gas unsafe.Pointer, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) {
|
||||||
|
vm := (*JitVm)(_vm)
|
||||||
|
|
||||||
|
value := llvm2big((*i256)(_value))
|
||||||
|
initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance
|
||||||
|
result := (*i256)(_result)
|
||||||
|
*result = i256{}
|
||||||
|
|
||||||
|
llvmGas := (*i256)(_gas)
|
||||||
|
gas := llvm2big(llvmGas)
|
||||||
|
|
||||||
|
ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
|
||||||
|
if suberr == nil {
|
||||||
|
dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter
|
||||||
|
dataGas.Mul(dataGas, GasCreateByte)
|
||||||
|
gas.Sub(gas, dataGas)
|
||||||
|
*result = hash2llvm(ref.Address())
|
||||||
|
}
|
||||||
|
*llvmGas = big2llvm(gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export env_log
|
||||||
|
func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) {
|
||||||
|
vm := (*JitVm)(_vm)
|
||||||
|
|
||||||
|
data := C.GoBytes(dataPtr, C.int(dataLen))
|
||||||
|
|
||||||
|
topics := make([][]byte, 0, 4)
|
||||||
|
if _topic1 != nil {
|
||||||
|
topics = append(topics, llvm2hash((*i256)(_topic1)))
|
||||||
|
}
|
||||||
|
if _topic2 != nil {
|
||||||
|
topics = append(topics, llvm2hash((*i256)(_topic2)))
|
||||||
|
}
|
||||||
|
if _topic3 != nil {
|
||||||
|
topics = append(topics, llvm2hash((*i256)(_topic3)))
|
||||||
|
}
|
||||||
|
if _topic4 != nil {
|
||||||
|
topics = append(topics, llvm2hash((*i256)(_topic4)))
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export env_extcode
|
||||||
|
func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte {
|
||||||
|
vm := (*JitVm)(_vm)
|
||||||
|
addr := llvm2hash((*i256)(_addr))
|
||||||
|
code := vm.Env().State().GetCode(addr)
|
||||||
|
*o_size = uint64(len(code))
|
||||||
|
return getDataPtr(code)
|
||||||
|
}
|
||||||
|
|
|
||||||
10
vm/vm_jit_fake.go
Normal file
10
vm/vm_jit_fake.go
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// +build !evmjit
|
||||||
|
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func NewJitVm(env Environment) VirtualMachine {
|
||||||
|
fmt.Printf("Warning! EVM JIT not enabled.\n")
|
||||||
|
return New(env)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue