aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--accounts/account_manager.go42
-rw-r--r--cmd/geth/admin.go26
-rw-r--r--cmd/geth/info_test.json2
-rw-r--r--cmd/geth/js.go7
-rw-r--r--cmd/geth/js_test.go51
-rw-r--r--cmd/geth/main.go18
-rw-r--r--cmd/mist/gui.go8
-rw-r--r--cmd/mist/html_container.go3
-rw-r--r--cmd/mist/ui_lib.go10
-rw-r--r--cmd/utils/flags.go12
-rw-r--r--common/compiler/solidity.go13
-rw-r--r--common/compiler/solidity_test.go16
-rw-r--r--common/natspec/natspec_e2e_test.go9
-rw-r--r--common/path.go21
-rw-r--r--common/size.go6
-rw-r--r--common/size_test.go14
-rw-r--r--core/chain_manager_test.go4
-rw-r--r--core/events.go6
-rw-r--r--core/manager.go2
-rw-r--r--core/transaction_pool.go2
-rw-r--r--crypto/crypto.go12
-rw-r--r--crypto/key.go75
-rw-r--r--crypto/key_store_passphrase.go133
-rw-r--r--crypto/key_store_plain.go37
-rw-r--r--crypto/key_store_test.go2
-rw-r--r--crypto/randentropy/rand_entropy.go51
-rw-r--r--crypto/secp256k1/secp256.go4
-rw-r--r--crypto/secp256k1/secp256_test.go20
-rw-r--r--eth/backend.go52
-rw-r--r--eth/downloader/downloader.go148
-rw-r--r--eth/downloader/downloader_test.go3
-rw-r--r--eth/downloader/peer.go222
-rw-r--r--eth/downloader/queue.go9
-rw-r--r--eth/handler.go2
-rw-r--r--eth/sync.go3
-rw-r--r--ethdb/database.go4
-rw-r--r--ethdb/database_test.go4
-rw-r--r--generators/defaults.go4
-rw-r--r--jsre/ethereum_js.go3692
-rw-r--r--miner/agent.go22
-rw-r--r--miner/miner.go35
-rw-r--r--miner/remote_agent.go4
-rw-r--r--miner/worker.go231
-rw-r--r--p2p/discover/udp_test.go4
-rw-r--r--rpc/api.go71
-rw-r--r--rpc/api_test.go11
-rw-r--r--rpc/args.go40
-rw-r--r--rpc/http.go2
-rw-r--r--rpc/jeth.go4
-rw-r--r--rpc/types.go25
-rw-r--r--tests/block_test.go4
-rw-r--r--tests/block_test_util.go2
-rw-r--r--update-license.go4
-rw-r--r--xeth/xeth.go62
54 files changed, 4653 insertions, 617 deletions
diff --git a/accounts/account_manager.go b/accounts/account_manager.go
index e9eb8f816..6cbd23c4e 100644
--- a/accounts/account_manager.go
+++ b/accounts/account_manager.go
@@ -33,7 +33,6 @@ and accounts persistence is derived from stored keys' addresses
package accounts
import (
- "bytes"
"crypto/ecdsa"
crand "crypto/rand"
"errors"
@@ -41,6 +40,7 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
@@ -50,12 +50,12 @@ var (
)
type Account struct {
- Address []byte
+ Address common.Address
}
type Manager struct {
keyStore crypto.KeyStore2
- unlocked map[string]*unlocked
+ unlocked map[common.Address]*unlocked
mutex sync.RWMutex
}
@@ -67,40 +67,40 @@ type unlocked struct {
func NewManager(keyStore crypto.KeyStore2) *Manager {
return &Manager{
keyStore: keyStore,
- unlocked: make(map[string]*unlocked),
+ unlocked: make(map[common.Address]*unlocked),
}
}
-func (am *Manager) HasAccount(addr []byte) bool {
+func (am *Manager) HasAccount(addr common.Address) bool {
accounts, _ := am.Accounts()
for _, acct := range accounts {
- if bytes.Compare(acct.Address, addr) == 0 {
+ if acct.Address == addr {
return true
}
}
return false
}
-func (am *Manager) Primary() (addr []byte, err error) {
+func (am *Manager) Primary() (addr common.Address, err error) {
addrs, err := am.keyStore.GetKeyAddresses()
if os.IsNotExist(err) {
- return nil, ErrNoKeys
+ return common.Address{}, ErrNoKeys
} else if err != nil {
- return nil, err
+ return common.Address{}, err
}
if len(addrs) == 0 {
- return nil, ErrNoKeys
+ return common.Address{}, ErrNoKeys
}
return addrs[0], nil
}
-func (am *Manager) DeleteAccount(address []byte, auth string) error {
+func (am *Manager) DeleteAccount(address common.Address, auth string) error {
return am.keyStore.DeleteKey(address, auth)
}
func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) {
am.mutex.RLock()
- unlockedKey, found := am.unlocked[string(a.Address)]
+ unlockedKey, found := am.unlocked[a.Address]
am.mutex.RUnlock()
if !found {
return nil, ErrLocked
@@ -111,7 +111,7 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error)
// TimedUnlock unlocks the account with the given address.
// When timeout has passed, the account will be locked again.
-func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duration) error {
+func (am *Manager) TimedUnlock(addr common.Address, keyAuth string, timeout time.Duration) error {
key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil {
return err
@@ -124,7 +124,7 @@ func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duratio
// Unlock unlocks the account with the given address. The account
// stays unlocked until the program exits or until a TimedUnlock
// timeout (started after the call to Unlock) expires.
-func (am *Manager) Unlock(addr []byte, keyAuth string) error {
+func (am *Manager) Unlock(addr common.Address, keyAuth string) error {
key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil {
return err
@@ -157,10 +157,10 @@ func (am *Manager) Accounts() ([]Account, error) {
return accounts, err
}
-func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked {
+func (am *Manager) addUnlocked(addr common.Address, key *crypto.Key) *unlocked {
u := &unlocked{Key: key, abort: make(chan struct{})}
am.mutex.Lock()
- prev, found := am.unlocked[string(addr)]
+ prev, found := am.unlocked[addr]
if found {
// terminate dropLater for this key to avoid unexpected drops.
close(prev.abort)
@@ -169,12 +169,12 @@ func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked {
// key, i.e. when Unlock was used.
zeroKey(prev.PrivateKey)
}
- am.unlocked[string(addr)] = u
+ am.unlocked[addr] = u
am.mutex.Unlock()
return u
}
-func (am *Manager) dropLater(addr []byte, u *unlocked, timeout time.Duration) {
+func (am *Manager) dropLater(addr common.Address, u *unlocked, timeout time.Duration) {
t := time.NewTimer(timeout)
defer t.Stop()
select {
@@ -186,9 +186,9 @@ func (am *Manager) dropLater(addr []byte, u *unlocked, timeout time.Duration) {
// was launched with. we can check that using pointer equality
// because the map stores a new pointer every time the key is
// unlocked.
- if am.unlocked[string(addr)] == u {
+ if am.unlocked[addr] == u {
zeroKey(u.PrivateKey)
- delete(am.unlocked, string(addr))
+ delete(am.unlocked, addr)
}
am.mutex.Unlock()
}
@@ -204,7 +204,7 @@ func zeroKey(k *ecdsa.PrivateKey) {
// USE WITH CAUTION = this will save an unencrypted private key on disk
// no cli or js interface
-func (am *Manager) Export(path string, addr []byte, keyAuth string) error {
+func (am *Manager) Export(path string, addr common.Address, keyAuth string) error {
key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil {
return err
diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go
index 2b9956638..15923c366 100644
--- a/cmd/geth/admin.go
+++ b/cmd/geth/admin.go
@@ -126,7 +126,7 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
// Add the accouns to a new set
accountSet := set.New()
for _, account := range accounts {
- accountSet.Add(common.BytesToAddress(account.Address))
+ accountSet.Add(account.Address)
}
//ltxs := make([]*tx, len(txs))
@@ -275,14 +275,22 @@ func (js *jsre) verbosity(call otto.FunctionCall) otto.Value {
}
func (js *jsre) startMining(call otto.FunctionCall) otto.Value {
- _, err := call.Argument(0).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
+ var (
+ threads int64
+ err error
+ )
+
+ if len(call.ArgumentList) > 0 {
+ threads, err = call.Argument(0).ToInteger()
+ if err != nil {
+ fmt.Println(err)
+ return otto.FalseValue()
+ }
+ } else {
+ threads = 4
}
- // threads now ignored
- err = js.ethereum.StartMining()
+ err = js.ethereum.StartMining(int(threads))
if err != nil {
fmt.Println(err)
return otto.FalseValue()
@@ -383,7 +391,7 @@ func (js *jsre) unlock(call otto.FunctionCall) otto.Value {
}
}
am := js.ethereum.AccountManager()
- err = am.TimedUnlock(common.FromHex(addr), passphrase, time.Duration(seconds)*time.Second)
+ err = am.TimedUnlock(common.HexToAddress(addr), passphrase, time.Duration(seconds)*time.Second)
if err != nil {
fmt.Printf("Unlock account failed '%v'\n", err)
return otto.FalseValue()
@@ -425,7 +433,7 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value {
fmt.Printf("Could not create the account: %v", err)
return otto.UndefinedValue()
}
- return js.re.ToVal(common.ToHex(acct.Address))
+ return js.re.ToVal(acct.Address.Hex())
}
func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value {
diff --git a/cmd/geth/info_test.json b/cmd/geth/info_test.json
index e9e2d342e..1e0c271ac 100644
--- a/cmd/geth/info_test.json
+++ b/cmd/geth/info_test.json
@@ -1 +1 @@
-{"code":"605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.13","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","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","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}} \ No newline at end of file
+{"code":"605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.17","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","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","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}} \ No newline at end of file
diff --git a/cmd/geth/js.go b/cmd/geth/js.go
index 9b0ab0a1b..4ddb3bd9c 100644
--- a/cmd/geth/js.go
+++ b/cmd/geth/js.go
@@ -22,10 +22,11 @@ import (
"fmt"
"math/big"
"os"
- "path"
+ "path/filepath"
"strings"
"github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/eth"
@@ -164,7 +165,7 @@ func (self *jsre) UnlockAccount(addr []byte) bool {
return false
}
// TODO: allow retry
- if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil {
+ if err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(addr), pass); err != nil {
return false
} else {
fmt.Println("Account is now unlocked for this session.")
@@ -209,7 +210,7 @@ func (self *jsre) interactive() {
}
func (self *jsre) withHistory(op func(*os.File)) {
- hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
+ hist, err := os.OpenFile(filepath.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
fmt.Printf("unable to open history file: %v\n", err)
return
diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go
index 5587fe2b2..c2a0e2fe2 100644
--- a/cmd/geth/js_test.go
+++ b/cmd/geth/js_test.go
@@ -4,7 +4,6 @@ import (
"fmt"
"io/ioutil"
"os"
- "path"
"path/filepath"
"regexp"
"runtime"
@@ -25,10 +24,13 @@ import (
const (
testSolcPath = ""
+ solcVersion = "0.9.17"
testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
testBalance = "10000000000000000000"
+ // of empty string
+ testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
)
var (
@@ -43,7 +45,7 @@ type testjethre struct {
}
func (self *testjethre) UnlockAccount(acc []byte) bool {
- err := self.ethereum.AccountManager().Unlock(acc, "")
+ err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
if err != nil {
panic("unable to unlock")
}
@@ -66,7 +68,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
// set up mock genesis with balance on the testAddress
core.GenesisData = []byte(testGenesis)
- ks := crypto.NewKeyStorePassphrase(filepath.Join(tmp, "keys"))
+ ks := crypto.NewKeyStorePassphrase(filepath.Join(tmp, "keystore"))
am := accounts.NewManager(ks)
ethereum, err := eth.New(&eth.Config{
DataDir: tmp,
@@ -93,7 +95,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
t.Fatal(err)
}
- assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
+ assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
ds, err := docserver.New("/")
if err != nil {
t.Errorf("Error creating DocServer: %v", err)
@@ -215,7 +217,34 @@ func TestCheckTestAccountBalance(t *testing.T) {
checkEvalJSON(t, repl, `eth.getBalance(primary)`, `"`+testBalance+`"`)
}
+func TestSignature(t *testing.T) {
+ tmp, repl, ethereum := testJEthRE(t)
+ if err := ethereum.Start(); err != nil {
+ t.Errorf("error starting ethereum: %v", err)
+ return
+ }
+ defer ethereum.Stop()
+ defer os.RemoveAll(tmp)
+
+ val, err := repl.re.Run(`eth.sign({from: "` + testAddress + `", data: "` + testHash + `"})`)
+
+ // This is a very preliminary test, lacking actual signature verification
+ if err != nil {
+ t.Errorf("Error runnig js: %v", err)
+ return
+ }
+ output := val.String()
+ t.Logf("Output: %v", output)
+
+ regex := regexp.MustCompile(`^0x[0-9a-f]{130}$`)
+ if !regex.MatchString(output) {
+ t.Errorf("Signature is not 65 bytes represented in hexadecimal.")
+ return
+ }
+}
+
func TestContract(t *testing.T) {
+ t.Skip()
tmp, repl, ethereum := testJEthRE(t)
if err := ethereum.Start(); err != nil {
@@ -245,9 +274,16 @@ func TestContract(t *testing.T) {
checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`)
checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`)
- _, err = compiler.New("")
+ // if solc is found with right version, test it, otherwise read from file
+ sol, err := compiler.New("")
if err != nil {
t.Logf("solc not found: skipping compiler test")
+ } else if sol.Version() != solcVersion {
+ err = fmt.Errorf("solc wrong version found (%v, expect %v): skipping compiler test", sol.Version(), solcVersion)
+ t.Log(err)
+ }
+
+ if err != nil {
info, err := ioutil.ReadFile("info_test.json")
if err != nil {
t.Fatalf("%v", err)
@@ -259,6 +295,7 @@ func TestContract(t *testing.T) {
} else {
checkEvalJSON(t, repl, `contract = eth.compile.solidity(source)`, string(contractInfo))
}
+
checkEvalJSON(t, repl, `contract.code`, `"605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056"`)
checkEvalJSON(
@@ -298,7 +335,7 @@ multiply7 = new Multiply7(contractaddress);
}
checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`)
- checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, `"0x57e577316ccee6514797d9de9823af2004fdfe22bcfb6e39bbb8f92f57dcc421"`)
+ checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, `"0x0d067e2dd99a4d8f0c0279738b17130dd415a89f24a23f0e7cf68c546ae3089d"`)
checkEvalJSON(t, repl, `admin.contractInfo.registerUrl(primary, contenthash, "file://"+filename)`, `true`)
if err != nil {
t.Errorf("unexpected error registering, got %v", err)
@@ -324,7 +361,7 @@ func checkEvalJSON(t *testing.T, re *testjethre, expr, want string) error {
}
if err != nil {
_, file, line, _ := runtime.Caller(1)
- file = path.Base(file)
+ file = filepath.Base(file)
fmt.Printf("\t%s:%d: %v\n", file, line, err)
t.Fail()
}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index fd7aae4c2..1582953f7 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -26,7 +26,6 @@ import (
"io"
"io/ioutil"
"os"
- "path"
"path/filepath"
"runtime"
"strconv"
@@ -51,7 +50,7 @@ import _ "net/http/pprof"
const (
ClientIdentifier = "Geth"
- Version = "0.9.19"
+ Version = "0.9.20"
)
var (
@@ -366,11 +365,10 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass
// Load startup keys. XXX we are going to need a different format
// Attempt to unlock the account
passphrase = getPassPhrase(ctx, "", false)
- accbytes := common.FromHex(account)
- if len(accbytes) == 0 {
+ if len(account) == 0 {
utils.Fatalf("Invalid account address '%s'", account)
}
- err = am.Unlock(accbytes, passphrase)
+ err = am.Unlock(common.StringToAddress(account), passphrase)
if err != nil {
utils.Fatalf("Unlock account failed '%v'", err)
}
@@ -386,11 +384,11 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
if len(account) > 0 {
if account == "primary" {
- accbytes, err := am.Primary()
+ primaryAcc, err := am.Primary()
if err != nil {
utils.Fatalf("no primary account: %v", err)
}
- account = common.ToHex(accbytes)
+ account = primaryAcc.Hex()
}
unlockAccount(ctx, am, account)
}
@@ -401,7 +399,7 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
}
}
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
- if err := eth.StartMining(); err != nil {
+ if err := eth.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil {
utils.Fatalf("%v", err)
}
}
@@ -565,7 +563,7 @@ func upgradeDb(ctx *cli.Context) {
}
filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("2006-01-02_15:04:05"))
- exportFile := path.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)
+ exportFile := filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)
err = utils.ExportChain(ethereum.ChainManager(), exportFile)
if err != nil {
@@ -576,7 +574,7 @@ func upgradeDb(ctx *cli.Context) {
ethereum.StateDb().Close()
ethereum.ExtraDb().Close()
- os.RemoveAll(path.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))
+ os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))
ethereum, err = eth.New(cfg)
if err != nil {
diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go
index 66614478c..f443bacbc 100644
--- a/cmd/mist/gui.go
+++ b/cmd/mist/gui.go
@@ -27,7 +27,7 @@ import (
"fmt"
"io/ioutil"
"math/big"
- "path"
+ "path/filepath"
"runtime"
"sort"
"time"
@@ -79,7 +79,7 @@ type Gui struct {
// Create GUI, but doesn't start it
func NewWindow(ethereum *eth.Ethereum) *Gui {
- db, err := ethdb.NewLDBDatabase(path.Join(ethereum.DataDir, "tx_database"))
+ db, err := ethdb.NewLDBDatabase(filepath.Join(ethereum.DataDir, "tx_database"))
if err != nil {
panic(err)
}
@@ -92,7 +92,7 @@ func NewWindow(ethereum *eth.Ethereum) *Gui {
plugins: make(map[string]plugin),
serviceEvents: make(chan ServEv, 1),
}
- data, _ := ioutil.ReadFile(path.Join(ethereum.DataDir, "plugins.json"))
+ data, _ := ioutil.ReadFile(filepath.Join(ethereum.DataDir, "plugins.json"))
json.Unmarshal(data, &gui.plugins)
return gui
@@ -232,7 +232,7 @@ func (self *Gui) loadMergedMiningOptions() {
func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
var inout string
from, _ := tx.From()
- if gui.eth.AccountManager().HasAccount(common.Hex2Bytes(from.Hex())) {
+ if gui.eth.AccountManager().HasAccount(from) {
inout = "send"
} else {
inout = "recv"
diff --git a/cmd/mist/html_container.go b/cmd/mist/html_container.go
index 7c948885a..c9b1f1cd6 100644
--- a/cmd/mist/html_container.go
+++ b/cmd/mist/html_container.go
@@ -26,7 +26,6 @@ import (
"io/ioutil"
"net/url"
"os"
- "path"
"path/filepath"
"github.com/ethereum/go-ethereum/common"
@@ -80,7 +79,7 @@ func (app *HtmlApplication) RootFolder() string {
if err != nil {
return ""
}
- return path.Dir(common.WindonizePath(folder.RequestURI()))
+ return filepath.Dir(common.WindonizePath(folder.RequestURI()))
}
func (app *HtmlApplication) RecursiveFolders() []os.FileInfo {
files, _ := ioutil.ReadDir(app.RootFolder())
diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go
index a604e87ba..0618c5e92 100644
--- a/cmd/mist/ui_lib.go
+++ b/cmd/mist/ui_lib.go
@@ -22,7 +22,7 @@ package main
import (
"io/ioutil"
- "path"
+ "path/filepath"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
@@ -110,7 +110,7 @@ func (ui *UiLib) ConnectToPeer(nodeURL string) {
}
func (ui *UiLib) AssetPath(p string) string {
- return path.Join(ui.assetPath, p)
+ return filepath.Join(ui.assetPath, p)
}
func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
@@ -127,7 +127,7 @@ func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
)
}
-func (self *UiLib) Call(params map[string]interface{}) (string, error) {
+func (self *UiLib) Call(params map[string]interface{}) (string, string, error) {
object := mapToTxParams(params)
return self.XEth.Call(
@@ -159,7 +159,7 @@ func (self *UiLib) RemoveLocalTransaction(id int) {
func (self *UiLib) ToggleMining() bool {
if !self.eth.IsMining() {
- err := self.eth.StartMining()
+ err := self.eth.StartMining(4)
return err == nil
} else {
self.eth.StopMining()
@@ -218,7 +218,7 @@ func (self *UiLib) Messages(id int) *common.List {
}
func (self *UiLib) ReadFile(p string) string {
- content, err := ioutil.ReadFile(self.AssetPath(path.Join("ext", p)))
+ content, err := ioutil.ReadFile(self.AssetPath(filepath.Join("ext", p)))
if err != nil {
guilogger.Infoln("error reading file", p, ":", err)
}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index dd3b6c8a2..ddbd36b5c 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -7,7 +7,7 @@ import (
"math/big"
"net/http"
"os"
- "path"
+ "path/filepath"
"runtime"
"github.com/codegangsta/cli"
@@ -55,7 +55,7 @@ OPTIONS:
// NewApp creates an app with sane defaults.
func NewApp(version, usage string) *cli.App {
app := cli.NewApp()
- app.Name = path.Base(os.Args[0])
+ app.Name = filepath.Base(os.Args[0])
app.Author = ""
//app.Authors = nil
app.Email = ""
@@ -319,17 +319,17 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) {
dataDir := ctx.GlobalString(DataDirFlag.Name)
- blockDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "blockchain"))
+ blockDb, err := ethdb.NewLDBDatabase(filepath.Join(dataDir, "blockchain"))
if err != nil {
Fatalf("Could not open database: %v", err)
}
- stateDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "state"))
+ stateDb, err := ethdb.NewLDBDatabase(filepath.Join(dataDir, "state"))
if err != nil {
Fatalf("Could not open database: %v", err)
}
- extraDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "extra"))
+ extraDb, err := ethdb.NewLDBDatabase(filepath.Join(dataDir, "extra"))
if err != nil {
Fatalf("Could not open database: %v", err)
}
@@ -346,7 +346,7 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat
func GetAccountManager(ctx *cli.Context) *accounts.Manager {
dataDir := ctx.GlobalString(DataDirFlag.Name)
- ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
+ ks := crypto.NewKeyStorePassphrase(filepath.Join(dataDir, "keystore"))
return accounts.NewManager(ks)
}
diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go
index 36d0e96cc..6790f9a1d 100644
--- a/common/compiler/solidity.go
+++ b/common/compiler/solidity.go
@@ -7,7 +7,6 @@ import (
"io/ioutil"
"os"
"os/exec"
- "path"
"path/filepath"
"regexp"
"strings"
@@ -88,6 +87,10 @@ func (sol *Solidity) Info() string {
return fmt.Sprintf("solc v%s\nSolidity Compiler: %s\n%s", sol.version, sol.solcPath, flair)
}
+func (sol *Solidity) Version() string {
+ return sol.version
+}
+
func (sol *Solidity) Compile(source string) (contract *Contract, err error) {
if len(source) == 0 {
@@ -126,10 +129,10 @@ func (sol *Solidity) Compile(source string) (contract *Contract, err error) {
_, file := filepath.Split(matches[0])
base := strings.Split(file, ".")[0]
- codeFile := path.Join(wd, base+".binary")
- abiDefinitionFile := path.Join(wd, base+".abi")
- userDocFile := path.Join(wd, base+".docuser")
- developerDocFile := path.Join(wd, base+".docdev")
+ codeFile := filepath.Join(wd, base+".binary")
+ abiDefinitionFile := filepath.Join(wd, base+".abi")
+ userDocFile := filepath.Join(wd, base+".docuser")
+ developerDocFile := filepath.Join(wd, base+".docdev")
code, err := ioutil.ReadFile(codeFile)
if err != nil {
diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go
index 8fdcb6a99..68e54a7ec 100644
--- a/common/compiler/solidity_test.go
+++ b/common/compiler/solidity_test.go
@@ -9,6 +9,8 @@ import (
"github.com/ethereum/go-ethereum/common"
)
+const solcVersion = "0.9.17"
+
var (
source = `
contract test {
@@ -19,9 +21,9 @@ contract test {
}
`
code = "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056"
- info = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0","compilerVersion":"0.9.13","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}`
+ info = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0","compilerVersion":"0.9.17","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}`
- infohash = common.HexToHash("0xfdb031637e8a1c1891143f8d129ebc7f7c4e4b41ecad8c85abe1756190f74204")
+ infohash = common.HexToHash("0x834075768a68e500e459b9c3213750c84de3df47156500cb01bb664d3f88c60a")
)
func TestCompiler(t *testing.T) {
@@ -34,14 +36,16 @@ func TestCompiler(t *testing.T) {
t.Errorf("error compiling source. result %v: %v", contract, err)
return
}
- if contract.Code != code {
- t.Errorf("wrong code, expected\n%s, got\n%s", code, contract.Code)
- }
+ /*
+ if contract.Code != code {
+ t.Errorf("wrong code, expected\n%s, got\n%s", code, contract.Code)
+ }
+ */
}
func TestCompileError(t *testing.T) {
sol, err := New("")
- if err != nil {
+ if err != nil || sol.version != solcVersion {
t.Skip("no solc installed")
}
contract, err := sol.Compile(source[2:])
diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go
index f9b0c1dcc..a8d318b57 100644
--- a/common/natspec/natspec_e2e_test.go
+++ b/common/natspec/natspec_e2e_test.go
@@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"os"
+ "strings"
"testing"
"github.com/ethereum/go-ethereum/accounts"
@@ -84,7 +85,7 @@ type testFrontend struct {
}
func (self *testFrontend) UnlockAccount(acc []byte) bool {
- self.ethereum.AccountManager().Unlock(acc, "password")
+ self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "password")
return true
}
@@ -103,19 +104,19 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {
os.RemoveAll("/tmp/eth-natspec/")
- err = os.MkdirAll("/tmp/eth-natspec/keys", os.ModePerm)
+ err = os.MkdirAll("/tmp/eth-natspec/keystore", os.ModePerm)
if err != nil {
panic(err)
}
// create a testAddress
- ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keys")
+ ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keystore")
am := accounts.NewManager(ks)
testAccount, err := am.NewAccount("password")
if err != nil {
panic(err)
}
- testAddress := common.Bytes2Hex(testAccount.Address)
+ testAddress := strings.TrimPrefix(testAccount.Address.Hex(), "0x")
// set up mock genesis with balance on the testAddress
core.GenesisData = []byte(`{
diff --git a/common/path.go b/common/path.go
index f9b0212c1..3468b3366 100644
--- a/common/path.go
+++ b/common/path.go
@@ -4,7 +4,6 @@ import (
"fmt"
"os"
"os/user"
- "path"
"path/filepath"
"runtime"
"strings"
@@ -44,22 +43,22 @@ func FileExist(filePath string) bool {
}
func AbsolutePath(Datadir string, filename string) string {
- if path.IsAbs(filename) {
+ if filepath.IsAbs(filename) {
return filename
}
- return path.Join(Datadir, filename)
+ return filepath.Join(Datadir, filename)
}
func DefaultAssetPath() string {
var assetPath string
pwd, _ := os.Getwd()
- srcdir := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist")
+ srcdir := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist")
// If the current working directory is the go-ethereum dir
// assume a debug build and use the source directory as
// asset directory.
if pwd == srcdir {
- assetPath = path.Join(pwd, "assets")
+ assetPath = filepath.Join(pwd, "assets")
} else {
switch runtime.GOOS {
case "darwin":
@@ -67,9 +66,9 @@ func DefaultAssetPath() string {
exedir, _ := osext.ExecutableFolder()
assetPath = filepath.Join(exedir, "..", "Resources")
case "linux":
- assetPath = path.Join("usr", "share", "mist")
+ assetPath = filepath.Join("usr", "share", "mist")
case "windows":
- assetPath = path.Join(".", "assets")
+ assetPath = filepath.Join(".", "assets")
default:
assetPath = "."
}
@@ -78,7 +77,7 @@ func DefaultAssetPath() string {
// Check if the assetPath exists. If not, try the source directory
// This happens when binary is run from outside cmd/mist directory
if _, err := os.Stat(assetPath); os.IsNotExist(err) {
- assetPath = path.Join(srcdir, "assets")
+ assetPath = filepath.Join(srcdir, "assets")
}
return assetPath
@@ -87,11 +86,11 @@ func DefaultAssetPath() string {
func DefaultDataDir() string {
usr, _ := user.Current()
if runtime.GOOS == "darwin" {
- return path.Join(usr.HomeDir, "Library", "Ethereum")
+ return filepath.Join(usr.HomeDir, "Library", "Ethereum")
} else if runtime.GOOS == "windows" {
- return path.Join(usr.HomeDir, "AppData", "Roaming", "Ethereum")
+ return filepath.Join(usr.HomeDir, "AppData", "Roaming", "Ethereum")
} else {
- return path.Join(usr.HomeDir, ".ethereum")
+ return filepath.Join(usr.HomeDir, ".ethereum")
}
}
diff --git a/common/size.go b/common/size.go
index 0d9dbf558..4ea7f7b11 100644
--- a/common/size.go
+++ b/common/size.go
@@ -44,12 +44,6 @@ func CurrencyToString(num *big.Int) string {
)
switch {
- case num.Cmp(Douglas) >= 0:
- fin = new(big.Int).Div(num, Douglas)
- denom = "Douglas"
- case num.Cmp(Einstein) >= 0:
- fin = new(big.Int).Div(num, Einstein)
- denom = "Einstein"
case num.Cmp(Ether) >= 0:
fin = new(big.Int).Div(num, Ether)
denom = "Ether"
diff --git a/common/size_test.go b/common/size_test.go
index 1cbeff0a8..cfe7efe31 100644
--- a/common/size_test.go
+++ b/common/size_test.go
@@ -25,8 +25,6 @@ func (s *SizeSuite) TestStorageSizeString(c *checker.C) {
}
func (s *CommonSuite) TestCommon(c *checker.C) {
- douglas := CurrencyToString(BigPow(10, 43))
- einstein := CurrencyToString(BigPow(10, 22))
ether := CurrencyToString(BigPow(10, 19))
finney := CurrencyToString(BigPow(10, 16))
szabo := CurrencyToString(BigPow(10, 13))
@@ -35,8 +33,6 @@ func (s *CommonSuite) TestCommon(c *checker.C) {
ada := CurrencyToString(BigPow(10, 4))
wei := CurrencyToString(big.NewInt(10))
- c.Assert(douglas, checker.Equals, "10 Douglas")
- c.Assert(einstein, checker.Equals, "10 Einstein")
c.Assert(ether, checker.Equals, "10 Ether")
c.Assert(finney, checker.Equals, "10 Finney")
c.Assert(szabo, checker.Equals, "10 Szabo")
@@ -45,13 +41,3 @@ func (s *CommonSuite) TestCommon(c *checker.C) {
c.Assert(ada, checker.Equals, "10 Ada")
c.Assert(wei, checker.Equals, "10 Wei")
}
-
-func (s *CommonSuite) TestLarge(c *checker.C) {
- douglaslarge := CurrencyToString(BigPow(100000000, 43))
- adalarge := CurrencyToString(BigPow(100000000, 4))
- weilarge := CurrencyToString(big.NewInt(100000000))
-
- c.Assert(douglaslarge, checker.Equals, "10000E298 Douglas")
- c.Assert(adalarge, checker.Equals, "10000E7 Einstein")
- c.Assert(weilarge, checker.Equals, "100 Babbage")
-}
diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go
index 50915459b..f456e4fff 100644
--- a/core/chain_manager_test.go
+++ b/core/chain_manager_test.go
@@ -4,7 +4,7 @@ import (
"fmt"
"math/big"
"os"
- "path"
+ "path/filepath"
"runtime"
"strconv"
"testing"
@@ -94,7 +94,7 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
}
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
- fh, err := os.OpenFile(path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm)
+ fh, err := os.OpenFile(filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm)
if err != nil {
return nil, err
}
diff --git a/core/events.go b/core/events.go
index 3da668af5..1ea35c2f4 100644
--- a/core/events.go
+++ b/core/events.go
@@ -1,8 +1,10 @@
package core
import (
- "github.com/ethereum/go-ethereum/core/types"
+ "math/big"
+
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
)
// TxPreEvent is posted when a transaction enters the transaction pool.
@@ -44,6 +46,8 @@ type ChainUncleEvent struct {
type ChainHeadEvent struct{ Block *types.Block }
+type GasPriceChanged struct{ Price *big.Int }
+
// Mining operation events
type StartMining struct{}
type TopMining struct{}
diff --git a/core/manager.go b/core/manager.go
index 9b5407a9e..433ada7ee 100644
--- a/core/manager.go
+++ b/core/manager.go
@@ -1,12 +1,14 @@
package core
import (
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
)
type Backend interface {
+ AccountManager() *accounts.Manager
BlockProcessor() *BlockProcessor
ChainManager() *ChainManager
TxPool() *TxPool
diff --git a/core/transaction_pool.go b/core/transaction_pool.go
index 6898a4bda..e68f7406a 100644
--- a/core/transaction_pool.go
+++ b/core/transaction_pool.go
@@ -21,7 +21,7 @@ var (
ErrInvalidSender = errors.New("Invalid sender")
ErrNonce = errors.New("Nonce too low")
ErrBalance = errors.New("Insufficient balance")
- ErrNonExistentAccount = errors.New("Account does not exist")
+ ErrNonExistentAccount = errors.New("Account does not exist or account balance too low")
ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value")
ErrIntrinsicGas = errors.New("Intrinsic gas too low")
ErrGasLimit = errors.New("Exceeds block gas limit")
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 3c5783014..4bbd62f7f 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -181,11 +181,11 @@ func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
// Used only by block tests.
func ImportBlockTestKey(privKeyBytes []byte) error {
- ks := NewKeyStorePassphrase(common.DefaultDataDir() + "/keys")
+ ks := NewKeyStorePassphrase(common.DefaultDataDir() + "/keystore")
ecKey := ToECDSA(privKeyBytes)
key := &Key{
Id: uuid.NewRandom(),
- Address: PubkeyToAddress(ecKey.PublicKey),
+ Address: common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
PrivateKey: ecKey,
}
err := ks.StoreKey(key, "")
@@ -231,13 +231,13 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
ecKey := ToECDSA(ethPriv)
key = &Key{
Id: nil,
- Address: PubkeyToAddress(ecKey.PublicKey),
+ Address: common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
PrivateKey: ecKey,
}
- derivedAddr := common.Bytes2Hex(key.Address)
+ derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr {
- err = errors.New("decrypted addr not equal to expected addr")
+ err = errors.New(fmt.Sprintf("decrypted addr not equal to expected addr ", derivedAddr, expectedAddr))
}
return key, err
}
@@ -252,7 +252,7 @@ func aesCBCDecrypt(key []byte, cipherText []byte, iv []byte) (plainText []byte,
decrypter.CryptBlocks(paddedPlainText, cipherText)
plainText = PKCS7Unpad(paddedPlainText)
if plainText == nil {
- err = errors.New("Decryption failed: PKCS7Unpad failed after decryption")
+ err = errors.New("Decryption failed: PKCS7Unpad failed after AES decryption")
}
return plainText, err
}
diff --git a/crypto/key.go b/crypto/key.go
index 0b84bfec1..0c5ce4254 100644
--- a/crypto/key.go
+++ b/crypto/key.go
@@ -26,44 +26,69 @@ package crypto
import (
"bytes"
"crypto/ecdsa"
+ "encoding/hex"
"encoding/json"
"io"
"code.google.com/p/go-uuid/uuid"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+const (
+ version = "1"
)
type Key struct {
Id uuid.UUID // Version 4 "random" for unique id not derived from key data
// to simplify lookups we also store the address
- Address []byte
+ Address common.Address
// we only store privkey as pubkey/address can be derived from it
// privkey in this struct is always in plaintext
PrivateKey *ecdsa.PrivateKey
}
type plainKeyJSON struct {
- Id []byte
- Address []byte
- PrivateKey []byte
+ Address string `json:"address"`
+ PrivateKey string `json:"privatekey"`
+ Id string `json:"id"`
+ Version string `json:"version"`
}
-type cipherJSON struct {
- Salt []byte
- IV []byte
- CipherText []byte
+type encryptedKeyJSON struct {
+ Address string `json:"address"`
+ Crypto cryptoJSON
+ Id string `json:"id"`
+ Version string `json:"version"`
}
-type encryptedKeyJSON struct {
- Id []byte
- Address []byte
- Crypto cipherJSON
+type cryptoJSON struct {
+ Cipher string `json:"cipher"`
+ CipherText string `json:"ciphertext"`
+ CipherParams cipherparamsJSON `json:"cipherparams"`
+ KDF string `json:"kdf"`
+ KDFParams scryptParamsJSON `json:"kdfparams"`
+ MAC string `json:"mac"`
+ Version string `json:"version"`
+}
+
+type cipherparamsJSON struct {
+ IV string `json:"iv"`
+}
+
+type scryptParamsJSON struct {
+ N int `json:"n"`
+ R int `json:"r"`
+ P int `json:"p"`
+ DkLen int `json:"dklen"`
+ Salt string `json:"salt"`
}
func (k *Key) MarshalJSON() (j []byte, err error) {
jStruct := plainKeyJSON{
- k.Id,
- k.Address,
- FromECDSA(k.PrivateKey),
+ hex.EncodeToString(k.Address[:]),
+ hex.EncodeToString(FromECDSA(k.PrivateKey)),
+ k.Id.String(),
+ version,
}
j, err = json.Marshal(jStruct)
return j, err
@@ -77,19 +102,29 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
}
u := new(uuid.UUID)
- *u = keyJSON.Id
+ *u = uuid.Parse(keyJSON.Id)
k.Id = *u
- k.Address = keyJSON.Address
- k.PrivateKey = ToECDSA(keyJSON.PrivateKey)
+ addr, err := hex.DecodeString(keyJSON.Address)
+ if err != nil {
+ return err
+ }
+
+ privkey, err := hex.DecodeString(keyJSON.PrivateKey)
+ if err != nil {
+ return err
+ }
+
+ k.Address = common.BytesToAddress(addr)
+ k.PrivateKey = ToECDSA(privkey)
- return err
+ return nil
}
func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
id := uuid.NewRandom()
key := &Key{
Id: id,
- Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
+ Address: common.BytesToAddress(PubkeyToAddress(privateKeyECDSA.PublicKey)),
PrivateKey: privateKeyECDSA,
}
return key
diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go
index 7d21b6604..d9a5a81f9 100644
--- a/crypto/key_store_passphrase.go
+++ b/crypto/key_store_passphrase.go
@@ -28,24 +28,25 @@ the private key is encrypted and on disk uses another JSON encoding.
Cryptography:
-1. Encryption key is scrypt derived key from user passphrase. Scrypt parameters
+1. Encryption key is first 16 bytes of SHA3-256 of first 16 bytes of
+ scrypt derived key from user passphrase. Scrypt parameters
(work factors) [1][2] are defined as constants below.
-2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext.
-3. Checksum is SHA3 of the private key bytes.
-4. Plaintext is concatenation of private key bytes and checksum.
-5. Encryption algo is AES 256 CBC [3][4]
-6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext.
+2. Scrypt salt is 32 random bytes from CSPRNG.
+ It's stored in plain next to ciphertext in key file.
+3. MAC is SHA3-256 of concatenation of ciphertext and last 16 bytes of scrypt derived key.
+4. Plaintext is the EC private key bytes.
+5. Encryption algo is AES 128 CBC [3][4]
+6. CBC IV is 16 random bytes from CSPRNG.
+ It's stored in plain next to ciphertext in key file.
7. Plaintext padding is PKCS #7 [5][6]
Encoding:
-1. On disk, ciphertext, salt and IV are encoded in a nested JSON object.
+1. On disk, the ciphertext, MAC, salt and IV are encoded in a nested JSON object.
cat a key file to see the structure.
2. byte arrays are base64 JSON strings.
3. The EC private key bytes are in uncompressed form [7].
They are a big-endian byte slice of the absolute value of D [8][9].
-4. The checksum is the last 32 bytes of the plaintext byte array and the
- private key is the preceeding bytes.
References:
@@ -72,14 +73,17 @@ import (
"errors"
"io"
"os"
- "path"
+ "path/filepath"
"code.google.com/p/go-uuid/uuid"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/randentropy"
"golang.org/x/crypto/scrypt"
)
const (
+ keyHeaderVersion = "1"
+ keyHeaderKDF = "scrypt"
// 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
scryptN = 1 << 18
scryptr = 8
@@ -99,7 +103,7 @@ func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *K
return GenerateNewKeyDefault(ks, rand, auth)
}
-func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *Key, err error) {
+func (ks keyStorePassphrase) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
keyBytes, keyId, err := DecryptKey(ks, keyAddr, auth)
if err != nil {
return nil, err
@@ -112,43 +116,63 @@ func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *Key, err
return key, err
}
-func (ks keyStorePassphrase) GetKeyAddresses() (addresses [][]byte, err error) {
+func (ks keyStorePassphrase) GetKeyAddresses() (addresses []common.Address, err error) {
return GetKeyAddresses(ks.keysDirPath)
}
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
authArray := []byte(auth)
- salt := randentropy.GetEntropyMixed(32)
+ salt := randentropy.GetEntropyCSPRNG(32)
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
if err != nil {
return err
}
+ encryptKey := Sha3(derivedKey[:16])[:16]
+
keyBytes := FromECDSA(key.PrivateKey)
- keyBytesHash := Sha3(keyBytes)
- toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...))
+ toEncrypt := PKCS7Pad(keyBytes)
- AES256Block, err := aes.NewCipher(derivedKey)
+ AES128Block, err := aes.NewCipher(encryptKey)
if err != nil {
return err
}
- iv := randentropy.GetEntropyMixed(aes.BlockSize) // 16
- AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv)
+ iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
+ AES128CBCEncrypter := cipher.NewCBCEncrypter(AES128Block, iv)
cipherText := make([]byte, len(toEncrypt))
- AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
+ AES128CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
+
+ mac := Sha3(derivedKey[16:32], cipherText)
- cipherStruct := cipherJSON{
- salt,
- iv,
- cipherText,
+ scryptParamsJSON := scryptParamsJSON{
+ N: scryptN,
+ R: scryptr,
+ P: scryptp,
+ DkLen: scryptdkLen,
+ Salt: hex.EncodeToString(salt),
}
- keyStruct := encryptedKeyJSON{
- key.Id,
- key.Address,
- cipherStruct,
+
+ cipherParamsJSON := cipherparamsJSON{
+ IV: hex.EncodeToString(iv),
}
- keyJSON, err := json.Marshal(keyStruct)
+
+ cryptoStruct := cryptoJSON{
+ Cipher: "aes-128-cbc",
+ CipherText: hex.EncodeToString(cipherText),
+ CipherParams: cipherParamsJSON,
+ KDF: "scrypt",
+ KDFParams: scryptParamsJSON,
+ MAC: hex.EncodeToString(mac),
+ Version: "1",
+ }
+ encryptedKeyJSON := encryptedKeyJSON{
+ hex.EncodeToString(key.Address[:]),
+ cryptoStruct,
+ key.Id.String(),
+ version,
+ }
+ keyJSON, err := json.Marshal(encryptedKeyJSON)
if err != nil {
return err
}
@@ -156,18 +180,18 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
return WriteKeyFile(key.Address, ks.keysDirPath, keyJSON)
}
-func (ks keyStorePassphrase) DeleteKey(keyAddr []byte, auth string) (err error) {
+func (ks keyStorePassphrase) DeleteKey(keyAddr common.Address, auth string) (err error) {
// only delete if correct passphrase is given
_, _, err = DecryptKey(ks, keyAddr, auth)
if err != nil {
return err
}
- keyDirPath := path.Join(ks.keysDirPath, hex.EncodeToString(keyAddr))
+ keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr[:]))
return os.RemoveAll(keyDirPath)
}
-func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []byte, keyId []byte, err error) {
+func DecryptKey(ks keyStorePassphrase, keyAddr common.Address, auth string) (keyBytes []byte, keyId []byte, err error) {
fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
if err != nil {
return nil, nil, err
@@ -176,25 +200,48 @@ func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []
keyProtected := new(encryptedKeyJSON)
err = json.Unmarshal(fileContent, keyProtected)
- keyId = keyProtected.Id
- salt := keyProtected.Crypto.Salt
- iv := keyProtected.Crypto.IV
- cipherText := keyProtected.Crypto.CipherText
+ keyId = uuid.Parse(keyProtected.Id)
- authArray := []byte(auth)
- derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
+ mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
if err != nil {
return nil, nil, err
}
- plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
+
+ salt, err := hex.DecodeString(keyProtected.Crypto.KDFParams.Salt)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ n := keyProtected.Crypto.KDFParams.N
+ r := keyProtected.Crypto.KDFParams.R
+ p := keyProtected.Crypto.KDFParams.P
+ dkLen := keyProtected.Crypto.KDFParams.DkLen
+
+ authArray := []byte(auth)
+ derivedKey, err := scrypt.Key(authArray, salt, n, r, p, dkLen)
if err != nil {
return nil, nil, err
}
- keyBytes = plainText[:len(plainText)-32]
- keyBytesHash := plainText[len(plainText)-32:]
- if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
- err = errors.New("Decryption failed: checksum mismatch")
+
+ calculatedMAC := Sha3(derivedKey[16:32], cipherText)
+ if !bytes.Equal(calculatedMAC, mac) {
+ err = errors.New("Decryption failed: MAC mismatch")
+ return nil, nil, err
+ }
+
+ plainText, err := aesCBCDecrypt(Sha3(derivedKey[:16])[:16], cipherText, iv)
+ if err != nil {
return nil, nil, err
}
- return keyBytes, keyId, err
+ return plainText, keyId, err
}
diff --git a/crypto/key_store_plain.go b/crypto/key_store_plain.go
index 9bbaf1c15..6a8afe27d 100644
--- a/crypto/key_store_plain.go
+++ b/crypto/key_store_plain.go
@@ -27,20 +27,21 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
+ "github.com/ethereum/go-ethereum/common"
"io"
"io/ioutil"
"os"
- "path"
+ "path/filepath"
)
// TODO: rename to KeyStore when replacing existing KeyStore
type KeyStore2 interface {
// create new key using io.Reader entropy source and optionally using auth string
GenerateNewKey(io.Reader, string) (*Key, error)
- 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
- DeleteKey([]byte, string) error // delete key by addr and auth string
+ GetKey(common.Address, string) (*Key, error) // key from addr and auth string
+ GetKeyAddresses() ([]common.Address, error) // get all addresses
+ StoreKey(*Key, string) error // store key optionally using auth string
+ DeleteKey(common.Address, string) error // delete key by addr and auth string
}
type keyStorePlain struct {
@@ -66,7 +67,7 @@ func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key,
return key, err
}
-func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error) {
+func (ks keyStorePlain) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
if err != nil {
return nil, err
@@ -77,7 +78,7 @@ func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error
return key, err
}
-func (ks keyStorePlain) GetKeyAddresses() (addresses [][]byte, err error) {
+func (ks keyStorePlain) GetKeyAddresses() (addresses []common.Address, err error) {
return GetKeyAddresses(ks.keysDirPath)
}
@@ -90,21 +91,21 @@ func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
return err
}
-func (ks keyStorePlain) DeleteKey(keyAddr []byte, auth string) (err error) {
- keyDirPath := path.Join(ks.keysDirPath, hex.EncodeToString(keyAddr))
+func (ks keyStorePlain) DeleteKey(keyAddr common.Address, auth string) (err error) {
+ keyDirPath := filepath.Join(ks.keysDirPath, keyAddr.Hex())
err = os.RemoveAll(keyDirPath)
return err
}
-func GetKeyFile(keysDirPath string, keyAddr []byte) (fileContent []byte, err error) {
- fileName := hex.EncodeToString(keyAddr)
- return ioutil.ReadFile(path.Join(keysDirPath, fileName, fileName))
+func GetKeyFile(keysDirPath string, keyAddr common.Address) (fileContent []byte, err error) {
+ fileName := hex.EncodeToString(keyAddr[:])
+ return ioutil.ReadFile(filepath.Join(keysDirPath, fileName, fileName))
}
-func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) {
- addrHex := hex.EncodeToString(addr)
- keyDirPath := path.Join(keysDirPath, addrHex)
- keyFilePath := path.Join(keyDirPath, addrHex)
+func WriteKeyFile(addr common.Address, keysDirPath string, content []byte) (err error) {
+ addrHex := hex.EncodeToString(addr[:])
+ keyDirPath := filepath.Join(keysDirPath, addrHex)
+ keyFilePath := filepath.Join(keyDirPath, addrHex)
err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
if err != nil {
return err
@@ -112,7 +113,7 @@ func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) {
return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
}
-func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) {
+func GetKeyAddresses(keysDirPath string) (addresses []common.Address, err error) {
fileInfos, err := ioutil.ReadDir(keysDirPath)
if err != nil {
return nil, err
@@ -122,7 +123,7 @@ func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) {
if err != nil {
continue
}
- addresses = append(addresses, address)
+ addresses = append(addresses, common.BytesToAddress(address))
}
return addresses, err
}
diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go
index f0a1e567b..6e50afe34 100644
--- a/crypto/key_store_test.go
+++ b/crypto/key_store_test.go
@@ -1,8 +1,8 @@
package crypto
import (
- "github.com/ethereum/go-ethereum/crypto/randentropy"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto/randentropy"
"reflect"
"testing"
)
diff --git a/crypto/randentropy/rand_entropy.go b/crypto/randentropy/rand_entropy.go
index b87fa564e..68bb8808b 100644
--- a/crypto/randentropy/rand_entropy.go
+++ b/crypto/randentropy/rand_entropy.go
@@ -2,12 +2,8 @@ package randentropy
import (
crand "crypto/rand"
- "encoding/binary"
"github.com/ethereum/go-ethereum/crypto/sha3"
"io"
- "os"
- "strings"
- "time"
)
var Reader io.Reader = &randEntropy{}
@@ -16,7 +12,7 @@ type randEntropy struct {
}
func (*randEntropy) Read(bytes []byte) (n int, err error) {
- readBytes := GetEntropyMixed(len(bytes))
+ readBytes := GetEntropyCSPRNG(len(bytes))
copy(bytes, readBytes)
return len(bytes), nil
}
@@ -29,40 +25,6 @@ func Sha3(data []byte) []byte {
return d.Sum(nil)
}
-// TODO: verify. this needs to be audited
-// we start with crypt/rand, then XOR in additional entropy from OS
-func GetEntropyMixed(n int) []byte {
- startTime := time.Now().UnixNano()
- // for each source, we take SHA3 of the source and use it as seed to math/rand
- // then read bytes from it and XOR them onto the bytes read from crypto/rand
- mainBuff := GetEntropyCSPRNG(n)
- // 1. OS entropy sources
- startTimeBytes := make([]byte, 32)
- binary.PutVarint(startTimeBytes, startTime)
- startTimeHash := Sha3(startTimeBytes)
- mixBytes(mainBuff, startTimeHash)
-
- pid := os.Getpid()
- pidBytes := make([]byte, 32)
- binary.PutUvarint(pidBytes, uint64(pid))
- pidHash := Sha3(pidBytes)
- mixBytes(mainBuff, pidHash)
-
- osEnv := os.Environ()
- osEnvBytes := []byte(strings.Join(osEnv, ""))
- osEnvHash := Sha3(osEnvBytes)
- mixBytes(mainBuff, osEnvHash)
-
- // not all OS have hostname in env variables
- osHostName, err := os.Hostname()
- if err != nil {
- osHostNameBytes := []byte(osHostName)
- osHostNameHash := Sha3(osHostNameBytes)
- mixBytes(mainBuff, osHostNameHash)
- }
- return mainBuff
-}
-
func GetEntropyCSPRNG(n int) []byte {
mainBuff := make([]byte, n)
_, err := io.ReadFull(crand.Reader, mainBuff)
@@ -71,14 +33,3 @@ func GetEntropyCSPRNG(n int) []byte {
}
return mainBuff
}
-
-func mixBytes(buff []byte, mixBuff []byte) []byte {
- bytesToMix := len(buff)
- if bytesToMix > 32 {
- bytesToMix = 32
- }
- for i := 0; i < bytesToMix; i++ {
- buff[i] ^= mixBuff[i]
- }
- return buff
-}
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index f8cc60e82..8ed81a1ed 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -59,7 +59,7 @@ func GenerateKeyPair() ([]byte, []byte) {
const seckey_len = 32
var pubkey []byte = make([]byte, pubkey_len)
- var seckey []byte = randentropy.GetEntropyMixed(seckey_len)
+ var seckey []byte = randentropy.GetEntropyCSPRNG(seckey_len)
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
@@ -99,7 +99,7 @@ func GeneratePubKey(seckey []byte) ([]byte, error) {
}
func Sign(msg []byte, seckey []byte) ([]byte, error) {
- nonce := randentropy.GetEntropyMixed(32)
+ nonce := randentropy.GetEntropyCSPRNG(32)
var sig []byte = make([]byte, 65)
var recid C.int
diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go
index 3599fde38..14d260beb 100644
--- a/crypto/secp256k1/secp256_test.go
+++ b/crypto/secp256k1/secp256_test.go
@@ -14,7 +14,7 @@ const SigSize = 65 //64+1
func Test_Secp256_00(t *testing.T) {
- var nonce []byte = randentropy.GetEntropyMixed(32) //going to get bitcoins stolen!
+ var nonce []byte = randentropy.GetEntropyCSPRNG(32) //going to get bitcoins stolen!
if len(nonce) != 32 {
t.Fatal()
@@ -52,7 +52,7 @@ func Test_Secp256_01(t *testing.T) {
//test size of messages
func Test_Secp256_02s(t *testing.T) {
pubkey, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
CompactSigTest(sig)
if sig == nil {
@@ -75,7 +75,7 @@ func Test_Secp256_02s(t *testing.T) {
//test signing message
func Test_Secp256_02(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
if sig == nil {
t.Fatal("Signature nil")
@@ -98,7 +98,7 @@ func Test_Secp256_02(t *testing.T) {
//test pubkey recovery
func Test_Secp256_02a(t *testing.T) {
pubkey1, seckey1 := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey1)
if sig == nil {
@@ -127,7 +127,7 @@ func Test_Secp256_02a(t *testing.T) {
func Test_Secp256_03(t *testing.T) {
_, seckey := GenerateKeyPair()
for i := 0; i < TESTS; i++ {
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
CompactSigTest(sig)
@@ -143,7 +143,7 @@ func Test_Secp256_03(t *testing.T) {
func Test_Secp256_04(t *testing.T) {
for i := 0; i < TESTS; i++ {
pubkey1, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
CompactSigTest(sig)
@@ -166,7 +166,7 @@ func Test_Secp256_04(t *testing.T) {
// -SIPA look at this
func randSig() []byte {
- sig := randentropy.GetEntropyMixed(65)
+ sig := randentropy.GetEntropyCSPRNG(65)
sig[32] &= 0x70
sig[64] %= 4
return sig
@@ -174,7 +174,7 @@ func randSig() []byte {
func Test_Secp256_06a_alt0(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
if sig == nil {
@@ -205,12 +205,12 @@ func Test_Secp256_06a_alt0(t *testing.T) {
func Test_Secp256_06b(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
fail_count := 0
for i := 0; i < TESTS; i++ {
- msg = randentropy.GetEntropyMixed(32)
+ msg = randentropy.GetEntropyCSPRNG(32)
pubkey2, _ := RecoverPubkey(msg, sig)
if bytes.Equal(pubkey1, pubkey2) == true {
t.Fail()
diff --git a/eth/backend.go b/eth/backend.go
index 8f0789467..362a7eab7 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -7,7 +7,6 @@ import (
"io/ioutil"
"math/big"
"os"
- "path"
"path/filepath"
"strings"
"time"
@@ -145,7 +144,7 @@ func (cfg *Config) nodeKey() (*ecdsa.PrivateKey, error) {
return cfg.NodeKey, nil
}
// use persistent key if present
- keyfile := path.Join(cfg.DataDir, "nodekey")
+ keyfile := filepath.Join(cfg.DataDir, "nodekey")
key, err := crypto.LoadECDSA(keyfile)
if err == nil {
return key, nil
@@ -207,29 +206,33 @@ func New(config *Config) (*Ethereum, error) {
logger.NewJSONsystem(config.DataDir, config.LogJSON)
}
+ // Let the database take 3/4 of the max open files (TODO figure out a way to get the actual limit of the open files)
+ const dbCount = 3
+ ethdb.OpenFileLimit = 256 / (dbCount + 1)
+
newdb := config.NewDB
if newdb == nil {
newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path) }
}
- blockDb, err := newdb(path.Join(config.DataDir, "blockchain"))
+ blockDb, err := newdb(filepath.Join(config.DataDir, "blockchain"))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("blockchain db err: %v", err)
}
- stateDb, err := newdb(path.Join(config.DataDir, "state"))
+ stateDb, err := newdb(filepath.Join(config.DataDir, "state"))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("state db err: %v", err)
}
- extraDb, err := newdb(path.Join(config.DataDir, "extra"))
+ extraDb, err := newdb(filepath.Join(config.DataDir, "extra"))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("extra db err: %v", err)
}
- nodeDb := path.Join(config.DataDir, "nodes")
+ nodeDb := filepath.Join(config.DataDir, "nodes")
// Perform database sanity checks
d, _ := blockDb.Get([]byte("ProtocolVersion"))
protov := int(common.NewValue(d).Uint())
if protov != config.ProtocolVersion && protov != 0 {
- path := path.Join(config.DataDir, "blockchain")
+ path := filepath.Join(config.DataDir, "blockchain")
return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, config.ProtocolVersion, path)
}
saveProtocolVersion(blockDb, config.ProtocolVersion)
@@ -267,7 +270,7 @@ func New(config *Config) (*Ethereum, error) {
eth.txPool = core.NewTxPool(eth.EventMux(), eth.chainManager.State, eth.chainManager.GasLimit)
eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockProcessor)
- eth.miner = miner.New(eth, eth.pow, config.MinerThreads)
+ eth.miner = miner.New(eth, eth.pow)
eth.miner.SetGasPrice(config.GasPrice)
eth.protocolManager = NewProtocolManager(config.ProtocolVersion, config.NetworkId, eth.eventMux, eth.txPool, eth.chainManager, eth.downloader)
@@ -368,7 +371,7 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
s.chainManager.ResetWithGenesisBlock(gb)
}
-func (s *Ethereum) StartMining() error {
+func (s *Ethereum) StartMining(threads int) error {
eb, err := s.Etherbase()
if err != nil {
err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)
@@ -376,21 +379,24 @@ func (s *Ethereum) StartMining() error {
return err
}
- go s.miner.Start(eb)
+ go s.miner.Start(eb, threads)
return nil
}
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
eb = s.etherbase
if (eb == common.Address{}) {
- var ebbytes []byte
- ebbytes, err = s.accountManager.Primary()
- eb = common.BytesToAddress(ebbytes)
- if (eb == common.Address{}) {
+ primary, err := s.accountManager.Primary()
+ if err != nil {
+ return eb, err
+ }
+ if (primary == common.Address{}) {
err = fmt.Errorf("no accounts found")
+ return eb, err
}
+ eb = primary
}
- return
+ return eb, nil
}
func (s *Ethereum) StopMining() { s.miner.Stop() }
@@ -451,6 +457,8 @@ func (s *Ethereum) Start() error {
return nil
}
+// sync databases every minute. If flushing fails we exit immediatly. The system
+// may not continue under any circumstances.
func (s *Ethereum) syncDatabases() {
ticker := time.NewTicker(1 * time.Minute)
done:
@@ -459,13 +467,13 @@ done:
case <-ticker.C:
// don't change the order of database flushes
if err := s.extraDb.Flush(); err != nil {
- glog.V(logger.Error).Infof("error: flush extraDb: %v\n", err)
+ glog.Fatalf("fatal error: flush extraDb: %v (Restart your node. We are aware of this issue)\n", err)
}
if err := s.stateDb.Flush(); err != nil {
- glog.V(logger.Error).Infof("error: flush stateDb: %v\n", err)
+ glog.Fatalf("fatal error: flush stateDb: %v (Restart your node. We are aware of this issue)\n", err)
}
if err := s.blockDb.Flush(); err != nil {
- glog.V(logger.Error).Infof("error: flush blockDb: %v\n", err)
+ glog.Fatalf("fatal error: flush blockDb: %v (Restart your node. We are aware of this issue)\n", err)
}
case <-s.shutdownChan:
break done
@@ -537,7 +545,7 @@ func (self *Ethereum) syncAccounts(tx *types.Transaction) {
return
}
- if self.accountManager.HasAccount(from.Bytes()) {
+ if self.accountManager.HasAccount(from) {
if self.chainManager.TxState().GetNonce(from) < tx.Nonce() {
self.chainManager.TxState().SetNonce(from, tx.Nonce())
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 14ca2cd3d..577152a21 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -28,7 +28,7 @@ var (
errUnknownPeer = errors.New("peer's unknown or unhealthy")
errBadPeer = errors.New("action from bad peer ignored")
errNoPeers = errors.New("no peers to keep download active")
- errPendingQueue = errors.New("pending items in queue")
+ ErrPendingQueue = errors.New("pending items in queue")
ErrTimeout = errors.New("timeout")
errEmptyHashSet = errors.New("empty hash set by peer")
errPeersUnavailable = errors.New("no peers available or all peers tried for block download process")
@@ -49,12 +49,6 @@ type blockPack struct {
blocks []*types.Block
}
-type syncPack struct {
- peer *peer
- hash common.Hash
- ignoreInitial bool
-}
-
type hashPack struct {
peerId string
hashes []common.Hash
@@ -63,7 +57,7 @@ type hashPack struct {
type Downloader struct {
mu sync.RWMutex
queue *queue
- peers peers
+ peers *peerSet
activePeer string
// Callbacks
@@ -83,7 +77,7 @@ type Downloader struct {
func New(hasBlock hashCheckFn, getBlock getBlockFn) *Downloader {
downloader := &Downloader{
queue: newQueue(),
- peers: make(peers),
+ peers: newPeerSet(),
hasBlock: hasBlock,
getBlock: getBlock,
newPeerCh: make(chan *peer, 1),
@@ -98,29 +92,26 @@ func (d *Downloader) Stats() (current int, max int) {
return d.queue.Size()
}
-func (d *Downloader) RegisterPeer(id string, hash common.Hash, getHashes hashFetcherFn, getBlocks blockFetcherFn) error {
- d.mu.Lock()
- defer d.mu.Unlock()
-
- glog.V(logger.Detail).Infoln("Register peer", id)
-
- // Create a new peer and add it to the list of known peers
- peer := newPeer(id, hash, getHashes, getBlocks)
- // add peer to our peer set
- d.peers[id] = peer
- // broadcast new peer
-
+// RegisterPeer injects a new download peer into the set of block source to be
+// used for fetching hashes and blocks from.
+func (d *Downloader) RegisterPeer(id string, head common.Hash, getHashes hashFetcherFn, getBlocks blockFetcherFn) error {
+ glog.V(logger.Detail).Infoln("Registering peer", id)
+ if err := d.peers.Register(newPeer(id, head, getHashes, getBlocks)); err != nil {
+ glog.V(logger.Error).Infoln("Register failed:", err)
+ return err
+ }
return nil
}
-// UnregisterPeer unregisters a peer. This will prevent any action from the specified peer.
-func (d *Downloader) UnregisterPeer(id string) {
- d.mu.Lock()
- defer d.mu.Unlock()
-
- glog.V(logger.Detail).Infoln("Unregister peer", id)
-
- delete(d.peers, id)
+// UnregisterPeer remove a peer from the known list, preventing any action from
+// the specified peer.
+func (d *Downloader) UnregisterPeer(id string) error {
+ glog.V(logger.Detail).Infoln("Unregistering peer", id)
+ if err := d.peers.Unregister(id); err != nil {
+ glog.V(logger.Error).Infoln("Unregister failed:", err)
+ return err
+ }
+ return nil
}
// Synchronise will select the peer and use it for synchronising. If an empty string is given
@@ -138,17 +129,18 @@ func (d *Downloader) Synchronise(id string, hash common.Hash) error {
// Abort if the queue still contains some leftover data
if _, cached := d.queue.Size(); cached > 0 && d.queue.GetHeadBlock() != nil {
- return errPendingQueue
+ return ErrPendingQueue
}
- // Reset the queue to clean any internal leftover state
+ // Reset the queue and peer set to clean any internal leftover state
d.queue.Reset()
+ d.peers.Reset()
// Retrieve the origin peer and initiate the downloading process
- p := d.peers[id]
+ p := d.peers.Peer(id)
if p == nil {
return errUnknownPeer
}
- return d.getFromPeer(p, hash, false)
+ return d.syncWithPeer(p, hash)
}
// TakeBlocks takes blocks from the queue and yields them to the blockTaker handler
@@ -167,7 +159,9 @@ func (d *Downloader) Has(hash common.Hash) bool {
return d.queue.Has(hash)
}
-func (d *Downloader) getFromPeer(p *peer, hash common.Hash, ignoreInitial bool) (err error) {
+// syncWithPeer starts a block synchronization based on the hash chain from the
+// specified peer and head hash.
+func (d *Downloader) syncWithPeer(p *peer, hash common.Hash) (err error) {
d.activePeer = p.id
defer func() {
// reset on error
@@ -177,21 +171,12 @@ func (d *Downloader) getFromPeer(p *peer, hash common.Hash, ignoreInitial bool)
}()
glog.V(logger.Debug).Infoln("Synchronizing with the network using:", p.id)
- // Start the fetcher. This will block the update entirely
- // interupts need to be send to the appropriate channels
- // respectively.
- if err = d.startFetchingHashes(p, hash, ignoreInitial); err != nil {
+ if err = d.fetchHashes(p, hash); err != nil {
return err
}
-
- // Start fetching blocks in paralel. The strategy is simple
- // take any available peers, seserve a chunk for each peer available,
- // let the peer deliver the chunkn and periodically check if a peer
- // has timedout.
- if err = d.startFetchingBlocks(p); err != nil {
+ if err = d.fetchBlocks(); err != nil {
return err
}
-
glog.V(logger.Debug).Infoln("Synchronization completed")
return nil
@@ -234,17 +219,14 @@ blockDone:
}
// XXX Make synchronous
-func (d *Downloader) startFetchingHashes(p *peer, h common.Hash, ignoreInitial bool) error {
+func (d *Downloader) fetchHashes(p *peer, h common.Hash) error {
glog.V(logger.Debug).Infof("Downloading hashes (%x) from %s", h[:4], p.id)
start := time.Now()
- // We ignore the initial hash in some cases (e.g. we received a block without it's parent)
- // In such circumstances we don't need to download the block so don't add it to the queue.
- if !ignoreInitial {
- // Add the hash to the queue first
- d.queue.Insert([]common.Hash{h})
- }
+ // Add the hash to the queue first
+ d.queue.Insert([]common.Hash{h})
+
// Get the first batch of hashes
p.getHashes(h)
@@ -308,20 +290,18 @@ out:
// Attempt to find a new peer by checking inclusion of peers best hash in our
// already fetched hash list. This can't guarantee 100% correctness but does
// a fair job. This is always either correct or false incorrect.
- for id, peer := range d.peers {
- if d.queue.Has(peer.recentHash) && !attemptedPeers[id] {
+ for _, peer := range d.peers.AllPeers() {
+ if d.queue.Has(peer.head) && !attemptedPeers[p.id] {
p = peer
break
}
}
-
// if all peers have been tried, abort the process entirely or if the hash is
// the zero hash.
if p == nil || (hash == common.Hash{}) {
d.queue.Reset()
return ErrTimeout
}
-
// set p to the active peer. this will invalidate any hashes that may be returned
// by our previous (delayed) peer.
activePeer = p
@@ -334,14 +314,11 @@ out:
return nil
}
-func (d *Downloader) startFetchingBlocks(p *peer) error {
+// fetchBlocks iteratively downloads the entire schedules block-chain, taking
+// any available peers, reserving a chunk of blocks for each, wait for delivery
+// and periodically checking for timeouts.
+func (d *Downloader) fetchBlocks() error {
glog.V(logger.Debug).Infoln("Downloading", d.queue.Pending(), "block(s)")
-
- // Defer the peer reset. This will empty the peer requested set
- // and makes sure there are no lingering peers with an incorrect
- // state
- defer d.peers.reset()
-
start := time.Now()
// default ticker for re-fetching blocks every now and then
@@ -354,19 +331,19 @@ out:
case blockPack := <-d.blockCh:
// If the peer was previously banned and failed to deliver it's pack
// in a reasonable time frame, ignore it's message.
- if d.peers[blockPack.peerId] != nil {
- err := d.queue.Deliver(blockPack.peerId, blockPack.blocks)
- if err != nil {
- glog.V(logger.Debug).Infof("deliver failed for peer %s: %v\n", blockPack.peerId, err)
- // FIXME d.UnregisterPeer(blockPack.peerId)
+ if peer := d.peers.Peer(blockPack.peerId); peer != nil {
+ // Deliver the received chunk of blocks, but drop the peer if invalid
+ if err := d.queue.Deliver(blockPack.peerId, blockPack.blocks); err != nil {
+ glog.V(logger.Debug).Infof("Failed delivery for peer %s: %v\n", blockPack.peerId, err)
+ peer.Demote()
break
}
-
if glog.V(logger.Debug) {
- glog.Infof("adding %d blocks from: %s\n", len(blockPack.blocks), blockPack.peerId)
+ glog.Infof("Added %d blocks from: %s\n", len(blockPack.blocks), blockPack.peerId)
}
- d.peers[blockPack.peerId].promote()
- d.peers.setState(blockPack.peerId, idleState)
+ // Promote the peer and update it's idle state
+ peer.Promote()
+ peer.SetIdle()
}
case <-ticker.C:
// Check for bad peers. Bad peers may indicate a peer not responding
@@ -381,13 +358,12 @@ out:
// 1) Time for them to respond;
// 2) Measure their speed;
// 3) Amount and availability.
- if peer := d.peers[pid]; peer != nil {
- peer.demote()
- peer.reset()
+ if peer := d.peers.Peer(pid); peer != nil {
+ peer.Demote()
}
}
// After removing bad peers make sure we actually have sufficient peer left to keep downloading
- if len(d.peers) == 0 {
+ if d.peers.Len() == 0 {
d.queue.Reset()
return errNoPeers
}
@@ -398,31 +374,33 @@ out:
if d.queue.Throttle() {
continue
}
-
- availablePeers := d.peers.get(idleState)
- for _, peer := range availablePeers {
+ // Send a download request to all idle peers, until throttled
+ idlePeers := d.peers.IdlePeers()
+ for _, peer := range idlePeers {
+ // Short circuit if throttling activated since above
+ if d.queue.Throttle() {
+ break
+ }
// Get a possible chunk. If nil is returned no chunk
// could be returned due to no hashes available.
request := d.queue.Reserve(peer, maxBlockFetch)
if request == nil {
continue
}
- // XXX make fetch blocking.
// Fetch the chunk and check for error. If the peer was somehow
// already fetching a chunk due to a bug, it will be returned to
// the queue
- if err := peer.fetch(request); err != nil {
- // log for tracing
- glog.V(logger.Debug).Infof("peer %s received double work (state = %v)\n", peer.id, peer.state)
+ if err := peer.Fetch(request); err != nil {
+ glog.V(logger.Error).Infof("Peer %s received double work\n", peer.id)
d.queue.Cancel(request)
}
}
- // make sure that we have peers available for fetching. If all peers have been tried
+ // Make sure that we have peers available for fetching. If all peers have been tried
// and all failed throw an error
if d.queue.InFlight() == 0 {
d.queue.Reset()
- return fmt.Errorf("%v peers avaialable = %d. total peers = %d. hashes needed = %d", errPeersUnavailable, len(availablePeers), len(d.peers), d.queue.Pending())
+ return fmt.Errorf("%v peers available = %d. total peers = %d. hashes needed = %d", errPeersUnavailable, len(idlePeers), d.peers.Len(), d.queue.Pending())
}
} else if d.queue.InFlight() == 0 {
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index d0f8d4c8f..385ad2909 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -229,7 +229,7 @@ func TestThrottling(t *testing.T) {
minDesiredPeerCount = 4
blockTtl = 1 * time.Second
- targetBlocks := 4 * blockCacheLimit
+ targetBlocks := 16 * blockCacheLimit
hashes := createHashes(0, targetBlocks)
blocks := createBlocksFromHashes(hashes)
tester := newTester(t, hashes, blocks)
@@ -256,6 +256,7 @@ func TestThrottling(t *testing.T) {
return
default:
took = append(took, tester.downloader.TakeBlocks()...)
+ time.Sleep(time.Millisecond)
}
}
}()
diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go
index 45ec1cbfd..4abae8d5e 100644
--- a/eth/downloader/peer.go
+++ b/eth/downloader/peer.go
@@ -1,125 +1,197 @@
+// Contains the active peer-set of the downloader, maintaining both failures
+// as well as reputation metrics to prioritize the block retrievals.
+
package downloader
import (
"errors"
"sync"
+ "sync/atomic"
"github.com/ethereum/go-ethereum/common"
"gopkg.in/fatih/set.v0"
)
-const (
- workingState = 2
- idleState = 4
-)
-
type hashFetcherFn func(common.Hash) error
type blockFetcherFn func([]common.Hash) error
-// XXX make threadsafe!!!!
-type peers map[string]*peer
+var (
+ errAlreadyFetching = errors.New("already fetching blocks from peer")
+ errAlreadyRegistered = errors.New("peer is already registered")
+ errNotRegistered = errors.New("peer is not registered")
+)
-func (p peers) reset() {
- for _, peer := range p {
- peer.reset()
- }
+// peer represents an active peer from which hashes and blocks are retrieved.
+type peer struct {
+ id string // Unique identifier of the peer
+ head common.Hash // Hash of the peers latest known block
+
+ idle int32 // Current activity state of the peer (idle = 0, active = 1)
+ rep int32 // Simple peer reputation (not used currently)
+
+ mu sync.RWMutex
+
+ ignored *set.Set
+
+ getHashes hashFetcherFn
+ getBlocks blockFetcherFn
}
-func (p peers) get(state int) []*peer {
- var peers []*peer
- for _, peer := range p {
- peer.mu.RLock()
- if peer.state == state {
- peers = append(peers, peer)
- }
- peer.mu.RUnlock()
+// newPeer create a new downloader peer, with specific hash and block retrieval
+// mechanisms.
+func newPeer(id string, head common.Hash, getHashes hashFetcherFn, getBlocks blockFetcherFn) *peer {
+ return &peer{
+ id: id,
+ head: head,
+ getHashes: getHashes,
+ getBlocks: getBlocks,
+ ignored: set.New(),
}
+}
- return peers
+// Reset clears the internal state of a peer entity.
+func (p *peer) Reset() {
+ atomic.StoreInt32(&p.idle, 0)
+ p.ignored.Clear()
}
-func (p peers) setState(id string, state int) {
- if peer, exist := p[id]; exist {
- peer.mu.Lock()
- defer peer.mu.Unlock()
- peer.state = state
+// Fetch sends a block retrieval request to the remote peer.
+func (p *peer) Fetch(request *fetchRequest) error {
+ // Short circuit if the peer is already fetching
+ if !atomic.CompareAndSwapInt32(&p.idle, 0, 1) {
+ return errAlreadyFetching
}
+ // Convert the hash set to a retrievable slice
+ hashes := make([]common.Hash, 0, len(request.Hashes))
+ for hash, _ := range request.Hashes {
+ hashes = append(hashes, hash)
+ }
+ p.getBlocks(hashes)
+
+ return nil
}
-func (p peers) getPeer(id string) *peer {
- return p[id]
+// SetIdle sets the peer to idle, allowing it to execute new retrieval requests.
+func (p *peer) SetIdle() {
+ atomic.StoreInt32(&p.idle, 0)
}
-// peer represents an active peer
-type peer struct {
- state int // Peer state (working, idle)
- rep int // TODO peer reputation
+// Promote increases the peer's reputation.
+func (p *peer) Promote() {
+ atomic.AddInt32(&p.rep, 1)
+}
- mu sync.RWMutex
- id string
- recentHash common.Hash
+// Demote decreases the peer's reputation or leaves it at 0.
+func (p *peer) Demote() {
+ for {
+ // Calculate the new reputation value
+ prev := atomic.LoadInt32(&p.rep)
+ next := prev / 2
- ignored *set.Set
+ // Try to update the old value
+ if atomic.CompareAndSwapInt32(&p.rep, prev, next) {
+ return
+ }
+ }
+}
- getHashes hashFetcherFn
- getBlocks blockFetcherFn
+// peerSet represents the collection of active peer participating in the block
+// download procedure.
+type peerSet struct {
+ peers map[string]*peer
+ lock sync.RWMutex
}
-// create a new peer
-func newPeer(id string, hash common.Hash, getHashes hashFetcherFn, getBlocks blockFetcherFn) *peer {
- return &peer{
- id: id,
- recentHash: hash,
- getHashes: getHashes,
- getBlocks: getBlocks,
- state: idleState,
- ignored: set.New(),
+// newPeerSet creates a new peer set top track the active download sources.
+func newPeerSet() *peerSet {
+ return &peerSet{
+ peers: make(map[string]*peer),
}
}
-// fetch a chunk using the peer
-func (p *peer) fetch(request *fetchRequest) error {
- p.mu.Lock()
- defer p.mu.Unlock()
+// Reset iterates over the current peer set, and resets each of the known peers
+// to prepare for a next batch of block retrieval.
+func (ps *peerSet) Reset() {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
- if p.state == workingState {
- return errors.New("peer already fetching chunk")
+ for _, peer := range ps.peers {
+ peer.Reset()
}
+}
- // set working state
- p.state = workingState
+// Register injects a new peer into the working set, or returns an error if the
+// peer is already known.
+func (ps *peerSet) Register(p *peer) error {
+ ps.lock.Lock()
+ defer ps.lock.Unlock()
- // Convert the hash set to a fetchable slice
- hashes := make([]common.Hash, 0, len(request.Hashes))
- for hash, _ := range request.Hashes {
- hashes = append(hashes, hash)
+ if _, ok := ps.peers[p.id]; ok {
+ return errAlreadyRegistered
}
- p.getBlocks(hashes)
+ ps.peers[p.id] = p
+ return nil
+}
+
+// Unregister removes a remote peer from the active set, disabling any further
+// actions to/from that particular entity.
+func (ps *peerSet) Unregister(id string) error {
+ ps.lock.Lock()
+ defer ps.lock.Unlock()
+ if _, ok := ps.peers[id]; !ok {
+ return errNotRegistered
+ }
+ delete(ps.peers, id)
return nil
}
-// promote increases the peer's reputation
-func (p *peer) promote() {
- p.mu.Lock()
- defer p.mu.Unlock()
+// Peer retrieves the registered peer with the given id.
+func (ps *peerSet) Peer(id string) *peer {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
+
+ return ps.peers[id]
+}
+
+// Len returns if the current number of peers in the set.
+func (ps *peerSet) Len() int {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
- p.rep++
+ return len(ps.peers)
}
-// demote decreases the peer's reputation or leaves it at 0
-func (p *peer) demote() {
- p.mu.Lock()
- defer p.mu.Unlock()
+// AllPeers retrieves a flat list of all the peers within the set.
+func (ps *peerSet) AllPeers() []*peer {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
- if p.rep > 1 {
- p.rep -= 2
- } else {
- p.rep = 0
+ list := make([]*peer, 0, len(ps.peers))
+ for _, p := range ps.peers {
+ list = append(list, p)
}
+ return list
}
-func (p *peer) reset() {
- p.state = idleState
- p.ignored.Clear()
+// IdlePeers retrieves a flat list of all the currently idle peers within the
+// active peer set, ordered by their reputation.
+func (ps *peerSet) IdlePeers() []*peer {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
+
+ list := make([]*peer, 0, len(ps.peers))
+ for _, p := range ps.peers {
+ if atomic.LoadInt32(&p.idle) == 0 {
+ list = append(list, p)
+ }
+ }
+ for i := 0; i < len(list); i++ {
+ for j := i + 1; j < len(list); j++ {
+ if atomic.LoadInt32(&list[i].rep) < atomic.LoadInt32(&list[j].rep) {
+ list[i], list[j] = list[j], list[i]
+ }
+ }
+ }
+ return list
}
diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go
index 515440bca..40749698c 100644
--- a/eth/downloader/queue.go
+++ b/eth/downloader/queue.go
@@ -1,3 +1,6 @@
+// Contains the block download scheduler to collect download tasks and schedule
+// them in an ordered, and throttled way.
+
package downloader
import (
@@ -8,6 +11,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
)
@@ -126,6 +131,10 @@ func (q *queue) Insert(hashes []common.Hash) {
for i, hash := range hashes {
index := q.hashCounter + i
+ if old, ok := q.hashPool[hash]; ok {
+ glog.V(logger.Warn).Infof("Hash %x already scheduled at index %v", hash, old)
+ continue
+ }
q.hashPool[hash] = index
q.hashQueue.Push(hash, float32(index)) // Highest gets schedules first
}
diff --git a/eth/handler.go b/eth/handler.go
index 41b6728d9..88394543e 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -381,7 +381,7 @@ func (pm *ProtocolManager) BroadcastTx(hash common.Hash, tx *types.Transaction)
}
}
// Broadcast block to peer set
- peers = peers[:int(math.Sqrt(float64(len(peers))))]
+ //FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))]
for _, peer := range peers {
peer.sendTransaction(tx)
}
diff --git a/eth/sync.go b/eth/sync.go
index d955eaa50..00b571782 100644
--- a/eth/sync.go
+++ b/eth/sync.go
@@ -98,7 +98,8 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
case downloader.ErrTimeout:
glog.V(logger.Debug).Infof("Removing peer %v due to sync timeout", peer.id)
pm.removePeer(peer)
-
+ case downloader.ErrPendingQueue:
+ glog.V(logger.Debug).Infoln("Synchronisation aborted:", err)
default:
glog.V(logger.Warn).Infof("Synchronisation failed: %v", err)
}
diff --git a/ethdb/database.go b/ethdb/database.go
index 15af02fdf..c351c024a 100644
--- a/ethdb/database.go
+++ b/ethdb/database.go
@@ -11,7 +11,7 @@ import (
"github.com/syndtr/goleveldb/leveldb/opt"
)
-const openFileLimit = 128
+var OpenFileLimit = 64
type LDBDatabase struct {
fn string
@@ -26,7 +26,7 @@ type LDBDatabase struct {
func NewLDBDatabase(file string) (*LDBDatabase, error) {
// Open the db
- db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: openFileLimit})
+ db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: OpenFileLimit})
if err != nil {
return nil, err
}
diff --git a/ethdb/database_test.go b/ethdb/database_test.go
index 3cead8bcd..a80976a9a 100644
--- a/ethdb/database_test.go
+++ b/ethdb/database_test.go
@@ -2,13 +2,13 @@ package ethdb
import (
"os"
- "path"
+ "path/filepath"
"github.com/ethereum/go-ethereum/common"
)
func newDb() *LDBDatabase {
- file := path.Join("/", "tmp", "ldbtesttmpfile")
+ file := filepath.Join("/", "tmp", "ldbtesttmpfile")
if common.FileExist(file) {
os.RemoveAll(file)
}
diff --git a/generators/defaults.go b/generators/defaults.go
index 41d46729c..d30a56434 100644
--- a/generators/defaults.go
+++ b/generators/defaults.go
@@ -8,7 +8,7 @@ import (
"io/ioutil"
"os"
"os/exec"
- "path"
+ "path/filepath"
"strings"
)
@@ -35,7 +35,7 @@ func main() {
m := make(map[string]setting)
json.Unmarshal(content, &m)
- filepath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "params", os.Args[2])
+ filepath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "params", os.Args[2])
output, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE, os.ModePerm /*0777*/)
if err != nil {
fatal("error opening file for writing %v\n", err)
diff --git a/jsre/ethereum_js.go b/jsre/ethereum_js.go
index d236052e3..a61ffcbbf 100644
--- a/jsre/ethereum_js.go
+++ b/jsre/ethereum_js.go
@@ -1,5 +1,3693 @@
package jsre
const Ethereum_JS = `
-require=function t(e,r,n){function o(a,s){if(!r[a]){if(!e[a]){var u="function"==typeof require&&require;if(!s&&u)return u(a,!0);if(i)return i(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var l=r[a]={exports:{}};e[a][0].call(l.exports,function(t){var r=e[a][1][t];return o(r?r:t)},l,l.exports,t,e,r,n)}return r[a].exports}for(var i="function"==typeof require&&require,a=0;a<n.length;a++)o(n[a]);return o}({1:[function(t,e,r){var n=t("../utils/utils"),o=t("./coder"),i=t("./utils"),a=function(t,e){var r=t.map(function(t){return t.type});return o.encodeParams(r,e)},s=function(t,e){var r=t.map(function(t){return t.type});return o.decodeParams(r,e)},u=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),o=n.extractTypeName(t.name),i=function(){var e=Array.prototype.slice.call(arguments);return a(t.inputs,e)};void 0===e[r]&&(e[r]=i),e[r][o]=i}),e},c=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),o=n.extractTypeName(t.name),i=function(e){return s(t.outputs,e)};void 0===e[r]&&(e[r]=i),e[r][o]=i}),e},l=function(t,e){var r=i.getConstructor(t,e.length);return r?a(r.inputs,e):(e.length>0&&console.warn("didn't found matching constructor, using default one"),"")};e.exports={inputParser:u,outputParser:c,formatInput:a,formatOutput:s,formatConstructorParams:l}},{"../utils/utils":8,"./coder":2,"./utils":5}],2:[function(t,e,r){var n=t("bignumber.js"),o=t("../utils/utils"),i=t("./formatters"),a=t("./param"),s=function(t){return"[]"===t.slice(-2)},u=function(t){this._name=t.name,this._match=t.match,this._mode=t.mode,this._inputFormatter=t.inputFormatter,this._outputFormatter=t.outputFormatter};u.prototype.isType=function(t){return"strict"===this._match?this._name===t||0===t.indexOf(this._name)&&"[]"===t.slice(this._name.length):"prefix"===this._match?0===t.indexOf(this._name):void 0},u.prototype.formatInput=function(t,e){if(o.isArray(t)&&e){var r=this;return t.map(function(t){return r._inputFormatter(t)}).reduce(function(t,e){return t.appendArrayElement(e),t},new a(i.formatInputInt(t.length).value))}return this._inputFormatter(t)},u.prototype.formatOutput=function(t,e){if(e){for(var r=[],o=new n(t.value,16),i=0;64*o>i;i+=64)r.push(this._outputFormatter(new a(t.suffix.slice(i,i+64))));return r}return this._outputFormatter(t)},u.prototype.isVariadicType=function(t){return s(t)||"bytes"===this._mode},u.prototype.shiftParam=function(t,e){if("bytes"===this._mode)return e.shiftBytes();if(s(t)){var r=new n(e.value.slice(0,64),16);return e.shiftArray(r)}return e.shiftValue()};var c=function(t){this._types=t};c.prototype._requireType=function(t){var e=this._types.filter(function(e){return e.isType(t)})[0];if(!e)throw Error("invalid solidity type!: "+t);return e},c.prototype._bytesToParam=function(t,e){var r=e.slice(0,64*t.length),n=e.slice(64*t.length);return new a(r,n)},c.prototype._formatInput=function(t,e){return this._requireType(t).formatInput(e,s(t))},c.prototype.encodeParam=function(t,e){return this._formatInput(t,e).encode()},c.prototype.encodeParams=function(t,e){var r=this;return t.map(function(t,n){return r._formatInput(t,e[n])}).reduce(function(t,e){return t.append(e),t},new a).encode()},c.prototype._formatOutput=function(t,e){return this._requireType(t).formatOutput(e,s(t))},c.prototype.decodeParam=function(t,e){return this._formatOutput(t,this._bytesToParam([t],e))},c.prototype.decodeParams=function(t,e){var r=this,n=this._bytesToParam(t,e);return t.map(function(t){var e=r._requireType(t),o=e.shiftParam(t,n);return e.formatOutput(o,s(t))})};var l=new c([new u({name:"address",match:"strict",mode:"value",inputFormatter:i.formatInputInt,outputFormatter:i.formatOutputAddress}),new u({name:"bool",match:"strict",mode:"value",inputFormatter:i.formatInputBool,outputFormatter:i.formatOutputBool}),new u({name:"int",match:"prefix",mode:"value",inputFormatter:i.formatInputInt,outputFormatter:i.formatOutputInt}),new u({name:"uint",match:"prefix",mode:"value",inputFormatter:i.formatInputInt,outputFormatter:i.formatOutputUInt}),new u({name:"bytes",match:"strict",mode:"bytes",inputFormatter:i.formatInputDynamicBytes,outputFormatter:i.formatOutputDynamicBytes}),new u({name:"bytes",match:"prefix",mode:"value",inputFormatter:i.formatInputBytes,outputFormatter:i.formatOutputBytes}),new u({name:"real",match:"prefix",mode:"value",inputFormatter:i.formatInputReal,outputFormatter:i.formatOutputReal}),new u({name:"ureal",match:"prefix",mode:"value",inputFormatter:i.formatInputReal,outputFormatter:i.formatOutputUReal})]);e.exports=l},{"../utils/utils":8,"./formatters":3,"./param":4,"bignumber.js":"bignumber.js"}],3:[function(t,e,r){var n=t("bignumber.js"),o=t("../utils/utils"),i=t("../utils/config"),a=t("./param"),s=function(t){var e=2*i.ETH_PADDING;n.config(i.ETH_BIGNUMBER_ROUNDING_MODE);var r=o.padLeft(o.toTwosComplement(t).round().toString(16),e);return new a(r)},u=function(t){var e=o.fromAscii(t,i.ETH_PADDING).substr(2);return new a(e)},c=function(t){var e=o.fromAscii(t,i.ETH_PADDING).substr(2);return new a(s(t.length).value,e)},l=function(t){var e="000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0");return new a(e)},p=function(t){return s(new n(t).times(new n(2).pow(128)))},f=function(t){return"1"===new n(t.substr(0,1),16).toString(2).substr(0,1)},m=function(t){var e=t.value||"0";return f(e)?new n(e,16).minus(new n("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new n(e,16)},h=function(t){var e=t.value||"0";return new n(e,16)},d=function(t){return m(t).dividedBy(new n(2).pow(128))},y=function(t){return h(t).dividedBy(new n(2).pow(128))},g=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t.value?!0:!1},v=function(t){return o.toAscii(t.value)},b=function(t){return o.toAscii(t.suffix)},w=function(t){var e=t.value;return"0x"+e.slice(e.length-40,e.length)};e.exports={formatInputInt:s,formatInputBytes:u,formatInputDynamicBytes:c,formatInputBool:l,formatInputReal:p,formatOutputInt:m,formatOutputUInt:h,formatOutputReal:d,formatOutputUReal:y,formatOutputBool:g,formatOutputBytes:v,formatOutputDynamicBytes:b,formatOutputAddress:w}},{"../utils/config":7,"../utils/utils":8,"./param":4,"bignumber.js":"bignumber.js"}],4:[function(t,e,r){var n=function(t,e){this.value=t||"",this.suffix=e||""};n.prototype.append=function(t){this.value+=t.value,this.suffix+=t.suffix},n.prototype.appendArrayElement=function(t){this.suffix+=t.value},n.prototype.encode=function(){return this.value+this.suffix},n.prototype.shiftValue=function(){var t=this.value.slice(0,64);return this.value=this.value.slice(64),new n(t)},n.prototype.shiftBytes=function(){return this.shiftArray(1)},n.prototype.shiftArray=function(t){var e=this.value.slice(0,64);this.value=this.value.slice(64);var r=this.suffix.slice(0,64*t);return this.suffix=this.suffix.slice(64*t),new n(e,r)},e.exports=n},{}],5:[function(t,e,r){var n=function(t,e){return t.filter(function(t){return"constructor"===t.type&&t.inputs.length===e})[0]};e.exports={getConstructor:n}},{}],6:[function(t,e,r){"use strict";r.XMLHttpRequest="undefined"==typeof XMLHttpRequest?{}:XMLHttpRequest},{}],7:[function(t,e,r){var n=t("bignumber.js"),o=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:o,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:n.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3,defaultBlock:"latest",defaultAccount:void 0}},{"bignumber.js":"bignumber.js"}],8:[function(t,e,r){var n=t("bignumber.js"),o={wei:"1",kwei:"1000",ada:"1000",mwei:"1000000",babbage:"1000000",gwei:"1000000000",shannon:"1000000000",szabo:"1000000000000",finney:"1000000000000000",ether:"1000000000000000000",kether:"1000000000000000000000",grand:"1000000000000000000000",einstein:"1000000000000000000000",mether:"1000000000000000000000000",gether:"1000000000000000000000000000",tether:"1000000000000000000000000000000"},i=function(t,e,r){return new Array(e-t.length+1).join(r?r:"0")+t},a=function(t){var e="",r=0,n=t.length;for("0x"===t.substring(0,2)&&(r=2);n>r;r+=2){var o=parseInt(t.substr(r,2),16);if(0===o)break;e+=String.fromCharCode(o)}return e},s=function(t){for(var e="",r=0;r<t.length;r++){var n=t.charCodeAt(r).toString(16);e+=n.length<2?"0"+n:n}return e},u=function(t,e){e=void 0===e?0:e;for(var r=s(t);r.length<2*e;)r+="00";return"0x"+r},c=function(t){if(-1!==t.name.indexOf("("))return t.name;var e=t.inputs.map(function(t){return t.type}).join();return t.name+"("+e+")"},l=function(t){var e=t.indexOf("(");return-1!==e?t.substr(0,e):t},p=function(t){var e=t.indexOf("(");return-1!==e?t.substr(e+1,t.length-1-(e+1)).replace(" ",""):""},f=function(t){return v(t).toNumber()},m=function(t){var e=v(t),r=e.toString(16);return e.lessThan(0)?"-0x"+r.substr(1):"0x"+r},h=function(t){if(T(t))return m(+t);if(F(t))return m(t);if(B(t))return u(JSON.stringify(t));if(I(t)){if(0===t.indexOf("-0x"))return m(t);if(!isFinite(t))return u(t)}return m(t)},d=function(t){t=t?t.toLowerCase():"ether";var e=o[t];if(void 0===e)throw new Error("This unit doesn't exists, please use the one of the following units"+JSON.stringify(o,null,2));return new n(e,10)},y=function(t,e){var r=v(t).dividedBy(d(e));return F(t)?r:r.toString(10)},g=function(t,e){var r=v(t).times(d(e));return F(t)?r:r.toString(10)},v=function(t){return t=t||0,F(t)?t:!I(t)||0!==t.indexOf("0x")&&0!==t.indexOf("-0x")?new n(t.toString(10),10):new n(t.replace("0x",""),16)},b=function(t){var e=v(t);return e.lessThan(0)?new n("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16).plus(e).plus(1):e},w=function(t){return/^0x[0-9a-f]{40}$/.test(t)},_=function(t){return/^(0x)?[0-9a-f]{40}$/.test(t)},x=function(t){return w(t)?t:/^[0-9a-f]{40}$/.test(t)?"0x"+t:"0x"+i(h(t).substr(2),40)},F=function(t){return t instanceof n||t&&t.constructor&&"BigNumber"===t.constructor.name},I=function(t){return"string"==typeof t||t&&t.constructor&&"String"===t.constructor.name},N=function(t){return"function"==typeof t},B=function(t){return"object"==typeof t},T=function(t){return"boolean"==typeof t},O=function(t){return t instanceof Array},P=function(t){try{return!!JSON.parse(t)}catch(e){return!1}};e.exports={padLeft:i,toHex:h,toDecimal:f,fromDecimal:m,toAscii:a,fromAscii:u,transformToFullName:c,extractDisplayName:l,extractTypeName:p,toWei:g,fromWei:y,toBigNumber:v,toTwosComplement:b,toAddress:x,isBigNumber:F,isStrictAddress:w,isAddress:_,isFunction:N,isString:I,isObject:B,isBoolean:T,isArray:O,isJson:P}},{"bignumber.js":"bignumber.js"}],9:[function(t,e,r){e.exports={version:"0.3.5"}},{}],10:[function(t,e,r){var n=t("./version.json"),o=t("./web3/net"),i=t("./web3/eth"),a=t("./web3/db"),s=t("./web3/shh"),u=t("./web3/watches"),c=t("./web3/filter"),l=t("./utils/utils"),p=t("./web3/formatters"),f=t("./web3/requestmanager"),m=t("./utils/config"),h=t("./web3/method"),d=t("./web3/property"),y=[new h({name:"sha3",call:"web3_sha3",params:1})],g=[new d({name:"version.client",getter:"web3_clientVersion"}),new d({name:"version.network",getter:"net_version",inputFormatter:l.toDecimal}),new d({name:"version.ethereum",getter:"eth_protocolVersion",inputFormatter:l.toDecimal}),new d({name:"version.whisper",getter:"shh_version",inputFormatter:l.toDecimal})],v=function(t,e){e.forEach(function(e){e.attachToObject(t)})},b=function(t,e){e.forEach(function(e){e.attachToObject(t)})},w={};w.providers={},w.version={},w.version.api=n.version,w.eth={},w.eth.filter=function(t,e,r,n){return t._isEvent?t(e,r):new c(t,u.eth(),n||p.outputLogFormatter)},w.shh={},w.shh.filter=function(t){return new c(t,u.shh(),p.outputPostFormatter)},w.net={},w.db={},w.setProvider=function(t){f.getInstance().setProvider(t)},w.reset=function(){f.getInstance().reset(),m.defaultBlock="latest",m.defaultAccount=void 0},w.toHex=l.toHex,w.toAscii=l.toAscii,w.fromAscii=l.fromAscii,w.toDecimal=l.toDecimal,w.fromDecimal=l.fromDecimal,w.toBigNumber=l.toBigNumber,w.toWei=l.toWei,w.fromWei=l.fromWei,w.isAddress=l.isAddress,Object.defineProperty(w.eth,"defaultBlock",{get:function(){return m.defaultBlock},set:function(t){return m.defaultBlock=t,t}}),Object.defineProperty(w.eth,"defaultAccount",{get:function(){return m.defaultAccount},set:function(t){return m.defaultAccount=t,t}}),v(w,y),b(w,g),v(w.net,o.methods),b(w.net,o.properties),v(w.eth,i.methods),b(w.eth,i.properties),v(w.db,a.methods),v(w.shh,s.methods),e.exports=w},{"./utils/config":7,"./utils/utils":8,"./version.json":9,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/method":21,"./web3/net":22,"./web3/property":23,"./web3/requestmanager":25,"./web3/shh":26,"./web3/watches":27}],11:[function(t,e,r){var n=t("../web3"),o=t("../solidity/abi"),i=t("../utils/utils"),a=t("./event"),s=t("./function"),u=function(t,e){e.filter(function(t){return"function"===t.type}).map(function(e){return new s(e,t.address)}).forEach(function(e){e.attachToContract(t)})},c=function(t,e){e.filter(function(t){return"event"===t.type}).map(function(e){return new a(e,t.address)}).forEach(function(e){e.attachToContract(t)})},l=function(t){return p.bind(null,t)},p=function(t,e){if(this.address="",i.isAddress(e))this.address=e;else{e=e||{};var r=Array.prototype.slice.call(arguments,2),a=o.formatConstructorParams(t,r);e.data+=a,this.address=n.eth.sendTransaction(e)}u(this,t),c(this,t)};p.prototype.call=function(){return console.error("contract.call is deprecated"),this},p.prototype.sendTransaction=function(){return console.error("contract.sendTransact is deprecated"),this},e.exports=l},{"../solidity/abi":1,"../utils/utils":8,"../web3":10,"./event":15,"./function":18}],12:[function(t,e,r){var n=t("./method"),o=new n({name:"putString",call:"db_putString",params:3}),i=new n({name:"getString",call:"db_getString",params:2}),a=new n({name:"putHex",call:"db_putHex",params:3}),s=new n({name:"getHex",call:"db_getHex",params:2}),u=[o,i,a,s];e.exports={methods:u}},{"./method":21}],13:[function(t,e,r){e.exports={InvalidNumberOfParams:function(){return new Error("Invalid number of input parameters")},InvalidConnection:function(t){return new Error("CONNECTION ERROR: Couldn't connect to node "+t+", is it running?")},InvalidProvider:function(){return new Error("Providor not set or invalid")},InvalidResponse:function(t){var e=t&&t.error&&t.error.message?t.error.message:"Invalid JSON RPC response";return new Error(e)}}},{}],14:[function(t,e,r){"use strict";var n=t("./formatters"),o=t("../utils/utils"),i=t("./method"),a=t("./property"),s=function(t){return o.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getBlockByHash":"eth_getBlockByNumber"},u=function(t){return o.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getTransactionByBlockHashAndIndex":"eth_getTransactionByBlockNumberAndIndex"},c=function(t){return o.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getUncleByBlockHashAndIndex":"eth_getUncleByBlockNumberAndIndex"},l=function(t){return o.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getBlockTransactionCountByHash":"eth_getBlockTransactionCountByNumber"},p=function(t){return o.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getUncleCountByBlockHash":"eth_getUncleCountByBlockNumber"},f=new i({name:"getBalance",call:"eth_getBalance",params:2,inputFormatter:[o.toAddress,n.inputDefaultBlockNumberFormatter],outputFormatter:n.outputBigNumberFormatter}),m=new i({name:"getStorageAt",call:"eth_getStorageAt",params:3,inputFormatter:[null,o.toHex,n.inputDefaultBlockNumberFormatter]}),h=new i({name:"getCode",call:"eth_getCode",params:2,inputFormatter:[o.toAddress,n.inputDefaultBlockNumberFormatter]}),d=new i({name:"getBlock",call:s,params:2,inputFormatter:[n.inputBlockNumberFormatter,function(t){return!!t}],outputFormatter:n.outputBlockFormatter}),y=new i({name:"getUncle",call:c,params:2,inputFormatter:[n.inputBlockNumberFormatter,o.toHex],outputFormatter:n.outputBlockFormatter}),g=new i({name:"getCompilers",call:"eth_getCompilers",params:0}),v=new i({name:"getBlockTransactionCount",call:l,params:1,inputFormatter:[n.inputBlockNumberFormatter],outputFormatter:o.toDecimal}),b=new i({name:"getBlockUncleCount",call:p,params:1,inputFormatter:[n.inputBlockNumberFormatter],outputFormatter:o.toDecimal}),w=new i({name:"getTransaction",call:"eth_getTransactionByHash",params:1,outputFormatter:n.outputTransactionFormatter}),_=new i({name:"getTransactionFromBlock",call:u,params:2,inputFormatter:[n.inputBlockNumberFormatter,o.toHex],outputFormatter:n.outputTransactionFormatter}),x=new i({name:"getTransactionCount",call:"eth_getTransactionCount",params:2,inputFormatter:[null,n.inputDefaultBlockNumberFormatter],outputFormatter:o.toDecimal}),F=new i({name:"sendTransaction",call:"eth_sendTransaction",params:1,inputFormatter:[n.inputTransactionFormatter]}),I=new i({name:"call",call:"eth_call",params:2,inputFormatter:[n.inputTransactionFormatter,n.inputDefaultBlockNumberFormatter]}),N=new i({name:"compile.solidity",call:"eth_compileSolidity",params:1}),B=new i({name:"compile.lll",call:"eth_compileLLL",params:1}),T=new i({name:"compile.serpent",call:"eth_compileSerpent",params:1}),O=[f,m,h,d,y,g,v,b,w,_,x,I,F,N,B,T],P=[new a({name:"coinbase",getter:"eth_coinbase"}),new a({name:"mining",getter:"eth_mining"}),new a({name:"hashrate",getter:"eth_hashrate",outputFormatter:o.toDecimal}),new a({name:"gasPrice",getter:"eth_gasPrice",outputFormatter:n.outputBigNumberFormatter}),new a({name:"accounts",getter:"eth_accounts"}),new a({name:"blockNumber",getter:"eth_blockNumber",outputFormatter:o.toDecimal})];e.exports={methods:O,properties:P}},{"../utils/utils":8,"./formatters":17,"./method":21,"./property":23}],15:[function(t,e,r){var n=t("../utils/utils"),o=t("../solidity/coder"),i=t("../web3"),a=t("./formatters"),s=function(t,e){this._params=t.inputs,this._name=n.transformToFullName(t),this._address=e,this._anonymous=t.anonymous};s.prototype.types=function(t){return this._params.filter(function(e){return e.indexed===t}).map(function(t){return t.type})},s.prototype.displayName=function(){return n.extractDisplayName(this._name)},s.prototype.typeName=function(){return n.extractTypeName(this._name)},s.prototype.signature=function(){return i.sha3(i.fromAscii(this._name)).slice(2)},s.prototype.encode=function(t,e){t=t||{},e=e||{};var r={};["fromBlock","toBlock"].filter(function(t){return void 0!==e[t]}).forEach(function(t){r[t]=a.inputBlockNumberFormatter(e[t])}),r.topics=[],this._anonymous||(r.address=this._address,r.topics.push("0x"+this.signature()));var i=this._params.filter(function(t){return t.indexed===!0}).map(function(e){var r=t[e.name];return void 0===r||null===r?null:n.isArray(r)?r.map(function(t){return"0x"+o.encodeParam(e.type,t)}):"0x"+o.encodeParam(e.type,r)});return r.topics=r.topics.concat(i),r},s.prototype.decode=function(t){t.data=t.data||"",t.topics=t.topics||[];var e=this._anonymous?t.topics:t.topics.slice(1),r=e.map(function(t){return t.slice(2)}).join(""),n=o.decodeParams(this.types(!0),r),i=t.data.slice(2),s=o.decodeParams(this.types(!1),i),u=a.outputLogFormatter(t);return u.event=this.displayName(),u.address=t.address,u.args=this._params.reduce(function(t,e){return t[e.name]=e.indexed?n.shift():s.shift(),t},{}),delete u.data,delete u.topics,u},s.prototype.execute=function(t,e){var r=this.encode(t,e),n=this.decode.bind(this);return i.eth.filter(r,void 0,void 0,n)},s.prototype.attachToContract=function(t){var e=this.execute.bind(this),r=this.displayName();t[r]||(t[r]=e),t[r][this.typeName()]=this.execute.bind(this,t)},e.exports=s},{"../solidity/coder":2,"../utils/utils":8,"../web3":10,"./formatters":17}],16:[function(t,e,r){var n=t("./requestmanager"),o=t("./formatters"),i=t("../utils/utils"),a=function(t){return null===t||"undefined"==typeof t?null:(t=String(t),0===t.indexOf("0x")?t:i.fromAscii(t))},s=function(t){return i.isString(t)?t:(t=t||{},t.topics=t.topics||[],t.topics=t.topics.map(function(t){return i.isArray(t)?t.map(a):a(t)}),{topics:t.topics,to:t.to,address:t.address,fromBlock:o.inputBlockNumberFormatter(t.fromBlock),toBlock:o.inputBlockNumberFormatter(t.toBlock)})},u=function(t,e,r){var n={};e.forEach(function(t){t.attachToObject(n)}),this.options=s(t),this.implementation=n,this.callbacks=[],this.formatter=r,this.filterId=this.implementation.newFilter(this.options)};u.prototype.watch=function(t){this.callbacks.push(t);var e=this,r=function(t,r){return t?e.callbacks.forEach(function(e){e(t)}):void r.forEach(function(t){t=e.formatter?e.formatter(t):t,e.callbacks.forEach(function(e){e(null,t)})})};i.isString(this.options)||this.get(function(e,r){e&&t(e),r.forEach(function(e){t(null,e)})}),n.getInstance().startPolling({method:this.implementation.poll.call,params:[this.filterId]},this.filterId,r,this.stopWatching.bind(this))},u.prototype.stopWatching=function(){n.getInstance().stopPolling(this.filterId),this.implementation.uninstallFilter(this.filterId),this.callbacks=[]},u.prototype.get=function(t){var e=this;if(!i.isFunction(t)){var r=this.implementation.getLogs(this.filterId);return r.map(function(t){return e.formatter?e.formatter(t):t})}this.implementation.getLogs(this.filterId,function(r,n){r?t(r):t(null,n.map(function(t){return e.formatter?e.formatter(t):t}))})},e.exports=u},{"../utils/utils":8,"./formatters":17,"./requestmanager":25}],17:[function(t,e,r){var n=t("../utils/utils"),o=t("../utils/config"),i=function(t){return n.toBigNumber(t)},a=function(t){return"latest"===t||"pending"===t||"earliest"===t},s=function(t){return void 0===t?o.defaultBlock:u(t)},u=function(t){return void 0===t?void 0:a(t)?t:n.toHex(t)},c=function(t){return t.from=t.from||o.defaultAccount,t.code&&(t.data=t.code,delete t.code),["gasPrice","gas","value"].filter(function(e){return void 0!==t[e]}).forEach(function(e){t[e]=n.fromDecimal(t[e])}),t},l=function(t){return t.blockNumber=n.toDecimal(t.blockNumber),t.transactionIndex=n.toDecimal(t.transactionIndex),t.nonce=n.toDecimal(t.nonce),t.gas=n.toDecimal(t.gas),t.gasPrice=n.toBigNumber(t.gasPrice),t.value=n.toBigNumber(t.value),t},p=function(t){return t.gasLimit=n.toDecimal(t.gasLimit),t.gasUsed=n.toDecimal(t.gasUsed),t.size=n.toDecimal(t.size),t.timestamp=n.toDecimal(t.timestamp),t.number=n.toDecimal(t.number),t.difficulty=n.toBigNumber(t.difficulty),t.totalDifficulty=n.toBigNumber(t.totalDifficulty),n.isArray(t.transactions)&&t.transactions.forEach(function(t){return n.isString(t)?void 0:l(t)}),t},f=function(t){return null===t?null:(t.blockNumber=n.toDecimal(t.blockNumber),t.transactionIndex=n.toDecimal(t.transactionIndex),t.logIndex=n.toDecimal(t.logIndex),t)},m=function(t){return t.payload=n.toHex(t.payload),t.ttl=n.fromDecimal(t.ttl),t.workToProve=n.fromDecimal(t.workToProve),t.priority=n.fromDecimal(t.priority),n.isArray(t.topics)||(t.topics=t.topics?[t.topics]:[]),t.topics=t.topics.map(function(t){return n.fromAscii(t)}),t},h=function(t){return t.expiry=n.toDecimal(t.expiry),t.sent=n.toDecimal(t.sent),t.ttl=n.toDecimal(t.ttl),t.workProved=n.toDecimal(t.workProved),t.payloadRaw=t.payload,t.payload=n.toAscii(t.payload),n.isJson(t.payload)&&(t.payload=JSON.parse(t.payload)),t.topics||(t.topics=[]),t.topics=t.topics.map(function(t){return n.toAscii(t)}),t};e.exports={inputDefaultBlockNumberFormatter:s,inputBlockNumberFormatter:u,inputTransactionFormatter:c,inputPostFormatter:m,outputBigNumberFormatter:i,outputTransactionFormatter:l,outputBlockFormatter:p,outputLogFormatter:f,outputPostFormatter:h}},{"../utils/config":7,"../utils/utils":8}],18:[function(t,e,r){var n=t("../web3"),o=t("../solidity/coder"),i=t("../utils/utils"),a=function(t,e){this._inputTypes=t.inputs.map(function(t){return t.type}),this._outputTypes=t.outputs.map(function(t){return t.type}),this._constant=t.constant,this._name=i.transformToFullName(t),this._address=e};a.prototype.toPayload=function(){var t=Array.prototype.slice.call(arguments),e={};return t.length>this._inputTypes.length&&i.isObject(t[t.length-1])&&(e=t.pop()),e.to=this._address,e.data="0x"+this.signature()+o.encodeParams(this._inputTypes,t),e},a.prototype.signature=function(){return n.sha3(n.fromAscii(this._name)).slice(2,10)},a.prototype.call=function(){var t=this.toPayload.apply(this,Array.prototype.slice.call(arguments)),e=n.eth.call(t);e=e.length>=2?e.slice(2):e;var r=o.decodeParams(this._outputTypes,e);return 1===r.length?r[0]:r},a.prototype.sendTransaction=function(){var t=this.toPayload.apply(this,Array.prototype.slice.call(arguments));n.eth.sendTransaction(t)},a.prototype.displayName=function(){return i.extractDisplayName(this._name)},a.prototype.typeName=function(){return i.extractTypeName(this._name)},a.prototype.execute=function(){var t=!this._constant;return t?this.sendTransaction.apply(this,Array.prototype.slice.call(arguments)):this.call.apply(this,Array.prototype.slice.call(arguments))},a.prototype.attachToContract=function(t){var e=this.execute.bind(this);e.call=this.call.bind(this),e.sendTransaction=this.sendTransaction.bind(this);var r=this.displayName();t[r]||(t[r]=e),t[r][this.typeName()]=e},e.exports=a},{"../solidity/coder":2,"../utils/utils":8,"../web3":10}],19:[function(t,e,r){"use strict";var n=t("xmlhttprequest").XMLHttpRequest,o=t("./errors"),i=function(t){this.host=t||"http://localhost:8545"};i.prototype.send=function(t){var e=new n;e.open("POST",this.host,!1);try{e.send(JSON.stringify(t))}catch(r){throw o.InvalidConnection(this.host)}return JSON.parse(e.responseText)},i.prototype.sendAsync=function(t,e){var r=new n;r.onreadystatechange=function(){4===r.readyState&&e(null,JSON.parse(r.responseText))},r.open("POST",this.host,!0);try{r.send(JSON.stringify(t))}catch(i){e(o.InvalidConnection(this.host))}},e.exports=i},{"./errors":13,xmlhttprequest:6}],20:[function(t,e,r){var n=function(){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,void(this.messageId=1))};n.getInstance=function(){var t=new n;return t},n.prototype.toPayload=function(t,e){return t||console.error("jsonrpc method should be specified!"),{jsonrpc:"2.0",method:t,params:e||[],id:this.messageId++}},n.prototype.isValidResponse=function(t){return!!t&&!t.error&&"2.0"===t.jsonrpc&&"number"==typeof t.id&&void 0!==t.result},n.prototype.toBatchPayload=function(t){var e=this;return t.map(function(t){return e.toPayload(t.method,t.params)})},e.exports=n},{}],21:[function(t,e,r){var n=t("./requestmanager"),o=t("../utils/utils"),i=t("./errors"),a=function(t){this.name=t.name,this.call=t.call,this.params=t.params||0,this.inputFormatter=t.inputFormatter,this.outputFormatter=t.outputFormatter};a.prototype.getCall=function(t){return o.isFunction(this.call)?this.call(t):this.call},a.prototype.extractCallback=function(t){return o.isFunction(t[t.length-1])?t.pop():null},a.prototype.validateArgs=function(t){if(t.length!==this.params)throw i.InvalidNumberOfParams()},a.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter.map(function(e,r){return e?e(t[r]):t[r]}):t},a.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},a.prototype.attachToObject=function(t){var e=this.send.bind(this);e.call=this.call;var r=this.name.split(".");r.length>1?(t[r[0]]=t[r[0]]||{},t[r[0]][r[1]]=e):t[r[0]]=e},a.prototype.toPayload=function(t){var e=this.getCall(t),r=this.extractCallback(t),n=this.formatInput(t);return this.validateArgs(n),{method:e,params:n,callback:r}},a.prototype.send=function(){var t=this.toPayload(Array.prototype.slice.call(arguments));if(t.callback){var e=this;return n.getInstance().sendAsync(t,function(r,n){t.callback(null,e.formatOutput(n))})}return this.formatOutput(n.getInstance().send(t))},e.exports=a},{"../utils/utils":8,"./errors":13,"./requestmanager":25}],22:[function(t,e,r){var n=t("../utils/utils"),o=t("./property"),i=[],a=[new o({name:"listening",getter:"net_listening"}),new o({name:"peerCount",getter:"net_peerCount",outputFormatter:n.toDecimal})];e.exports={methods:i,properties:a}},{"../utils/utils":8,"./property":23}],23:[function(t,e,r){var n=t("./requestmanager"),o=function(t){this.name=t.name,this.getter=t.getter,this.setter=t.setter,this.outputFormatter=t.outputFormatter,this.inputFormatter=t.inputFormatter};o.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter(t):t},o.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},o.prototype.attachToObject=function(t){var e={get:this.get.bind(this),set:this.set.bind(this)},r=this.name.split(".");r.length>1?(t[r[0]]=t[r[0]]||{},Object.defineProperty(t[r[0]],r[1],e)):Object.defineProperty(t,r[0],e)},o.prototype.get=function(){return this.formatOutput(n.getInstance().send({method:this.getter}))},o.prototype.set=function(t){return n.getInstance().send({method:this.setter,params:[this.formatInput(t)]})},e.exports=o},{"./requestmanager":25}],24:[function(t,e,r){var n=function(){};n.prototype.send=function(t){var e=navigator.qt.callMethod(JSON.stringify(t));return JSON.parse(e)},e.exports=n},{}],25:[function(t,e,r){var n=t("./jsonrpc"),o=t("../utils/utils"),i=t("../utils/config"),a=t("./errors"),s=function(t){return arguments.callee._singletonInstance?arguments.callee._singletonInstance:(arguments.callee._singletonInstance=this,this.provider=t,this.polls=[],this.timeout=null,void this.poll())};s.getInstance=function(){var t=new s;return t},s.prototype.send=function(t){if(!this.provider)return console.error(a.InvalidProvider()),null;var e=n.getInstance().toPayload(t.method,t.params),r=this.provider.send(e);if(!n.getInstance().isValidResponse(r))throw a.InvalidResponse(r);return r.result},s.prototype.sendAsync=function(t,e){if(!this.provider)return e(a.InvalidProvider());var r=n.getInstance().toPayload(t.method,t.params);this.provider.sendAsync(r,function(t,r){return t?e(t):n.getInstance().isValidResponse(r)?void e(null,r.result):e(a.InvalidResponse(r))})},s.prototype.setProvider=function(t){this.provider=t},s.prototype.startPolling=function(t,e,r,n){this.polls.push({data:t,id:e,callback:r,uninstall:n})},s.prototype.stopPolling=function(t){for(var e=this.polls.length;e--;){var r=this.polls[e];r.id===t&&this.polls.splice(e,1)}},s.prototype.reset=function(){this.polls.forEach(function(t){t.uninstall(t.id)}),this.polls=[],this.timeout&&(clearTimeout(this.timeout),this.timeout=null),this.poll()},s.prototype.poll=function(){if(this.timeout=setTimeout(this.poll.bind(this),i.ETH_POLLING_TIMEOUT),this.polls.length){if(!this.provider)return void console.error(a.InvalidProvider());var t=n.getInstance().toBatchPayload(this.polls.map(function(t){return t.data})),e=this;this.provider.sendAsync(t,function(t,r){if(!t){if(!o.isArray(r))throw a.InvalidResponse(r);r.map(function(t,r){return t.callback=e.polls[r].callback,t}).filter(function(t){var e=n.getInstance().isValidResponse(t);return e||t.callback(a.InvalidResponse(t)),e}).filter(function(t){return o.isArray(t.result)&&t.result.length>0}).forEach(function(t){t.callback(null,t.result)})}})}},e.exports=s},{"../utils/config":7,"../utils/utils":8,"./errors":13,"./jsonrpc":20}],26:[function(t,e,r){var n=t("./method"),o=t("./formatters"),i=new n({name:"post",call:"shh_post",params:1,inputFormatter:[o.inputPostFormatter]}),a=new n({name:"newIdentity",call:"shh_newIdentity",params:0}),s=new n({name:"hasIdentity",call:"shh_hasIdentity",params:1}),u=new n({name:"newGroup",call:"shh_newGroup",params:0}),c=new n({name:"addToGroup",call:"shh_addToGroup",params:0}),l=[i,a,s,u,c];e.exports={methods:l}},{"./formatters":17,"./method":21}],27:[function(t,e,r){var n=t("./method"),o=function(){var t=function(t){return"string"==typeof t[0]?"eth_newBlockFilter":"eth_newFilter"},e=new n({name:"newFilter",call:t,params:1}),r=new n({name:"uninstallFilter",call:"eth_uninstallFilter",params:1}),o=new n({name:"getLogs",call:"eth_getFilterLogs",params:1}),i=new n({name:"poll",call:"eth_getFilterChanges",params:1});return[e,r,o,i]},i=function(){var t=new n({name:"newFilter",call:"shh_newFilter",params:1}),e=new n({name:"uninstallFilter",
-call:"shh_uninstallFilter",params:1}),r=new n({name:"getLogs",call:"shh_getMessages",params:1}),o=new n({name:"poll",call:"shh_getFilterChanges",params:1});return[t,e,r,o]};e.exports={eth:o,shh:i}},{"./method":21}],28:[function(t,e,r){},{}],"bignumber.js":[function(t,e,r){"use strict";e.exports=BigNumber},{}],web3:[function(t,e,r){var n=t("./lib/web3");n.providers.HttpProvider=t("./lib/web3/httpprovider"),n.providers.QtSyncProvider=t("./lib/web3/qtsync"),n.eth.contract=t("./lib/web3/contract"),n.abi=t("./lib/solidity/abi"),"undefined"!=typeof window&&"undefined"==typeof window.web3&&(window.web3=n),e.exports=n},{"./lib/solidity/abi":1,"./lib/web3":10,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/qtsync":24}]},{},["web3"]);`
+require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+/*
+ 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
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ */
+
+var utils = require('../utils/utils');
+var coder = require('./coder');
+var solUtils = require('./utils');
+
+/**
+ * Formats input params to bytes
+ *
+ * @method formatInput
+ * @param {Array} abi inputs of method
+ * @param {Array} params that will be formatted to bytes
+ * @returns bytes representation of input params
+ */
+var formatInput = function (inputs, params) {
+ var i = inputs.map(function (input) {
+ return input.type;
+ });
+ return coder.encodeParams(i, params);
+};
+
+/**
+ * Formats output bytes back to param list
+ *
+ * @method formatOutput
+ * @param {Array} abi outputs of method
+ * @param {String} bytes represention of output
+ * @returns {Array} output params
+ */
+var formatOutput = function (outs, bytes) {
+ var o = outs.map(function (out) {
+ return out.type;
+ });
+
+ return coder.decodeParams(o, bytes);
+};
+
+/**
+ * Should be called to create input parser for contract with given abi
+ *
+ * @method inputParser
+ * @param {Array} contract abi
+ * @returns {Object} input parser object for given json abi
+ * TODO: refactor creating the parser, do not double logic from contract
+ */
+var inputParser = function (json) {
+ var parser = {};
+ json.forEach(function (method) {
+ var displayName = utils.extractDisplayName(method.name);
+ var typeName = utils.extractTypeName(method.name);
+
+ var impl = function () {
+ var params = Array.prototype.slice.call(arguments);
+ return formatInput(method.inputs, params);
+ };
+
+ if (parser[displayName] === undefined) {
+ parser[displayName] = impl;
+ }
+
+ parser[displayName][typeName] = impl;
+ });
+
+ return parser;
+};
+
+/**
+ * Should be called to create output parser for contract with given abi
+ *
+ * @method outputParser
+ * @param {Array} contract abi
+ * @returns {Object} output parser for given json abi
+ */
+var outputParser = function (json) {
+ var parser = {};
+ json.forEach(function (method) {
+
+ var displayName = utils.extractDisplayName(method.name);
+ var typeName = utils.extractTypeName(method.name);
+
+ var impl = function (output) {
+ return formatOutput(method.outputs, output);
+ };
+
+ if (parser[displayName] === undefined) {
+ parser[displayName] = impl;
+ }
+
+ parser[displayName][typeName] = impl;
+ });
+
+ return parser;
+};
+
+var formatConstructorParams = function (abi, params) {
+ var constructor = solUtils.getConstructor(abi, params.length);
+ if (!constructor) {
+ if (params.length > 0) {
+ console.warn("didn't found matching constructor, using default one");
+ }
+ return '';
+ }
+ return formatInput(constructor.inputs, params);
+};
+
+module.exports = {
+ inputParser: inputParser,
+ outputParser: outputParser,
+ formatInput: formatInput,
+ formatOutput: formatOutput,
+ formatConstructorParams: formatConstructorParams
+};
+
+},{"../utils/utils":8,"./coder":2,"./utils":5}],2:[function(require,module,exports){
+/*
+ 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 coder.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var BigNumber = require('bignumber.js');
+var utils = require('../utils/utils');
+var f = require('./formatters');
+var SolidityParam = require('./param');
+
+/**
+ * Should be used to check if a type is an array type
+ *
+ * @method isArrayType
+ * @param {String} type
+ * @return {Bool} true is the type is an array, otherwise false
+ */
+var isArrayType = function (type) {
+ return type.slice(-2) === '[]';
+};
+
+/**
+ * SolidityType prototype is used to encode/decode solidity params of certain type
+ */
+var SolidityType = function (config) {
+ this._name = config.name;
+ this._match = config.match;
+ this._mode = config.mode;
+ this._inputFormatter = config.inputFormatter;
+ this._outputFormatter = config.outputFormatter;
+};
+
+/**
+ * Should be used to determine if this SolidityType do match given type
+ *
+ * @method isType
+ * @param {String} name
+ * @return {Bool} true if type match this SolidityType, otherwise false
+ */
+SolidityType.prototype.isType = function (name) {
+ if (this._match === 'strict') {
+ return this._name === name || (name.indexOf(this._name) === 0 && name.slice(this._name.length) === '[]');
+ } else if (this._match === 'prefix') {
+ // TODO better type detection!
+ return name.indexOf(this._name) === 0;
+ }
+};
+
+/**
+ * Should be used to transform plain param to SolidityParam object
+ *
+ * @method formatInput
+ * @param {Object} param - plain object, or an array of objects
+ * @param {Bool} arrayType - true if a param should be encoded as an array
+ * @return {SolidityParam} encoded param wrapped in SolidityParam object
+ */
+SolidityType.prototype.formatInput = function (param, arrayType) {
+ if (utils.isArray(param) && arrayType) { // TODO: should fail if this two are not the same
+ var self = this;
+ return param.map(function (p) {
+ return self._inputFormatter(p);
+ }).reduce(function (acc, current) {
+ acc.appendArrayElement(current);
+ return acc;
+ }, new SolidityParam(f.formatInputInt(param.length).value));
+ }
+ return this._inputFormatter(param);
+};
+
+/**
+ * Should be used to transoform SolidityParam to plain param
+ *
+ * @method formatOutput
+ * @param {SolidityParam} byteArray
+ * @param {Bool} arrayType - true if a param should be decoded as an array
+ * @return {Object} plain decoded param
+ */
+SolidityType.prototype.formatOutput = function (param, arrayType) {
+ if (arrayType) {
+ // let's assume, that we solidity will never return long arrays :P
+ var result = [];
+ var length = new BigNumber(param.value, 16);
+ for (var i = 0; i < length * 64; i += 64) {
+ result.push(this._outputFormatter(new SolidityParam(param.suffix.slice(i, i + 64))));
+ }
+ return result;
+ }
+ return this._outputFormatter(param);
+};
+
+/**
+ * Should be used to check if a type is variadic
+ *
+ * @method isVariadicType
+ * @param {String} type
+ * @returns {Bool} true if the type is variadic
+ */
+SolidityType.prototype.isVariadicType = function (type) {
+ return isArrayType(type) || this._mode === 'bytes';
+};
+
+/**
+ * Should be used to shift param from params group
+ *
+ * @method shiftParam
+ * @param {String} type
+ * @returns {SolidityParam} shifted param
+ */
+SolidityType.prototype.shiftParam = function (type, param) {
+ if (this._mode === 'bytes') {
+ return param.shiftBytes();
+ } else if (isArrayType(type)) {
+ var length = new BigNumber(param.value.slice(0, 64), 16);
+ return param.shiftArray(length);
+ }
+ return param.shiftValue();
+};
+
+/**
+ * SolidityCoder prototype should be used to encode/decode solidity params of any type
+ */
+var SolidityCoder = function (types) {
+ this._types = types;
+};
+
+/**
+ * This method should be used to transform type to SolidityType
+ *
+ * @method _requireType
+ * @param {String} type
+ * @returns {SolidityType}
+ * @throws {Error} throws if no matching type is found
+ */
+SolidityCoder.prototype._requireType = function (type) {
+ var solidityType = this._types.filter(function (t) {
+ return t.isType(type);
+ })[0];
+
+ if (!solidityType) {
+ throw Error('invalid solidity type!: ' + type);
+ }
+
+ return solidityType;
+};
+
+/**
+ * Should be used to transform plain bytes to SolidityParam object
+ *
+ * @method _bytesToParam
+ * @param {Array} types of params
+ * @param {String} bytes to be transformed to SolidityParam
+ * @return {SolidityParam} SolidityParam for this group of params
+ */
+SolidityCoder.prototype._bytesToParam = function (types, bytes) {
+ var value = bytes.slice(0, types.length * 64);
+ var suffix = bytes.slice(types.length * 64);
+ return new SolidityParam(value, suffix);
+};
+
+/**
+ * Should be used to transform plain param of given type to SolidityParam
+ *
+ * @method _formatInput
+ * @param {String} type of param
+ * @param {Object} plain param
+ * @return {SolidityParam}
+ */
+SolidityCoder.prototype._formatInput = function (type, param) {
+ return this._requireType(type).formatInput(param, isArrayType(type));
+};
+
+/**
+ * Should be used to encode plain param
+ *
+ * @method encodeParam
+ * @param {String} type
+ * @param {Object} plain param
+ * @return {String} encoded plain param
+ */
+SolidityCoder.prototype.encodeParam = function (type, param) {
+ return this._formatInput(type, param).encode();
+};
+
+/**
+ * Should be used to encode list of params
+ *
+ * @method encodeParams
+ * @param {Array} types
+ * @param {Array} params
+ * @return {String} encoded list of params
+ */
+SolidityCoder.prototype.encodeParams = function (types, params) {
+ var self = this;
+ return types.map(function (type, index) {
+ return self._formatInput(type, params[index]);
+ }).reduce(function (acc, solidityParam) {
+ acc.append(solidityParam);
+ return acc;
+ }, new SolidityParam()).encode();
+};
+
+/**
+ * Should be used to transform SolidityParam to plain param
+ *
+ * @method _formatOutput
+ * @param {String} type
+ * @param {SolidityParam} param
+ * @return {Object} plain param
+ */
+SolidityCoder.prototype._formatOutput = function (type, param) {
+ return this._requireType(type).formatOutput(param, isArrayType(type));
+};
+
+/**
+ * Should be used to decode bytes to plain param
+ *
+ * @method decodeParam
+ * @param {String} type
+ * @param {String} bytes
+ * @return {Object} plain param
+ */
+SolidityCoder.prototype.decodeParam = function (type, bytes) {
+ return this._formatOutput(type, this._bytesToParam([type], bytes));
+};
+
+/**
+ * Should be used to decode list of params
+ *
+ * @method decodeParam
+ * @param {Array} types
+ * @param {String} bytes
+ * @return {Array} array of plain params
+ */
+SolidityCoder.prototype.decodeParams = function (types, bytes) {
+ var self = this;
+ var param = this._bytesToParam(types, bytes);
+ return types.map(function (type) {
+ var solidityType = self._requireType(type);
+ var p = solidityType.shiftParam(type, param);
+ return solidityType.formatOutput(p, isArrayType(type));
+ });
+};
+
+var coder = new SolidityCoder([
+ new SolidityType({
+ name: 'address',
+ match: 'strict',
+ mode: 'value',
+ inputFormatter: f.formatInputInt,
+ outputFormatter: f.formatOutputAddress
+ }),
+ new SolidityType({
+ name: 'bool',
+ match: 'strict',
+ mode: 'value',
+ inputFormatter: f.formatInputBool,
+ outputFormatter: f.formatOutputBool
+ }),
+ new SolidityType({
+ name: 'int',
+ match: 'prefix',
+ mode: 'value',
+ inputFormatter: f.formatInputInt,
+ outputFormatter: f.formatOutputInt,
+ }),
+ new SolidityType({
+ name: 'uint',
+ match: 'prefix',
+ mode: 'value',
+ inputFormatter: f.formatInputInt,
+ outputFormatter: f.formatOutputUInt
+ }),
+ new SolidityType({
+ name: 'bytes',
+ match: 'strict',
+ mode: 'bytes',
+ inputFormatter: f.formatInputDynamicBytes,
+ outputFormatter: f.formatOutputDynamicBytes
+ }),
+ new SolidityType({
+ name: 'bytes',
+ match: 'prefix',
+ mode: 'value',
+ inputFormatter: f.formatInputBytes,
+ outputFormatter: f.formatOutputBytes
+ }),
+ new SolidityType({
+ name: 'real',
+ match: 'prefix',
+ mode: 'value',
+ inputFormatter: f.formatInputReal,
+ outputFormatter: f.formatOutputReal
+ }),
+ new SolidityType({
+ name: 'ureal',
+ match: 'prefix',
+ mode: 'value',
+ inputFormatter: f.formatInputReal,
+ outputFormatter: f.formatOutputUReal
+ })
+]);
+
+module.exports = coder;
+
+
+},{"../utils/utils":8,"./formatters":3,"./param":4,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){
+/*
+ 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 formatters.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var BigNumber = require('bignumber.js');
+var utils = require('../utils/utils');
+var c = require('../utils/config');
+var SolidityParam = require('./param');
+
+
+/**
+ * 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
+ *
+ * @method formatInputInt
+ * @param {String|Number|BigNumber} value that needs to be formatted
+ * @returns {SolidityParam}
+ */
+var formatInputInt = function (value) {
+ var padding = c.ETH_PADDING * 2;
+ BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE);
+ var result = utils.padLeft(utils.toTwosComplement(value).round().toString(16), padding);
+ return new SolidityParam(result);
+};
+
+/**
+ * Formats input value to byte representation of string
+ *
+ * @method formatInputBytes
+ * @param {String}
+ * @returns {SolidityParam}
+ */
+var formatInputBytes = function (value) {
+ var result = utils.fromAscii(value, c.ETH_PADDING).substr(2);
+ return new SolidityParam(result);
+};
+
+/**
+ * Formats input value to byte representation of string
+ *
+ * @method formatInputDynamicBytes
+ * @param {String}
+ * @returns {SolidityParam}
+ */
+var formatInputDynamicBytes = function (value) {
+ var result = utils.fromAscii(value, c.ETH_PADDING).substr(2);
+ return new SolidityParam(formatInputInt(value.length).value, result);
+};
+
+/**
+ * Formats input value to byte representation of bool
+ *
+ * @method formatInputBool
+ * @param {Boolean}
+ * @returns {SolidityParam}
+ */
+var formatInputBool = function (value) {
+ var result = '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
+ return new SolidityParam(result);
+};
+
+/**
+ * Formats input value to byte representation of real
+ * Values are multiplied by 2^m and encoded as integers
+ *
+ * @method formatInputReal
+ * @param {String|Number|BigNumber}
+ * @returns {SolidityParam}
+ */
+var formatInputReal = function (value) {
+ return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128)));
+};
+
+/**
+ * Check if input value is negative
+ *
+ * @method signedIsNegative
+ * @param {String} value is hex format
+ * @returns {Boolean} 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 right-aligned output bytes to int
+ *
+ * @method formatOutputInt
+ * @param {SolidityParam} param
+ * @returns {BigNumber} right-aligned output bytes formatted to big number
+ */
+var formatOutputInt = function (param) {
+ var value = param.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 right-aligned output bytes to uint
+ *
+ * @method formatOutputUInt
+ * @param {SolidityParam}
+ * @returns {BigNumeber} right-aligned output bytes formatted to uint
+ */
+var formatOutputUInt = function (param) {
+ var value = param.value || "0";
+ return new BigNumber(value, 16);
+};
+
+/**
+ * Formats right-aligned output bytes to real
+ *
+ * @method formatOutputReal
+ * @param {SolidityParam}
+ * @returns {BigNumber} input bytes formatted to real
+ */
+var formatOutputReal = function (param) {
+ return formatOutputInt(param).dividedBy(new BigNumber(2).pow(128));
+};
+
+/**
+ * Formats right-aligned output bytes to ureal
+ *
+ * @method formatOutputUReal
+ * @param {SolidityParam}
+ * @returns {BigNumber} input bytes formatted to ureal
+ */
+var formatOutputUReal = function (param) {
+ return formatOutputUInt(param).dividedBy(new BigNumber(2).pow(128));
+};
+
+/**
+ * Should be used to format output bool
+ *
+ * @method formatOutputBool
+ * @param {SolidityParam}
+ * @returns {Boolean} right-aligned input bytes formatted to bool
+ */
+var formatOutputBool = function (param) {
+ return param.value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;
+};
+
+/**
+ * Should be used to format output string
+ *
+ * @method formatOutputBytes
+ * @param {SolidityParam} left-aligned hex representation of string
+ * @returns {String} ascii string
+ */
+var formatOutputBytes = function (param) {
+ // length might also be important!
+ return utils.toAscii(param.value);
+};
+
+/**
+ * Should be used to format output string
+ *
+ * @method formatOutputDynamicBytes
+ * @param {SolidityParam} left-aligned hex representation of string
+ * @returns {String} ascii string
+ */
+var formatOutputDynamicBytes = function (param) {
+ // length might also be important!
+ return utils.toAscii(param.suffix);
+};
+
+/**
+ * Should be used to format output address
+ *
+ * @method formatOutputAddress
+ * @param {SolidityParam} right-aligned input bytes
+ * @returns {String} address
+ */
+var formatOutputAddress = function (param) {
+ var value = param.value;
+ return "0x" + value.slice(value.length - 40, value.length);
+};
+
+module.exports = {
+ formatInputInt: formatInputInt,
+ formatInputBytes: formatInputBytes,
+ formatInputDynamicBytes: formatInputDynamicBytes,
+ formatInputBool: formatInputBool,
+ formatInputReal: formatInputReal,
+ formatOutputInt: formatOutputInt,
+ formatOutputUInt: formatOutputUInt,
+ formatOutputReal: formatOutputReal,
+ formatOutputUReal: formatOutputUReal,
+ formatOutputBool: formatOutputBool,
+ formatOutputBytes: formatOutputBytes,
+ formatOutputDynamicBytes: formatOutputDynamicBytes,
+ formatOutputAddress: formatOutputAddress
+};
+
+
+},{"../utils/config":7,"../utils/utils":8,"./param":4,"bignumber.js":"bignumber.js"}],4:[function(require,module,exports){
+/*
+ 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 param.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+/**
+ * SolidityParam object prototype.
+ * Should be used when encoding, decoding solidity bytes
+ */
+var SolidityParam = function (value, suffix) {
+ this.value = value || '';
+ this.suffix = suffix || '';
+};
+
+/**
+ * This method should be used to encode two params one after another
+ *
+ * @method append
+ * @param {SolidityParam} param that it appended after this
+ */
+SolidityParam.prototype.append = function (param) {
+ this.value += param.value;
+ this.suffix += param.suffix;
+};
+
+/**
+ * This method should be used to encode next param in an array
+ *
+ * @method appendArrayElement
+ * @param {SolidityParam} param that is appended to an array
+ */
+SolidityParam.prototype.appendArrayElement = function (param) {
+ this.suffix += param.value;
+ //this.suffix += param.suffix; // we do not support nested dynamic types
+};
+
+/**
+ * This method should be used to create bytearrays from param
+ *
+ * @method encode
+ * @return {String} encoded param(s)
+ */
+SolidityParam.prototype.encode = function () {
+ return this.value + this.suffix;
+};
+
+/**
+ * This method should be used to shift first param from group of params
+ *
+ * @method shiftValue
+ * @return {SolidityParam} first value param
+ */
+SolidityParam.prototype.shiftValue = function () {
+ var value = this.value.slice(0, 64);
+ this.value = this.value.slice(64);
+ return new SolidityParam(value);
+};
+
+/**
+ * This method should be used to first bytes param from group of params
+ *
+ * @method shiftBytes
+ * @return {SolidityParam} first bytes param
+ */
+SolidityParam.prototype.shiftBytes = function () {
+ return this.shiftArray(1);
+};
+
+/**
+ * This method should be used to shift an array from group of params
+ *
+ * @method shiftArray
+ * @param {Number} size of an array to shift
+ * @return {SolidityParam} first array param
+ */
+SolidityParam.prototype.shiftArray = function (length) {
+ var value = this.value.slice(0, 64);
+ this.value = this.value.slice(64);
+ var suffix = this.suffix.slice(0, 64 * length);
+ this.suffix = this.suffix.slice(64 * length);
+ return new SolidityParam(value, suffix);
+};
+
+module.exports = SolidityParam;
+
+
+},{}],5:[function(require,module,exports){
+/*
+ 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 utils.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+/**
+ * Returns the contstructor with matching number of arguments
+ *
+ * @method getConstructor
+ * @param {Array} abi
+ * @param {Number} numberOfArgs
+ * @returns {Object} constructor function abi
+ */
+var getConstructor = function (abi, numberOfArgs) {
+ return abi.filter(function (f) {
+ return f.type === 'constructor' && f.inputs.length === numberOfArgs;
+ })[0];
+};
+
+module.exports = {
+ getConstructor: getConstructor
+};
+
+
+},{}],6:[function(require,module,exports){
+'use strict';
+
+// go env doesn't have and need XMLHttpRequest
+if (typeof XMLHttpRequest === 'undefined') {
+ exports.XMLHttpRequest = {};
+} else {
+ exports.XMLHttpRequest = XMLHttpRequest; // jshint ignore:line
+}
+
+
+},{}],7:[function(require,module,exports){
+/*
+ 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 config.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+/**
+ * Utils
+ *
+ * @module utils
+ */
+
+/**
+ * Utility functions
+ *
+ * @class [utils] config
+ * @constructor
+ */
+
+/// required to define ETH_BIGNUMBER_ROUNDING_MODE
+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'
+];
+
+module.exports = {
+ ETH_PADDING: 32,
+ ETH_SIGNATURE_LENGTH: 4,
+ ETH_UNITS: ETH_UNITS,
+ ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN },
+ ETH_POLLING_TIMEOUT: 1000,
+ defaultBlock: 'latest',
+ defaultAccount: undefined
+};
+
+
+},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){
+/*
+ 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 utils.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+/**
+ * Utils
+ *
+ * @module utils
+ */
+
+/**
+ * Utility functions
+ *
+ * @class [utils] utils
+ * @constructor
+ */
+
+var BigNumber = require('bignumber.js');
+
+var unitMap = {
+ 'wei': '1',
+ 'kwei': '1000',
+ 'ada': '1000',
+ 'mwei': '1000000',
+ 'babbage': '1000000',
+ 'gwei': '1000000000',
+ 'shannon': '1000000000',
+ 'szabo': '1000000000000',
+ 'finney': '1000000000000000',
+ 'ether': '1000000000000000000',
+ 'kether': '1000000000000000000000',
+ 'grand': '1000000000000000000000',
+ 'einstein': '1000000000000000000000',
+ 'mether': '1000000000000000000000000',
+ 'gether': '1000000000000000000000000000',
+ 'tether': '1000000000000000000000000000000'
+};
+
+/**
+ * Should be called to pad string to expected length
+ *
+ * @method padLeft
+ * @param {String} string to be padded
+ * @param {Number} characters that result string should have
+ * @param {String} sign, by default 0
+ * @returns {String} right aligned string
+ */
+var padLeft = function (string, chars, sign) {
+ return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
+};
+
+/**
+ * Should be called to get sting from it's hex representation
+ *
+ * @method toAscii
+ * @param {String} string in hex
+ * @returns {String} ascii string representation of hex value
+ */
+var 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;
+};
+
+/**
+ * Shold be called to get hex representation (prefixed by 0x) of ascii string
+ *
+ * @method toHexNative
+ * @param {String} string
+ * @returns {String} hex representation of input string
+ */
+var toHexNative = 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;
+};
+
+/**
+ * Shold be called to get hex representation (prefixed by 0x) of ascii string
+ *
+ * @method fromAscii
+ * @param {String} string
+ * @param {Number} optional padding
+ * @returns {String} hex representation of input string
+ */
+var fromAscii = function(str, pad) {
+ pad = pad === undefined ? 0 : pad;
+ var hex = toHexNative(str);
+ while (hex.length < pad*2)
+ hex += "00";
+ return "0x" + hex;
+};
+
+/**
+ * Should be used to create full function/event name from json abi
+ *
+ * @method transformToFullName
+ * @param {Object} json-abi
+ * @return {String} full fnction/event name
+ */
+var transformToFullName = function (json) {
+ if (json.name.indexOf('(') !== -1) {
+ return json.name;
+ }
+
+ var typeName = json.inputs.map(function(i){return i.type; }).join();
+ return json.name + '(' + typeName + ')';
+};
+
+/**
+ * Should be called to get display name of contract function
+ *
+ * @method extractDisplayName
+ * @param {String} name of function/event
+ * @returns {String} display name for function/event eg. multiply(uint256) -> multiply
+ */
+var extractDisplayName = function (name) {
+ var length = name.indexOf('(');
+ return length !== -1 ? name.substr(0, length) : name;
+};
+
+/// @returns overloaded part of function/event name
+var extractTypeName = function (name) {
+ /// TODO: make it invulnerable
+ var length = name.indexOf('(');
+ return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : "";
+};
+
+/**
+ * Converts value to it's decimal representation in string
+ *
+ * @method toDecimal
+ * @param {String|Number|BigNumber}
+ * @return {String}
+ */
+var toDecimal = function (value) {
+ return toBigNumber(value).toNumber();
+};
+
+/**
+ * Converts value to it's hex representation
+ *
+ * @method fromDecimal
+ * @param {String|Number|BigNumber}
+ * @return {String}
+ */
+var fromDecimal = function (value) {
+ var number = toBigNumber(value);
+ var result = number.toString(16);
+
+ return number.lessThan(0) ? '-0x' + result.substr(1) : '0x' + result;
+};
+
+/**
+ * Auto converts any given value into it's hex representation.
+ *
+ * And even stringifys objects before.
+ *
+ * @method toHex
+ * @param {String|Number|BigNumber|Object}
+ * @return {String}
+ */
+var toHex = function (val) {
+ /*jshint maxcomplexity:7 */
+
+ if (isBoolean(val))
+ return fromDecimal(+val);
+
+ if (isBigNumber(val))
+ return fromDecimal(val);
+
+ if (isObject(val))
+ return fromAscii(JSON.stringify(val));
+
+ // if its a negative number, pass it through fromDecimal
+ if (isString(val)) {
+ if (val.indexOf('-0x') === 0)
+ return fromDecimal(val);
+ else if (!isFinite(val))
+ return fromAscii(val);
+ }
+
+ return fromDecimal(val);
+};
+
+/**
+ * Returns value of unit in Wei
+ *
+ * @method getValueOfUnit
+ * @param {String} unit the unit to convert to, default ether
+ * @returns {BigNumber} value of the unit (in Wei)
+ * @throws error if the unit is not correct:w
+ */
+var getValueOfUnit = function (unit) {
+ unit = unit ? unit.toLowerCase() : 'ether';
+ var unitValue = unitMap[unit];
+ if (unitValue === undefined) {
+ throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2));
+ }
+ return new BigNumber(unitValue, 10);
+};
+
+/**
+ * Takes a number of wei and converts it to any other ether unit.
+ *
+ * Possible units are:
+ * - kwei/ada
+ * - mwei/babbage
+ * - gwei/shannon
+ * - szabo
+ * - finney
+ * - ether
+ * - kether/grand/einstein
+ * - mether
+ * - gether
+ * - tether
+ *
+ * @method fromWei
+ * @param {Number|String} number can be a number, number string or a HEX of a decimal
+ * @param {String} unit the unit to convert to, default ether
+ * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number
+*/
+var fromWei = function(number, unit) {
+ var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit));
+
+ return isBigNumber(number) ? returnValue : returnValue.toString(10);
+};
+
+/**
+ * Takes a number of a unit and converts it to wei.
+ *
+ * Possible units are:
+ * - kwei/ada
+ * - mwei/babbage
+ * - gwei/shannon
+ * - szabo
+ * - finney
+ * - ether
+ * - kether/grand/einstein
+ * - mether
+ * - gether
+ * - tether
+ *
+ * @method toWei
+ * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal
+ * @param {String} unit the unit to convert from, default ether
+ * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number
+*/
+var toWei = function(number, unit) {
+ var returnValue = toBigNumber(number).times(getValueOfUnit(unit));
+
+ return isBigNumber(number) ? returnValue : returnValue.toString(10);
+};
+
+/**
+ * Takes an input and transforms it into an bignumber
+ *
+ * @method toBigNumber
+ * @param {Number|String|BigNumber} a number, string, HEX string or BigNumber
+ * @return {BigNumber} BigNumber
+*/
+var toBigNumber = function(number) {
+ /*jshint maxcomplexity:5 */
+ number = number || 0;
+ if (isBigNumber(number))
+ return number;
+
+ if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) {
+ return new BigNumber(number.replace('0x',''), 16);
+ }
+
+ return new BigNumber(number.toString(10), 10);
+};
+
+/**
+ * Takes and input transforms it into bignumber and if it is negative value, into two's complement
+ *
+ * @method toTwosComplement
+ * @param {Number|String|BigNumber}
+ * @return {BigNumber}
+ */
+var toTwosComplement = function (number) {
+ var bigNumber = toBigNumber(number);
+ if (bigNumber.lessThan(0)) {
+ return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1);
+ }
+ return bigNumber;
+};
+
+/**
+ * Checks if the given string is strictly an address
+ *
+ * @method isStrictAddress
+ * @param {String} address the given HEX adress
+ * @return {Boolean}
+*/
+var isStrictAddress = function (address) {
+ return /^0x[0-9a-f]{40}$/.test(address);
+};
+
+/**
+ * Checks if the given string is an address
+ *
+ * @method isAddress
+ * @param {String} address the given HEX adress
+ * @return {Boolean}
+*/
+var isAddress = function (address) {
+ return /^(0x)?[0-9a-f]{40}$/.test(address);
+};
+
+/**
+ * Transforms given string to valid 20 bytes-length addres with 0x prefix
+ *
+ * @method toAddress
+ * @param {String} address
+ * @return {String} formatted address
+ */
+var toAddress = function (address) {
+ if (isStrictAddress(address)) {
+ return address;
+ }
+
+ if (/^[0-9a-f]{40}$/.test(address)) {
+ return '0x' + address;
+ }
+
+ return '0x' + padLeft(toHex(address).substr(2), 40);
+};
+
+/**
+ * Returns true if object is BigNumber, otherwise false
+ *
+ * @method isBigNumber
+ * @param {Object}
+ * @return {Boolean}
+ */
+var isBigNumber = function (object) {
+ return object instanceof BigNumber ||
+ (object && object.constructor && object.constructor.name === 'BigNumber');
+};
+
+/**
+ * Returns true if object is string, otherwise false
+ *
+ * @method isString
+ * @param {Object}
+ * @return {Boolean}
+ */
+var isString = function (object) {
+ return typeof object === 'string' ||
+ (object && object.constructor && object.constructor.name === 'String');
+};
+
+/**
+ * Returns true if object is function, otherwise false
+ *
+ * @method isFunction
+ * @param {Object}
+ * @return {Boolean}
+ */
+var isFunction = function (object) {
+ return typeof object === 'function';
+};
+
+/**
+ * Returns true if object is Objet, otherwise false
+ *
+ * @method isObject
+ * @param {Object}
+ * @return {Boolean}
+ */
+var isObject = function (object) {
+ return typeof object === 'object';
+};
+
+/**
+ * Returns true if object is boolean, otherwise false
+ *
+ * @method isBoolean
+ * @param {Object}
+ * @return {Boolean}
+ */
+var isBoolean = function (object) {
+ return typeof object === 'boolean';
+};
+
+/**
+ * Returns true if object is array, otherwise false
+ *
+ * @method isArray
+ * @param {Object}
+ * @return {Boolean}
+ */
+var isArray = function (object) {
+ return object instanceof Array;
+};
+
+/**
+ * Returns true if given string is valid json object
+ *
+ * @method isJson
+ * @param {String}
+ * @return {Boolean}
+ */
+var isJson = function (str) {
+ try {
+ return !!JSON.parse(str);
+ } catch (e) {
+ return false;
+ }
+};
+
+module.exports = {
+ padLeft: padLeft,
+ toHex: toHex,
+ toDecimal: toDecimal,
+ fromDecimal: fromDecimal,
+ toAscii: toAscii,
+ fromAscii: fromAscii,
+ transformToFullName: transformToFullName,
+ extractDisplayName: extractDisplayName,
+ extractTypeName: extractTypeName,
+ toWei: toWei,
+ fromWei: fromWei,
+ toBigNumber: toBigNumber,
+ toTwosComplement: toTwosComplement,
+ toAddress: toAddress,
+ isBigNumber: isBigNumber,
+ isStrictAddress: isStrictAddress,
+ isAddress: isAddress,
+ isFunction: isFunction,
+ isString: isString,
+ isObject: isObject,
+ isBoolean: isBoolean,
+ isArray: isArray,
+ isJson: isJson
+};
+
+
+},{"bignumber.js":"bignumber.js"}],9:[function(require,module,exports){
+module.exports={
+ "version": "0.3.5"
+}
+
+},{}],10:[function(require,module,exports){
+/*
+ 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>
+ * Fabian Vogelsteller <fabian@ethdev.com>
+ * Gav Wood <g@ethdev.com>
+ * @date 2014
+ */
+
+var version = require('./version.json');
+var net = require('./web3/net');
+var eth = require('./web3/eth');
+var db = require('./web3/db');
+var shh = require('./web3/shh');
+var watches = require('./web3/watches');
+var Filter = require('./web3/filter');
+var utils = require('./utils/utils');
+var formatters = require('./web3/formatters');
+var RequestManager = require('./web3/requestmanager');
+var c = require('./utils/config');
+var Method = require('./web3/method');
+var Property = require('./web3/property');
+
+var web3Methods = [
+ new Method({
+ name: 'sha3',
+ call: 'web3_sha3',
+ params: 1
+ })
+];
+
+var web3Properties = [
+ new Property({
+ name: 'version.client',
+ getter: 'web3_clientVersion'
+ }),
+ new Property({
+ name: 'version.network',
+ getter: 'net_version',
+ inputFormatter: utils.toDecimal
+ }),
+ new Property({
+ name: 'version.ethereum',
+ getter: 'eth_protocolVersion',
+ inputFormatter: utils.toDecimal
+ }),
+ new Property({
+ name: 'version.whisper',
+ getter: 'shh_version',
+ inputFormatter: utils.toDecimal
+ })
+];
+
+/// 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) {
+ method.attachToObject(obj);
+ });
+};
+
+/// 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) {
+ property.attachToObject(obj);
+ });
+};
+
+/// setups web3 object, and it's in-browser executed methods
+var web3 = {};
+web3.providers = {};
+web3.version = {};
+web3.version.api = version.version;
+web3.eth = {};
+
+/*jshint maxparams:4 */
+web3.eth.filter = function (fil, eventParams, options, formatter) {
+
+ // if its event, treat it differently
+ // TODO: simplify and remove
+ if (fil._isEvent) {
+ return fil(eventParams, options);
+ }
+
+ // what outputLogFormatter? that's wrong
+ //return new Filter(fil, watches.eth(), formatters.outputLogFormatter);
+ return new Filter(fil, watches.eth(), formatter || formatters.outputLogFormatter);
+};
+/*jshint maxparams:3 */
+
+web3.shh = {};
+web3.shh.filter = function (fil) {
+ return new Filter(fil, watches.shh(), formatters.outputPostFormatter);
+};
+web3.net = {};
+web3.db = {};
+web3.setProvider = function (provider) {
+ RequestManager.getInstance().setProvider(provider);
+};
+web3.reset = function () {
+ RequestManager.getInstance().reset();
+ c.defaultBlock = 'latest';
+ c.defaultAccount = undefined;
+};
+web3.toHex = utils.toHex;
+web3.toAscii = utils.toAscii;
+web3.fromAscii = utils.fromAscii;
+web3.toDecimal = utils.toDecimal;
+web3.fromDecimal = utils.fromDecimal;
+web3.toBigNumber = utils.toBigNumber;
+web3.toWei = utils.toWei;
+web3.fromWei = utils.fromWei;
+web3.isAddress = utils.isAddress;
+
+// ADD defaultblock
+Object.defineProperty(web3.eth, 'defaultBlock', {
+ get: function () {
+ return c.defaultBlock;
+ },
+ set: function (val) {
+ c.defaultBlock = val;
+ return val;
+ }
+});
+
+Object.defineProperty(web3.eth, 'defaultAccount', {
+ get: function () {
+ return c.defaultAccount;
+ },
+ set: function (val) {
+ c.defaultAccount = val;
+ return val;
+ }
+});
+
+/// setups all api methods
+setupMethods(web3, web3Methods);
+setupProperties(web3, web3Properties);
+setupMethods(web3.net, net.methods);
+setupProperties(web3.net, net.properties);
+setupMethods(web3.eth, eth.methods);
+setupProperties(web3.eth, eth.properties);
+setupMethods(web3.db, db.methods);
+setupMethods(web3.shh, shh.methods);
+
+module.exports = web3;
+
+
+},{"./utils/config":7,"./utils/utils":8,"./version.json":9,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/method":21,"./web3/net":22,"./web3/property":23,"./web3/requestmanager":25,"./web3/shh":26,"./web3/watches":27}],11:[function(require,module,exports){
+/*
+ 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
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2014
+ */
+
+var web3 = require('../web3');
+var solAbi = require('../solidity/abi');
+var utils = require('../utils/utils');
+var SolidityEvent = require('./event');
+var SolidityFunction = require('./function');
+
+var addFunctionsToContract = function (contract, desc) {
+ desc.filter(function (json) {
+ return json.type === 'function';
+ }).map(function (json) {
+ return new SolidityFunction(json, contract.address);
+ }).forEach(function (f) {
+ f.attachToContract(contract);
+ });
+};
+
+var addEventsToContract = function (contract, desc) {
+ desc.filter(function (json) {
+ return json.type === 'event';
+ }).map(function (json) {
+ return new SolidityEvent(json, contract.address);
+ }).forEach(function (e) {
+ e.attachToContract(contract);
+ });
+};
+
+/**
+ * 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(abi); // creation of contract prototype
+ *
+ * var contractInstance = new MyContract('0x0123123121');
+ *
+ * contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default)
+ * contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit)
+ * contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction
+ *
+ * @param abi - abi json description of the contract, which is being created
+ * @returns contract object
+ */
+var contract = function (abi) {
+
+ // return prototype
+ return Contract.bind(null, abi);
+};
+
+var Contract = function (abi, options) {
+
+ this.address = '';
+ if (utils.isAddress(options)) {
+ this.address = options;
+ } else { // is an object!
+ // TODO, parse the rest of the args
+ options = options || {};
+ var args = Array.prototype.slice.call(arguments, 2);
+ var bytes = solAbi.formatConstructorParams(abi, args);
+ options.data += bytes;
+ this.address = web3.eth.sendTransaction(options);
+ }
+
+ addFunctionsToContract(this, abi);
+ addEventsToContract(this, abi);
+};
+
+Contract.prototype.call = function () {
+ console.error('contract.call is deprecated');
+ return this;
+};
+
+Contract.prototype.sendTransaction = function () {
+ console.error('contract.sendTransact is deprecated');
+ return this;
+};
+
+module.exports = contract;
+
+
+},{"../solidity/abi":1,"../utils/utils":8,"../web3":10,"./event":15,"./function":18}],12:[function(require,module,exports){
+/*
+ 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 db.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var Method = require('./method');
+
+var putString = new Method({
+ name: 'putString',
+ call: 'db_putString',
+ params: 3
+});
+
+
+var getString = new Method({
+ name: 'getString',
+ call: 'db_getString',
+ params: 2
+});
+
+var putHex = new Method({
+ name: 'putHex',
+ call: 'db_putHex',
+ params: 3
+});
+
+var getHex = new Method({
+ name: 'getHex',
+ call: 'db_getHex',
+ params: 2
+});
+
+var methods = [
+ putString, getString, putHex, getHex
+];
+
+module.exports = {
+ methods: methods
+};
+
+},{"./method":21}],13:[function(require,module,exports){
+/*
+ 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 errors.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+module.exports = {
+ InvalidNumberOfParams: function () {
+ return new Error('Invalid number of input parameters');
+ },
+ InvalidConnection: function (host){
+ return new Error('CONNECTION ERROR: Couldn\'t connect to node '+ host +', is it running?');
+ },
+ InvalidProvider: function () {
+ return new Error('Providor not set or invalid');
+ },
+ InvalidResponse: function (result){
+ var message = !!result && !!result.error && !!result.error.message ? result.error.message : 'Invalid JSON RPC response';
+ return new Error(message);
+ }
+};
+
+
+},{}],14:[function(require,module,exports){
+/*
+ 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 eth.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @author Fabian Vogelsteller <fabian@ethdev.com>
+ * @date 2015
+ */
+
+/**
+ * Web3
+ *
+ * @module web3
+ */
+
+/**
+ * Eth methods and properties
+ *
+ * An example method object can look as follows:
+ *
+ * {
+ * name: 'getBlock',
+ * call: blockCall,
+ * params: 2,
+ * outputFormatter: formatters.outputBlockFormatter,
+ * inputFormatter: [ // can be a formatter funciton or an array of functions. Where each item in the array will be used for one parameter
+ * utils.toHex, // formats paramter 1
+ * function(param){ return !!param; } // formats paramter 2
+ * ]
+ * },
+ *
+ * @class [web3] eth
+ * @constructor
+ */
+
+"use strict";
+
+var formatters = require('./formatters');
+var utils = require('../utils/utils');
+var Method = require('./method');
+var Property = require('./property');
+
+var blockCall = function (args) {
+ return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber";
+};
+
+var transactionFromBlockCall = function (args) {
+ return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex';
+};
+
+var uncleCall = function (args) {
+ return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex';
+};
+
+var getBlockTransactionCountCall = function (args) {
+ return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber';
+};
+
+var uncleCountCall = function (args) {
+ return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber';
+};
+
+/// @returns an array of objects describing web3.eth api methods
+
+var getBalance = new Method({
+ name: 'getBalance',
+ call: 'eth_getBalance',
+ params: 2,
+ inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter],
+ outputFormatter: formatters.outputBigNumberFormatter
+});
+
+var getStorageAt = new Method({
+ name: 'getStorageAt',
+ call: 'eth_getStorageAt',
+ params: 3,
+ inputFormatter: [null, utils.toHex, formatters.inputDefaultBlockNumberFormatter]
+});
+
+var getCode = new Method({
+ name: 'getCode',
+ call: 'eth_getCode',
+ params: 2,
+ inputFormatter: [utils.toAddress, formatters.inputDefaultBlockNumberFormatter]
+});
+
+var getBlock = new Method({
+ name: 'getBlock',
+ call: blockCall,
+ params: 2,
+ inputFormatter: [formatters.inputBlockNumberFormatter, function (val) { return !!val; }],
+ outputFormatter: formatters.outputBlockFormatter
+});
+
+var getUncle = new Method({
+ name: 'getUncle',
+ call: uncleCall,
+ params: 2,
+ inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex],
+ outputFormatter: formatters.outputBlockFormatter,
+
+});
+
+var getCompilers = new Method({
+ name: 'getCompilers',
+ call: 'eth_getCompilers',
+ params: 0
+});
+
+var getBlockTransactionCount = new Method({
+ name: 'getBlockTransactionCount',
+ call: getBlockTransactionCountCall,
+ params: 1,
+ inputFormatter: [formatters.inputBlockNumberFormatter],
+ outputFormatter: utils.toDecimal
+});
+
+var getBlockUncleCount = new Method({
+ name: 'getBlockUncleCount',
+ call: uncleCountCall,
+ params: 1,
+ inputFormatter: [formatters.inputBlockNumberFormatter],
+ outputFormatter: utils.toDecimal
+});
+
+var getTransaction = new Method({
+ name: 'getTransaction',
+ call: 'eth_getTransactionByHash',
+ params: 1,
+ outputFormatter: formatters.outputTransactionFormatter
+});
+
+var getTransactionFromBlock = new Method({
+ name: 'getTransactionFromBlock',
+ call: transactionFromBlockCall,
+ params: 2,
+ inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex],
+ outputFormatter: formatters.outputTransactionFormatter
+});
+
+var getTransactionCount = new Method({
+ name: 'getTransactionCount',
+ call: 'eth_getTransactionCount',
+ params: 2,
+ inputFormatter: [null, formatters.inputDefaultBlockNumberFormatter],
+ outputFormatter: utils.toDecimal
+});
+
+var sign = new Method({
+ name: 'sign',
+ call: 'eth_sign',
+ params: 1
+});
+
+var sendTransaction = new Method({
+ name: 'sendTransaction',
+ call: 'eth_sendTransaction',
+ params: 1,
+ inputFormatter: [formatters.inputTransactionFormatter]
+});
+
+var call = new Method({
+ name: 'call',
+ call: 'eth_call',
+ params: 2,
+ inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter]
+});
+
+var compileSolidity = new Method({
+ name: 'compile.solidity',
+ call: 'eth_compileSolidity',
+ params: 1
+});
+
+var compileLLL = new Method({
+ name: 'compile.lll',
+ call: 'eth_compileLLL',
+ params: 1
+});
+
+var compileSerpent = new Method({
+ name: 'compile.serpent',
+ call: 'eth_compileSerpent',
+ params: 1
+});
+
+var methods = [
+ getBalance,
+ getStorageAt,
+ getCode,
+ getBlock,
+ getUncle,
+ getCompilers,
+ getBlockTransactionCount,
+ getBlockUncleCount,
+ getTransaction,
+ getTransactionFromBlock,
+ getTransactionCount,
+ call,
+ sign,
+ sendTransaction,
+ compileSolidity,
+ compileLLL,
+ compileSerpent,
+];
+
+/// @returns an array of objects describing web3.eth api properties
+
+
+
+var properties = [
+ new Property({
+ name: 'coinbase',
+ getter: 'eth_coinbase'
+ }),
+ new Property({
+ name: 'mining',
+ getter: 'eth_mining'
+ }),
+ new Property({
+ name: 'hashrate',
+ getter: 'eth_hashrate',
+ outputFormatter: utils.toDecimal
+ }),
+ new Property({
+ name: 'gasPrice',
+ getter: 'eth_gasPrice',
+ outputFormatter: formatters.outputBigNumberFormatter
+ }),
+ new Property({
+ name: 'accounts',
+ getter: 'eth_accounts'
+ }),
+ new Property({
+ name: 'blockNumber',
+ getter: 'eth_blockNumber',
+ outputFormatter: utils.toDecimal
+ })
+];
+
+module.exports = {
+ methods: methods,
+ properties: properties
+};
+
+
+},{"../utils/utils":8,"./formatters":17,"./method":21,"./property":23}],15:[function(require,module,exports){
+/*
+ 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 event.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2014
+ */
+
+var utils = require('../utils/utils');
+var coder = require('../solidity/coder');
+var web3 = require('../web3');
+var formatters = require('./formatters');
+
+/**
+ * This prototype should be used to create event filters
+ */
+var SolidityEvent = function (json, address) {
+ this._params = json.inputs;
+ this._name = utils.transformToFullName(json);
+ this._address = address;
+ this._anonymous = json.anonymous;
+};
+
+/**
+ * Should be used to get filtered param types
+ *
+ * @method types
+ * @param {Bool} decide if returned typed should be indexed
+ * @return {Array} array of types
+ */
+SolidityEvent.prototype.types = function (indexed) {
+ return this._params.filter(function (i) {
+ return i.indexed === indexed;
+ }).map(function (i) {
+ return i.type;
+ });
+};
+
+/**
+ * Should be used to get event display name
+ *
+ * @method displayName
+ * @return {String} event display name
+ */
+SolidityEvent.prototype.displayName = function () {
+ return utils.extractDisplayName(this._name);
+};
+
+/**
+ * Should be used to get event type name
+ *
+ * @method typeName
+ * @return {String} event type name
+ */
+SolidityEvent.prototype.typeName = function () {
+ return utils.extractTypeName(this._name);
+};
+
+/**
+ * Should be used to get event signature
+ *
+ * @method signature
+ * @return {String} event signature
+ */
+SolidityEvent.prototype.signature = function () {
+ return web3.sha3(web3.fromAscii(this._name)).slice(2);
+};
+
+/**
+ * Should be used to encode indexed params and options to one final object
+ *
+ * @method encode
+ * @param {Object} indexed
+ * @param {Object} options
+ * @return {Object} everything combined together and encoded
+ */
+SolidityEvent.prototype.encode = function (indexed, options) {
+ indexed = indexed || {};
+ options = options || {};
+ var result = {};
+
+ ['fromBlock', 'toBlock'].filter(function (f) {
+ return options[f] !== undefined;
+ }).forEach(function (f) {
+ result[f] = formatters.inputBlockNumberFormatter(options[f]);
+ });
+
+ result.topics = [];
+
+ if (!this._anonymous) {
+ result.address = this._address;
+ result.topics.push('0x' + this.signature());
+ }
+
+ var indexedTopics = this._params.filter(function (i) {
+ return i.indexed === true;
+ }).map(function (i) {
+ var value = indexed[i.name];
+ if (value === undefined || value === null) {
+ return null;
+ }
+
+ if (utils.isArray(value)) {
+ return value.map(function (v) {
+ return '0x' + coder.encodeParam(i.type, v);
+ });
+ }
+ return '0x' + coder.encodeParam(i.type, value);
+ });
+
+ result.topics = result.topics.concat(indexedTopics);
+
+ return result;
+};
+
+/**
+ * Should be used to decode indexed params and options
+ *
+ * @method decode
+ * @param {Object} data
+ * @return {Object} result object with decoded indexed && not indexed params
+ */
+SolidityEvent.prototype.decode = function (data) {
+
+ data.data = data.data || '';
+ data.topics = data.topics || [];
+
+ var argTopics = this._anonymous ? data.topics : data.topics.slice(1);
+ var indexedData = argTopics.map(function (topics) { return topics.slice(2); }).join("");
+ var indexedParams = coder.decodeParams(this.types(true), indexedData);
+
+ var notIndexedData = data.data.slice(2);
+ var notIndexedParams = coder.decodeParams(this.types(false), notIndexedData);
+
+ var result = formatters.outputLogFormatter(data);
+ result.event = this.displayName();
+ result.address = data.address;
+
+ result.args = this._params.reduce(function (acc, current) {
+ acc[current.name] = current.indexed ? indexedParams.shift() : notIndexedParams.shift();
+ return acc;
+ }, {});
+
+ delete result.data;
+ delete result.topics;
+
+ return result;
+};
+
+/**
+ * Should be used to create new filter object from event
+ *
+ * @method execute
+ * @param {Object} indexed
+ * @param {Object} options
+ * @return {Object} filter object
+ */
+SolidityEvent.prototype.execute = function (indexed, options) {
+ var o = this.encode(indexed, options);
+ var formatter = this.decode.bind(this);
+ return web3.eth.filter(o, undefined, undefined, formatter);
+};
+
+/**
+ * Should be used to attach event to contract object
+ *
+ * @method attachToContract
+ * @param {Contract}
+ */
+SolidityEvent.prototype.attachToContract = function (contract) {
+ var execute = this.execute.bind(this);
+ var displayName = this.displayName();
+ if (!contract[displayName]) {
+ contract[displayName] = execute;
+ }
+ contract[displayName][this.typeName()] = this.execute.bind(this, contract);
+};
+
+module.exports = SolidityEvent;
+
+
+},{"../solidity/coder":2,"../utils/utils":8,"../web3":10,"./formatters":17}],16:[function(require,module,exports){
+/*
+ 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>
+ * Fabian Vogelsteller <fabian@ethdev.com>
+ * Gav Wood <g@ethdev.com>
+ * @date 2014
+ */
+
+var RequestManager = require('./requestmanager');
+var formatters = require('./formatters');
+var utils = require('../utils/utils');
+
+/**
+* Converts a given topic to a hex string, but also allows null values.
+*
+* @param {Mixed} value
+* @return {String}
+*/
+var toTopic = function(value){
+
+ if(value === null || typeof value === 'undefined')
+ return null;
+
+ value = String(value);
+
+ if(value.indexOf('0x') === 0)
+ return value;
+ else
+ return utils.fromAscii(value);
+};
+
+/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones
+/// @param should be string or object
+/// @returns options string or object
+var getOptions = function (options) {
+
+ if (utils.isString(options)) {
+ return options;
+ }
+
+ options = options || {};
+
+ // make sure topics, get converted to hex
+ options.topics = options.topics || [];
+ options.topics = options.topics.map(function(topic){
+ return (utils.isArray(topic)) ? topic.map(toTopic) : toTopic(topic);
+ });
+
+ // lazy load
+ return {
+ topics: options.topics,
+ to: options.to,
+ address: options.address,
+ fromBlock: formatters.inputBlockNumberFormatter(options.fromBlock),
+ toBlock: formatters.inputBlockNumberFormatter(options.toBlock)
+ };
+};
+
+var Filter = function (options, methods, formatter) {
+ var implementation = {};
+ methods.forEach(function (method) {
+ method.attachToObject(implementation);
+ });
+ this.options = getOptions(options);
+ this.implementation = implementation;
+ this.callbacks = [];
+ this.formatter = formatter;
+ this.filterId = this.implementation.newFilter(this.options);
+};
+
+Filter.prototype.watch = function (callback) {
+ this.callbacks.push(callback);
+ var self = this;
+
+ var onMessage = function (error, messages) {
+ if (error) {
+ return self.callbacks.forEach(function (callback) {
+ callback(error);
+ });
+ }
+
+ messages.forEach(function (message) {
+ message = self.formatter ? self.formatter(message) : message;
+ self.callbacks.forEach(function (callback) {
+ callback(null, message);
+ });
+ });
+ };
+
+ // call getFilterLogs on start
+ if (!utils.isString(this.options)) {
+ this.get(function (err, messages) {
+ // don't send all the responses to all the watches again... just to this one
+ if (err) {
+ callback(err);
+ }
+
+ messages.forEach(function (message) {
+ callback(null, message);
+ });
+ });
+ }
+
+ RequestManager.getInstance().startPolling({
+ method: this.implementation.poll.call,
+ params: [this.filterId],
+ }, this.filterId, onMessage, this.stopWatching.bind(this));
+};
+
+Filter.prototype.stopWatching = function () {
+ RequestManager.getInstance().stopPolling(this.filterId);
+ this.implementation.uninstallFilter(this.filterId);
+ this.callbacks = [];
+};
+
+Filter.prototype.get = function (callback) {
+ var self = this;
+ if (utils.isFunction(callback)) {
+ this.implementation.getLogs(this.filterId, function(err, res){
+ if (err) {
+ callback(err);
+ } else {
+ callback(null, res.map(function (log) {
+ return self.formatter ? self.formatter(log) : log;
+ }));
+ }
+ });
+ } else {
+ var logs = this.implementation.getLogs(this.filterId);
+ return logs.map(function (log) {
+ return self.formatter ? self.formatter(log) : log;
+ });
+ }
+};
+
+module.exports = Filter;
+
+
+},{"../utils/utils":8,"./formatters":17,"./requestmanager":25}],17:[function(require,module,exports){
+/*
+ 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 formatters.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @author Fabian Vogelsteller <fabian@ethdev.com>
+ * @date 2015
+ */
+
+var utils = require('../utils/utils');
+var config = require('../utils/config');
+
+/**
+ * Should the format output to a big number
+ *
+ * @method outputBigNumberFormatter
+ * @param {String|Number|BigNumber}
+ * @returns {BigNumber} object
+ */
+var outputBigNumberFormatter = function (number) {
+ return utils.toBigNumber(number);
+};
+
+var isPredefinedBlockNumber = function (blockNumber) {
+ return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest';
+};
+
+var inputDefaultBlockNumberFormatter = function (blockNumber) {
+ if (blockNumber === undefined) {
+ return config.defaultBlock;
+ }
+ return inputBlockNumberFormatter(blockNumber);
+};
+
+var inputBlockNumberFormatter = function (blockNumber) {
+ if (blockNumber === undefined) {
+ return undefined;
+ } else if (isPredefinedBlockNumber(blockNumber)) {
+ return blockNumber;
+ }
+ return utils.toHex(blockNumber);
+};
+
+/**
+ * Formats the input of a transaction and converts all values to HEX
+ *
+ * @method inputTransactionFormatter
+ * @param {Object} transaction options
+ * @returns object
+*/
+var inputTransactionFormatter = function (options){
+
+ options.from = options.from || config.defaultAccount;
+
+ // make code -> data
+ if (options.code) {
+ options.data = options.code;
+ delete options.code;
+ }
+
+ ['gasPrice', 'gas', 'value'].filter(function (key) {
+ return options[key] !== undefined;
+ }).forEach(function(key){
+ options[key] = utils.fromDecimal(options[key]);
+ });
+
+ return options;
+};
+
+/**
+ * Formats the output of a transaction to its proper values
+ *
+ * @method outputTransactionFormatter
+ * @param {Object} transaction
+ * @returns {Object} transaction
+*/
+var outputTransactionFormatter = function (tx){
+ tx.blockNumber = utils.toDecimal(tx.blockNumber);
+ tx.transactionIndex = utils.toDecimal(tx.transactionIndex);
+ tx.nonce = utils.toDecimal(tx.nonce);
+ tx.gas = utils.toDecimal(tx.gas);
+ tx.gasPrice = utils.toBigNumber(tx.gasPrice);
+ tx.value = utils.toBigNumber(tx.value);
+ return tx;
+};
+
+/**
+ * Formats the output of a block to its proper values
+ *
+ * @method outputBlockFormatter
+ * @param {Object} block object
+ * @returns {Object} block object
+*/
+var outputBlockFormatter = function(block) {
+
+ // transform to number
+ block.gasLimit = utils.toDecimal(block.gasLimit);
+ block.gasUsed = utils.toDecimal(block.gasUsed);
+ block.size = utils.toDecimal(block.size);
+ block.timestamp = utils.toDecimal(block.timestamp);
+ block.number = utils.toDecimal(block.number);
+
+ block.difficulty = utils.toBigNumber(block.difficulty);
+ block.totalDifficulty = utils.toBigNumber(block.totalDifficulty);
+
+ if (utils.isArray(block.transactions)) {
+ block.transactions.forEach(function(item){
+ if(!utils.isString(item))
+ return outputTransactionFormatter(item);
+ });
+ }
+
+ return block;
+};
+
+/**
+ * Formats the output of a log
+ *
+ * @method outputLogFormatter
+ * @param {Object} log object
+ * @returns {Object} log
+*/
+var outputLogFormatter = function(log) {
+ if (log === null) { // 'pending' && 'latest' filters are nulls
+ return null;
+ }
+
+ log.blockNumber = utils.toDecimal(log.blockNumber);
+ log.transactionIndex = utils.toDecimal(log.transactionIndex);
+ log.logIndex = utils.toDecimal(log.logIndex);
+
+ return log;
+};
+
+/**
+ * Formats the input of a whisper post and converts all values to HEX
+ *
+ * @method inputPostFormatter
+ * @param {Object} transaction object
+ * @returns {Object}
+*/
+var inputPostFormatter = function(post) {
+
+ post.payload = utils.toHex(post.payload);
+ post.ttl = utils.fromDecimal(post.ttl);
+ post.workToProve = utils.fromDecimal(post.workToProve);
+ post.priority = utils.fromDecimal(post.priority);
+
+ // fallback
+ if (!utils.isArray(post.topics)) {
+ post.topics = post.topics ? [post.topics] : [];
+ }
+
+ // format the following options
+ post.topics = post.topics.map(function(topic){
+ return utils.fromAscii(topic);
+ });
+
+ return post;
+};
+
+/**
+ * Formats the output of a received post message
+ *
+ * @method outputPostFormatter
+ * @param {Object}
+ * @returns {Object}
+ */
+var outputPostFormatter = function(post){
+
+ post.expiry = utils.toDecimal(post.expiry);
+ post.sent = utils.toDecimal(post.sent);
+ post.ttl = utils.toDecimal(post.ttl);
+ post.workProved = utils.toDecimal(post.workProved);
+ post.payloadRaw = post.payload;
+ post.payload = utils.toAscii(post.payload);
+
+ if (utils.isJson(post.payload)) {
+ post.payload = JSON.parse(post.payload);
+ }
+
+ // format the following options
+ if (!post.topics) {
+ post.topics = [];
+ }
+ post.topics = post.topics.map(function(topic){
+ return utils.toAscii(topic);
+ });
+
+ return post;
+};
+
+module.exports = {
+ inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter,
+ inputBlockNumberFormatter: inputBlockNumberFormatter,
+ inputTransactionFormatter: inputTransactionFormatter,
+ inputPostFormatter: inputPostFormatter,
+ outputBigNumberFormatter: outputBigNumberFormatter,
+ outputTransactionFormatter: outputTransactionFormatter,
+ outputBlockFormatter: outputBlockFormatter,
+ outputLogFormatter: outputLogFormatter,
+ outputPostFormatter: outputPostFormatter
+};
+
+
+},{"../utils/config":7,"../utils/utils":8}],18:[function(require,module,exports){
+/*
+ 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 function.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var web3 = require('../web3');
+var coder = require('../solidity/coder');
+var utils = require('../utils/utils');
+
+/**
+ * This prototype should be used to call/sendTransaction to solidity functions
+ */
+var SolidityFunction = function (json, address) {
+ this._inputTypes = json.inputs.map(function (i) {
+ return i.type;
+ });
+ this._outputTypes = json.outputs.map(function (i) {
+ return i.type;
+ });
+ this._constant = json.constant;
+ this._name = utils.transformToFullName(json);
+ this._address = address;
+};
+
+/**
+ * Should be used to create payload from arguments
+ *
+ * @method toPayload
+ * @param {...} solidity function params
+ * @param {Object} optional payload options
+ */
+SolidityFunction.prototype.toPayload = function () {
+ var args = Array.prototype.slice.call(arguments);
+ var options = {};
+ if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) {
+ options = args.pop();
+ }
+ options.to = this._address;
+ options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args);
+ return options;
+};
+
+/**
+ * Should be used to get function signature
+ *
+ * @method signature
+ * @return {String} function signature
+ */
+SolidityFunction.prototype.signature = function () {
+ return web3.sha3(web3.fromAscii(this._name)).slice(2, 10);
+};
+
+/**
+ * Should be used to call function
+ *
+ * @method call
+ * @param {Object} options
+ * @return {String} output bytes
+ */
+SolidityFunction.prototype.call = function () {
+ var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments));
+ var output = web3.eth.call(payload);
+ output = output.length >= 2 ? output.slice(2) : output;
+ var result = coder.decodeParams(this._outputTypes, output);
+ return result.length === 1 ? result[0] : result;
+};
+
+/**
+ * Should be used to sendTransaction to solidity function
+ *
+ * @method sendTransaction
+ * @param {Object} options
+ */
+SolidityFunction.prototype.sendTransaction = function () {
+ var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments));
+ web3.eth.sendTransaction(payload);
+};
+
+/**
+ * Should be used to get function display name
+ *
+ * @method displayName
+ * @return {String} display name of the function
+ */
+SolidityFunction.prototype.displayName = function () {
+ return utils.extractDisplayName(this._name);
+};
+
+/**
+ * Should be used to get function type name
+ *
+ * @method typeName
+ * @return {String} type name of the function
+ */
+SolidityFunction.prototype.typeName = function () {
+ return utils.extractTypeName(this._name);
+};
+
+/**
+ * Should be called to execute function
+ *
+ * @method execute
+ */
+SolidityFunction.prototype.execute = function () {
+ var transaction = !this._constant;
+
+ // send transaction
+ if (transaction) {
+ return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments));
+ }
+
+ // call
+ return this.call.apply(this, Array.prototype.slice.call(arguments));
+};
+
+/**
+ * Should be called to attach function to contract
+ *
+ * @method attachToContract
+ * @param {Contract}
+ */
+SolidityFunction.prototype.attachToContract = function (contract) {
+ var execute = this.execute.bind(this);
+ execute.call = this.call.bind(this);
+ execute.sendTransaction = this.sendTransaction.bind(this);
+ var displayName = this.displayName();
+ if (!contract[displayName]) {
+ contract[displayName] = execute;
+ }
+ contract[displayName][this.typeName()] = execute; // circular!!!!
+};
+
+module.exports = SolidityFunction;
+
+
+},{"../solidity/coder":2,"../utils/utils":8,"../web3":10}],19:[function(require,module,exports){
+/*
+ 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 httpprovider.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * Marian Oancea <marian@ethdev.com>
+ * Fabian Vogelsteller <fabian@ethdev.com>
+ * @date 2014
+ */
+
+"use strict";
+
+var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
+var errors = require('./errors');
+
+var HttpProvider = function (host) {
+ this.host = host || 'http://localhost:8545';
+};
+
+HttpProvider.prototype.send = function (payload) {
+ var request = new XMLHttpRequest();
+
+ request.open('POST', this.host, false);
+
+ try {
+ request.send(JSON.stringify(payload));
+ } catch(error) {
+ throw errors.InvalidConnection(this.host);
+ }
+
+
+ // check request.status
+ // TODO: throw an error here! it cannot silently fail!!!
+ //if (request.status !== 200) {
+ //return;
+ //}
+ return JSON.parse(request.responseText);
+};
+
+HttpProvider.prototype.sendAsync = function (payload, callback) {
+ var request = new XMLHttpRequest();
+ request.onreadystatechange = function() {
+ if (request.readyState === 4) {
+ // TODO: handle the error properly here!!!
+ callback(null, JSON.parse(request.responseText));
+ }
+ };
+
+ request.open('POST', this.host, true);
+
+ try {
+ request.send(JSON.stringify(payload));
+ } catch(error) {
+ callback(errors.InvalidConnection(this.host));
+ }
+};
+
+module.exports = HttpProvider;
+
+
+},{"./errors":13,"xmlhttprequest":6}],20:[function(require,module,exports){
+/*
+ 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 jsonrpc.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var Jsonrpc = function () {
+ // singleton pattern
+ if (arguments.callee._singletonInstance) {
+ return arguments.callee._singletonInstance;
+ }
+ arguments.callee._singletonInstance = this;
+
+ this.messageId = 1;
+};
+
+/**
+ * @return {Jsonrpc} singleton
+ */
+Jsonrpc.getInstance = function () {
+ var instance = new Jsonrpc();
+ return instance;
+};
+
+/**
+ * Should be called to valid json create payload object
+ *
+ * @method toPayload
+ * @param {Function} method of jsonrpc call, required
+ * @param {Array} params, an array of method params, optional
+ * @returns {Object} valid jsonrpc payload object
+ */
+Jsonrpc.prototype.toPayload = function (method, params) {
+ if (!method)
+ console.error('jsonrpc method should be specified!');
+
+ return {
+ jsonrpc: '2.0',
+ method: method,
+ params: params || [],
+ id: this.messageId++
+ };
+};
+
+/**
+ * Should be called to check if jsonrpc response is valid
+ *
+ * @method isValidResponse
+ * @param {Object}
+ * @returns {Boolean} true if response is valid, otherwise false
+ */
+Jsonrpc.prototype.isValidResponse = function (response) {
+ return !!response &&
+ !response.error &&
+ response.jsonrpc === '2.0' &&
+ typeof response.id === 'number' &&
+ response.result !== undefined; // only undefined is not valid json object
+};
+
+/**
+ * Should be called to create batch payload object
+ *
+ * @method toBatchPayload
+ * @param {Array} messages, an array of objects with method (required) and params (optional) fields
+ * @returns {Array} batch payload
+ */
+Jsonrpc.prototype.toBatchPayload = function (messages) {
+ var self = this;
+ return messages.map(function (message) {
+ return self.toPayload(message.method, message.params);
+ });
+};
+
+module.exports = Jsonrpc;
+
+
+},{}],21:[function(require,module,exports){
+/*
+ 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 method.js
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var RequestManager = require('./requestmanager');
+var utils = require('../utils/utils');
+var errors = require('./errors');
+
+var Method = function (options) {
+ this.name = options.name;
+ this.call = options.call;
+ this.params = options.params || 0;
+ this.inputFormatter = options.inputFormatter;
+ this.outputFormatter = options.outputFormatter;
+};
+
+/**
+ * Should be used to determine name of the jsonrpc method based on arguments
+ *
+ * @method getCall
+ * @param {Array} arguments
+ * @return {String} name of jsonrpc method
+ */
+Method.prototype.getCall = function (args) {
+ return utils.isFunction(this.call) ? this.call(args) : this.call;
+};
+
+/**
+ * Should be used to extract callback from array of arguments. Modifies input param
+ *
+ * @method extractCallback
+ * @param {Array} arguments
+ * @return {Function|Null} callback, if exists
+ */
+Method.prototype.extractCallback = function (args) {
+ if (utils.isFunction(args[args.length - 1])) {
+ return args.pop(); // modify the args array!
+ }
+ return null;
+};
+
+/**
+ * Should be called to check if the number of arguments is correct
+ *
+ * @method validateArgs
+ * @param {Array} arguments
+ * @throws {Error} if it is not
+ */
+Method.prototype.validateArgs = function (args) {
+ if (args.length !== this.params) {
+ throw errors.InvalidNumberOfParams();
+ }
+};
+
+/**
+ * Should be called to format input args of method
+ *
+ * @method formatInput
+ * @param {Array}
+ * @return {Array}
+ */
+Method.prototype.formatInput = function (args) {
+ if (!this.inputFormatter) {
+ return args;
+ }
+
+ return this.inputFormatter.map(function (formatter, index) {
+ return formatter ? formatter(args[index]) : args[index];
+ });
+};
+
+/**
+ * Should be called to format output(result) of method
+ *
+ * @method formatOutput
+ * @param {Object}
+ * @return {Object}
+ */
+Method.prototype.formatOutput = function (result) {
+ return this.outputFormatter && result !== null ? this.outputFormatter(result) : result;
+};
+
+/**
+ * Should attach function to method
+ *
+ * @method attachToObject
+ * @param {Object}
+ * @param {Function}
+ */
+Method.prototype.attachToObject = function (obj) {
+ var func = this.send.bind(this);
+ func.call = this.call; // that's ugly. filter.js uses it
+ var name = this.name.split('.');
+ if (name.length > 1) {
+ obj[name[0]] = obj[name[0]] || {};
+ obj[name[0]][name[1]] = func;
+ } else {
+ obj[name[0]] = func;
+ }
+};
+
+/**
+ * Should create payload from given input args
+ *
+ * @method toPayload
+ * @param {Array} args
+ * @return {Object}
+ */
+Method.prototype.toPayload = function (args) {
+ var call = this.getCall(args);
+ var callback = this.extractCallback(args);
+ var params = this.formatInput(args);
+ this.validateArgs(params);
+
+ return {
+ method: call,
+ params: params,
+ callback: callback
+ };
+};
+
+/**
+ * Should send request to the API
+ *
+ * @method send
+ * @param list of params
+ * @return result
+ */
+Method.prototype.send = function () {
+ var payload = this.toPayload(Array.prototype.slice.call(arguments));
+ if (payload.callback) {
+ var self = this;
+ return RequestManager.getInstance().sendAsync(payload, function (err, result) {
+ payload.callback(null, self.formatOutput(result));
+ });
+ }
+ return this.formatOutput(RequestManager.getInstance().send(payload));
+};
+
+module.exports = Method;
+
+
+},{"../utils/utils":8,"./errors":13,"./requestmanager":25}],22:[function(require,module,exports){
+/*
+ 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 eth.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var utils = require('../utils/utils');
+var Property = require('./property');
+
+/// @returns an array of objects describing web3.eth api methods
+var methods = [
+];
+
+/// @returns an array of objects describing web3.eth api properties
+var properties = [
+ new Property({
+ name: 'listening',
+ getter: 'net_listening'
+ }),
+ new Property({
+ name: 'peerCount',
+ getter: 'net_peerCount',
+ outputFormatter: utils.toDecimal
+ })
+];
+
+
+module.exports = {
+ methods: methods,
+ properties: properties
+};
+
+
+},{"../utils/utils":8,"./property":23}],23:[function(require,module,exports){
+/*
+ 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 property.js
+ * @author Fabian Vogelsteller <fabian@frozeman.de>
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var RequestManager = require('./requestmanager');
+
+var Property = function (options) {
+ this.name = options.name;
+ this.getter = options.getter;
+ this.setter = options.setter;
+ this.outputFormatter = options.outputFormatter;
+ this.inputFormatter = options.inputFormatter;
+};
+
+/**
+ * Should be called to format input args of method
+ *
+ * @method formatInput
+ * @param {Array}
+ * @return {Array}
+ */
+Property.prototype.formatInput = function (arg) {
+ return this.inputFormatter ? this.inputFormatter(arg) : arg;
+};
+
+/**
+ * Should be called to format output(result) of method
+ *
+ * @method formatOutput
+ * @param {Object}
+ * @return {Object}
+ */
+Property.prototype.formatOutput = function (result) {
+ return this.outputFormatter && result !== null ? this.outputFormatter(result) : result;
+};
+
+/**
+ * Should attach function to method
+ *
+ * @method attachToObject
+ * @param {Object}
+ * @param {Function}
+ */
+Property.prototype.attachToObject = function (obj) {
+ var proto = {
+ get: this.get.bind(this),
+ set: this.set.bind(this)
+ };
+
+ var name = this.name.split('.');
+ if (name.length > 1) {
+ obj[name[0]] = obj[name[0]] || {};
+ Object.defineProperty(obj[name[0]], name[1], proto);
+ } else {
+ Object.defineProperty(obj, name[0], proto);
+ }
+};
+
+/**
+ * Should be used to get value of the property
+ *
+ * @method get
+ * @return {Object} value of the property
+ */
+Property.prototype.get = function () {
+ return this.formatOutput(RequestManager.getInstance().send({
+ method: this.getter
+ }));
+};
+
+/**
+ * Should be used to set value of the property
+ *
+ * @method set
+ * @param {Object} new value of the property
+ */
+Property.prototype.set = function (value) {
+ return RequestManager.getInstance().send({
+ method: this.setter,
+ params: [this.formatInput(value)]
+ });
+};
+
+module.exports = Property;
+
+
+},{"./requestmanager":25}],24:[function(require,module,exports){
+/*
+ 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) {
+ var result = navigator.qt.callMethod(JSON.stringify(payload));
+ return JSON.parse(result);
+};
+
+module.exports = QtSyncProvider;
+
+
+},{}],25:[function(require,module,exports){
+/*
+ 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 requestmanager.js
+ * @author Jeffrey Wilcke <jeff@ethdev.com>
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @author Marian Oancea <marian@ethdev.com>
+ * @author Fabian Vogelsteller <fabian@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ */
+
+var Jsonrpc = require('./jsonrpc');
+var utils = require('../utils/utils');
+var c = require('../utils/config');
+var errors = require('./errors');
+
+/**
+ * It's responsible for passing messages to providers
+ * It's also responsible for polling the ethereum node for incoming messages
+ * Default poll timeout is 1 second
+ * Singleton
+ */
+var RequestManager = function (provider) {
+ // singleton pattern
+ if (arguments.callee._singletonInstance) {
+ return arguments.callee._singletonInstance;
+ }
+ arguments.callee._singletonInstance = this;
+
+ this.provider = provider;
+ this.polls = [];
+ this.timeout = null;
+ this.poll();
+};
+
+/**
+ * @return {RequestManager} singleton
+ */
+RequestManager.getInstance = function () {
+ var instance = new RequestManager();
+ return instance;
+};
+
+/**
+ * Should be used to synchronously send request
+ *
+ * @method send
+ * @param {Object} data
+ * @return {Object}
+ */
+RequestManager.prototype.send = function (data) {
+ if (!this.provider) {
+ console.error(errors.InvalidProvider());
+ return null;
+ }
+
+ var payload = Jsonrpc.getInstance().toPayload(data.method, data.params);
+ var result = this.provider.send(payload);
+
+ if (!Jsonrpc.getInstance().isValidResponse(result)) {
+ throw errors.InvalidResponse(result);
+ }
+
+ return result.result;
+};
+
+/**
+ * Should be used to asynchronously send request
+ *
+ * @method sendAsync
+ * @param {Object} data
+ * @param {Function} callback
+ */
+RequestManager.prototype.sendAsync = function (data, callback) {
+ if (!this.provider) {
+ return callback(errors.InvalidProvider());
+ }
+
+ var payload = Jsonrpc.getInstance().toPayload(data.method, data.params);
+ this.provider.sendAsync(payload, function (err, result) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (!Jsonrpc.getInstance().isValidResponse(result)) {
+ return callback(errors.InvalidResponse(result));
+ }
+
+ callback(null, result.result);
+ });
+};
+
+/**
+ * Should be used to set provider of request manager
+ *
+ * @method setProvider
+ * @param {Object}
+ */
+RequestManager.prototype.setProvider = function (p) {
+ this.provider = p;
+};
+
+/*jshint maxparams:4 */
+
+/**
+ * Should be used to start polling
+ *
+ * @method startPolling
+ * @param {Object} data
+ * @param {Number} pollId
+ * @param {Function} callback
+ * @param {Function} uninstall
+ *
+ * @todo cleanup number of params
+ */
+RequestManager.prototype.startPolling = function (data, pollId, callback, uninstall) {
+ this.polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall});
+};
+/*jshint maxparams:3 */
+
+/**
+ * Should be used to stop polling for filter with given id
+ *
+ * @method stopPolling
+ * @param {Number} pollId
+ */
+RequestManager.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);
+ }
+ }
+};
+
+/**
+ * Should be called to reset polling mechanism of request manager
+ *
+ * @method reset
+ */
+RequestManager.prototype.reset = function () {
+ this.polls.forEach(function (poll) {
+ poll.uninstall(poll.id);
+ });
+ this.polls = [];
+
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ this.timeout = null;
+ }
+ this.poll();
+};
+
+/**
+ * Should be called to poll for changes on filter with given id
+ *
+ * @method poll
+ */
+RequestManager.prototype.poll = function () {
+ this.timeout = setTimeout(this.poll.bind(this), c.ETH_POLLING_TIMEOUT);
+
+ if (!this.polls.length) {
+ return;
+ }
+
+ if (!this.provider) {
+ console.error(errors.InvalidProvider());
+ return;
+ }
+
+ var payload = Jsonrpc.getInstance().toBatchPayload(this.polls.map(function (data) {
+ return data.data;
+ }));
+
+ var self = this;
+ this.provider.sendAsync(payload, function (error, results) {
+ // TODO: console log?
+ if (error) {
+ return;
+ }
+
+ if (!utils.isArray(results)) {
+ throw errors.InvalidResponse(results);
+ }
+
+ results.map(function (result, index) {
+ result.callback = self.polls[index].callback;
+ return result;
+ }).filter(function (result) {
+ var valid = Jsonrpc.getInstance().isValidResponse(result);
+ if (!valid) {
+ result.callback(errors.InvalidResponse(result));
+ }
+ return valid;
+ }).filter(function (result) {
+ return utils.isArray(result.result) && result.result.length > 0;
+ }).forEach(function (result) {
+ result.callback(null, result.result);
+ });
+ });
+};
+
+module.exports = RequestManager;
+
+
+},{"../utils/config":7,"../utils/utils":8,"./errors":13,"./jsonrpc":20}],26:[function(require,module,exports){
+/*
+ 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 shh.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var Method = require('./method');
+var formatters = require('./formatters');
+
+var post = new Method({
+ name: 'post',
+ call: 'shh_post',
+ params: 1,
+ inputFormatter: [formatters.inputPostFormatter]
+});
+
+var newIdentity = new Method({
+ name: 'newIdentity',
+ call: 'shh_newIdentity',
+ params: 0
+});
+
+var hasIdentity = new Method({
+ name: 'hasIdentity',
+ call: 'shh_hasIdentity',
+ params: 1
+});
+
+var newGroup = new Method({
+ name: 'newGroup',
+ call: 'shh_newGroup',
+ params: 0
+});
+
+var addToGroup = new Method({
+ name: 'addToGroup',
+ call: 'shh_addToGroup',
+ params: 0
+});
+
+var methods = [
+ post,
+ newIdentity,
+ hasIdentity,
+ newGroup,
+ addToGroup
+];
+
+module.exports = {
+ methods: methods
+};
+
+
+},{"./formatters":17,"./method":21}],27:[function(require,module,exports){
+/*
+ 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 watches.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * @date 2015
+ */
+
+var Method = require('./method');
+
+/// @returns an array of objects describing web3.eth.filter api methods
+var eth = function () {
+ var newFilterCall = function (args) {
+ return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter';
+ };
+
+ var newFilter = new Method({
+ name: 'newFilter',
+ call: newFilterCall,
+ params: 1
+ });
+
+ var uninstallFilter = new Method({
+ name: 'uninstallFilter',
+ call: 'eth_uninstallFilter',
+ params: 1
+ });
+
+ var getLogs = new Method({
+ name: 'getLogs',
+ call: 'eth_getFilterLogs',
+ params: 1
+ });
+
+ var poll = new Method({
+ name: 'poll',
+ call: 'eth_getFilterChanges',
+ params: 1
+ });
+
+ return [
+ newFilter,
+ uninstallFilter,
+ getLogs,
+ poll
+ ];
+};
+
+/// @returns an array of objects describing web3.shh.watch api methods
+var shh = function () {
+ var newFilter = new Method({
+ name: 'newFilter',
+ call: 'shh_newFilter',
+ params: 1
+ });
+
+ var uninstallFilter = new Method({
+ name: 'uninstallFilter',
+ call: 'shh_uninstallFilter',
+ params: 1
+ });
+
+ var getLogs = new Method({
+ name: 'getLogs',
+ call: 'shh_getMessages',
+ params: 1
+ });
+
+ var poll = new Method({
+ name: 'poll',
+ call: 'shh_getFilterChanges',
+ params: 1
+ });
+
+ return [
+ newFilter,
+ uninstallFilter,
+ getLogs,
+ poll
+ ];
+};
+
+module.exports = {
+ eth: eth,
+ shh: shh
+};
+
+
+},{"./method":21}],28:[function(require,module,exports){
+
+},{}],"bignumber.js":[function(require,module,exports){
+'use strict';
+
+module.exports = BigNumber; // jshint ignore:line
+
+
+},{}],"web3":[function(require,module,exports){
+var web3 = require('./lib/web3');
+web3.providers.HttpProvider = require('./lib/web3/httpprovider');
+web3.providers.QtSyncProvider = require('./lib/web3/qtsync');
+web3.eth.contract = require('./lib/web3/contract');
+web3.abi = require('./lib/solidity/abi');
+
+// dont override global variable
+if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') {
+ window.web3 = web3;
+}
+
+module.exports = web3;
+
+
+},{"./lib/solidity/abi":1,"./lib/web3":10,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/qtsync":24}]},{},["web3"])
+
+
+//# sourceMappingURL=web3-light.js.map
+`
diff --git a/miner/agent.go b/miner/agent.go
index b2f89aaab..939f63fef 100644
--- a/miner/agent.go
+++ b/miner/agent.go
@@ -10,7 +10,7 @@ import (
"github.com/ethereum/go-ethereum/pow"
)
-type CpuMiner struct {
+type CpuAgent struct {
chMu sync.Mutex
c chan *types.Block
quit chan struct{}
@@ -21,8 +21,8 @@ type CpuMiner struct {
pow pow.PoW
}
-func NewCpuMiner(index int, pow pow.PoW) *CpuMiner {
- miner := &CpuMiner{
+func NewCpuAgent(index int, pow pow.PoW) *CpuAgent {
+ miner := &CpuAgent{
pow: pow,
index: index,
}
@@ -30,16 +30,16 @@ func NewCpuMiner(index int, pow pow.PoW) *CpuMiner {
return miner
}
-func (self *CpuMiner) Work() chan<- *types.Block { return self.c }
-func (self *CpuMiner) Pow() pow.PoW { return self.pow }
-func (self *CpuMiner) SetReturnCh(ch chan<- *types.Block) { self.returnCh = ch }
+func (self *CpuAgent) Work() chan<- *types.Block { return self.c }
+func (self *CpuAgent) Pow() pow.PoW { return self.pow }
+func (self *CpuAgent) SetReturnCh(ch chan<- *types.Block) { self.returnCh = ch }
-func (self *CpuMiner) Stop() {
+func (self *CpuAgent) Stop() {
close(self.quit)
close(self.quitCurrentOp)
}
-func (self *CpuMiner) Start() {
+func (self *CpuAgent) Start() {
self.quit = make(chan struct{})
self.quitCurrentOp = make(chan struct{}, 1)
self.c = make(chan *types.Block, 1)
@@ -47,7 +47,7 @@ func (self *CpuMiner) Start() {
go self.update()
}
-func (self *CpuMiner) update() {
+func (self *CpuAgent) update() {
out:
for {
select {
@@ -76,7 +76,7 @@ done:
}
}
-func (self *CpuMiner) mine(block *types.Block) {
+func (self *CpuAgent) mine(block *types.Block) {
glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index)
// Reset the channel
@@ -95,6 +95,6 @@ func (self *CpuMiner) mine(block *types.Block) {
}
}
-func (self *CpuMiner) GetHashRate() int64 {
+func (self *CpuAgent) GetHashRate() int64 {
return self.pow.GetHashrate()
}
diff --git a/miner/miner.go b/miner/miner.go
index d5ea9a146..09342e250 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -7,6 +7,8 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/pow"
)
@@ -21,16 +23,8 @@ type Miner struct {
pow pow.PoW
}
-func New(eth core.Backend, pow pow.PoW, minerThreads int) *Miner {
- // note: minerThreads is currently ignored because
- // ethash is not thread safe.
- miner := &Miner{eth: eth, pow: pow, worker: newWorker(common.Address{}, eth)}
- for i := 0; i < minerThreads; i++ {
- miner.worker.register(NewCpuMiner(i, pow))
- }
- miner.threads = minerThreads
-
- return miner
+func New(eth core.Backend, pow pow.PoW) *Miner {
+ return &Miner{eth: eth, pow: pow, worker: newWorker(common.Address{}, eth)}
}
func (self *Miner) Mining() bool {
@@ -46,13 +40,27 @@ func (m *Miner) SetGasPrice(price *big.Int) {
m.worker.gasPrice = price
}
-func (self *Miner) Start(coinbase common.Address) {
+func (self *Miner) Start(coinbase common.Address, threads int) {
+
self.mining = true
+
+ for i := 0; i < threads; i++ {
+ self.worker.register(NewCpuAgent(i, self.pow))
+ }
+ self.threads = threads
+
+ glog.V(logger.Info).Infof("Starting mining operation (CPU=%d TOT=%d)\n", threads, len(self.worker.agents))
+
self.worker.coinbase = coinbase
self.worker.start()
self.worker.commitNewWork()
}
+func (self *Miner) Stop() {
+ self.worker.stop()
+ self.mining = false
+}
+
func (self *Miner) Register(agent Agent) {
if self.mining {
agent.Start()
@@ -61,11 +69,6 @@ func (self *Miner) Register(agent Agent) {
self.worker.register(agent)
}
-func (self *Miner) Stop() {
- self.mining = false
- self.worker.stop()
-}
-
func (self *Miner) HashRate() int64 {
return self.worker.HashRate()
}
diff --git a/miner/remote_agent.go b/miner/remote_agent.go
index 87456cfec..80cc9053e 100644
--- a/miner/remote_agent.go
+++ b/miner/remote_agent.go
@@ -64,13 +64,13 @@ func (a *RemoteAgent) GetWork() [3]string {
res[0] = a.work.HashNoNonce().Hex()
seedHash, _ := ethash.GetSeedHash(a.currentWork.NumberU64())
- res[1] = common.Bytes2Hex(seedHash)
+ res[1] = common.BytesToHash(seedHash).Hex()
// Calculate the "target" to be returned to the external miner
n := big.NewInt(1)
n.Lsh(n, 255)
n.Div(n, a.work.Difficulty())
n.Lsh(n, 1)
- res[2] = common.Bytes2Hex(n.Bytes())
+ res[2] = common.BytesToHash(n.Bytes()).Hex()
}
return res
diff --git a/miner/worker.go b/miner/worker.go
index 22493c235..f737be507 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -7,6 +7,7 @@ import (
"sync"
"sync/atomic"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
@@ -20,15 +21,41 @@ import (
var jsonlogger = logger.NewJsonLogger()
+// Work holds the current work
+type Work struct {
+ Number uint64
+ Nonce uint64
+ MixDigest []byte
+ SeedHash []byte
+}
+
+// Agent can register themself with the worker
+type Agent interface {
+ Work() chan<- *types.Block
+ SetReturnCh(chan<- *types.Block)
+ Stop()
+ Start()
+ GetHashRate() int64
+}
+
+// environment is the workers current environment and holds
+// all of the current state information
type environment struct {
- totalUsedGas *big.Int
- state *state.StateDB
- coinbase *state.StateObject
- block *types.Block
- family *set.Set
- uncles *set.Set
+ totalUsedGas *big.Int // total gas usage in the cycle
+ state *state.StateDB // apply state changes here
+ coinbase *state.StateObject // the miner's account
+ block *types.Block // the new block
+ family *set.Set // family set (used for checking uncles)
+ uncles *set.Set // uncle set
+ remove *set.Set // tx which will be removed
+ tcount int // tx count in cycle
+ ignoredTransactors *set.Set
+ lowGasTransactors *set.Set
+ ownedAccounts *set.Set
+ lowGasTxs types.Transactions
}
+// env returns a new environment for the current cycle
func env(block *types.Block, eth core.Backend) *environment {
state := state.New(block.Root(), eth.StateDb())
env := &environment{
@@ -43,21 +70,7 @@ func env(block *types.Block, eth core.Backend) *environment {
return env
}
-type Work struct {
- Number uint64
- Nonce uint64
- MixDigest []byte
- SeedHash []byte
-}
-
-type Agent interface {
- Work() chan<- *types.Block
- SetReturnCh(chan<- *types.Block)
- Stop()
- Start()
- GetHashRate() int64
-}
-
+// worker is the main object which takes care of applying messages to the new state
type worker struct {
mu sync.Mutex
@@ -128,12 +141,12 @@ func (self *worker) start() {
self.mu.Lock()
defer self.mu.Unlock()
+ atomic.StoreInt32(&self.mining, 1)
+
// spin up agents
for _, agent := range self.agents {
agent.Start()
}
-
- atomic.StoreInt32(&self.mining, 1)
}
func (self *worker) stop() {
@@ -141,10 +154,16 @@ func (self *worker) stop() {
defer self.mu.Unlock()
if atomic.LoadInt32(&self.mining) == 1 {
+ var keep []Agent
// stop all agents
for _, agent := range self.agents {
agent.Stop()
+ // keep all that's not a cpu agent
+ if _, ok := agent.(*CpuAgent); !ok {
+ keep = append(keep, agent)
+ }
}
+ self.agents = keep
}
atomic.StoreInt32(&self.mining, 0)
@@ -174,8 +193,11 @@ out:
self.possibleUncles[ev.Block.Hash()] = ev.Block
self.uncleMu.Unlock()
case core.TxPreEvent:
+ // Apply transaction to the pending state if we're not mining
if atomic.LoadInt32(&self.mining) == 0 {
- self.commitNewWork()
+ self.mu.Lock()
+ self.commitTransactions(types.Transactions{ev.Tx})
+ self.mu.Unlock()
}
}
case <-self.quit:
@@ -241,19 +263,33 @@ func (self *worker) makeCurrent() {
}
block.Header().Extra = self.extra
- self.current = env(block, self.eth)
+ current := env(block, self.eth)
for _, ancestor := range self.chain.GetAncestors(block, 7) {
- self.current.family.Add(ancestor.Hash())
+ current.family.Add(ancestor.Hash())
}
+ accounts, _ := self.eth.AccountManager().Accounts()
+ // Keep track of transactions which return errors so they can be removed
+ current.remove = set.New()
+ current.tcount = 0
+ current.ignoredTransactors = set.New()
+ current.lowGasTransactors = set.New()
+ current.ownedAccounts = accountAddressesSet(accounts)
+
+ parent := self.chain.GetBlock(current.block.ParentHash())
+ current.coinbase.SetGasPool(core.CalcGasLimit(parent))
- parent := self.chain.GetBlock(self.current.block.ParentHash())
- self.current.coinbase.SetGasPool(core.CalcGasLimit(parent))
+ self.current = current
}
func (w *worker) setGasPrice(p *big.Int) {
w.mu.Lock()
defer w.mu.Unlock()
- w.gasPrice = p
+
+ // calculate the minimal gas price the miner accepts when sorting out transactions.
+ const pct = int64(90)
+ w.gasPrice = gasprice(p, pct)
+
+ w.mux.Post(core.GasPriceChanged{w.gasPrice})
}
func (self *worker) commitNewWork() {
@@ -265,68 +301,14 @@ func (self *worker) commitNewWork() {
defer self.currentMu.Unlock()
self.makeCurrent()
+ current := self.current
transactions := self.eth.TxPool().GetTransactions()
sort.Sort(types.TxByNonce{transactions})
- // Keep track of transactions which return errors so they can be removed
- var (
- remove = set.New()
- tcount = 0
- ignoredTransactors = set.New()
- )
-
- const pct = int64(90)
- // calculate the minimal gas price the miner accepts when sorting out transactions.
- minprice := gasprice(self.gasPrice, pct)
- for _, tx := range transactions {
- // We can skip err. It has already been validated in the tx pool
- from, _ := tx.From()
-
- // check if it falls within margin
- if tx.GasPrice().Cmp(minprice) < 0 {
- // ignore the transaction and transactor. We ignore the transactor
- // because nonce will fail after ignoring this transaction so there's
- // no point
- ignoredTransactors.Add(from)
- glog.V(logger.Info).Infof("transaction(%x) below gas price (<%d%% ask price). All sequential txs from this address(%x) will fail\n", tx.Hash().Bytes()[:4], pct, from[:4])
- continue
- }
-
- // Move on to the next transaction when the transactor is in ignored transactions set
- // This may occur when a transaction hits the gas limit. When a gas limit is hit and
- // the transaction is processed (that could potentially be included in the block) it
- // will throw a nonce error because the previous transaction hasn't been processed.
- // Therefor we need to ignore any transaction after the ignored one.
- if ignoredTransactors.Has(from) {
- continue
- }
-
- self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0)
-
- err := self.commitTransaction(tx)
- switch {
- case core.IsNonceErr(err) || core.IsInvalidTxErr(err):
- // Remove invalid transactions
- from, _ := tx.From()
-
- self.chain.TxState().RemoveNonce(from, tx.Nonce())
- remove.Add(tx.Hash())
-
- if glog.V(logger.Detail) {
- glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err)
- }
- case state.IsGasLimitErr(err):
- from, _ := tx.From()
- // ignore the transactor so no nonce errors will be thrown for this account
- // next time the worker is run, they'll be picked up again.
- ignoredTransactors.Add(from)
-
- glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4])
- default:
- tcount++
- }
- }
+ // commit transactions for this run
+ self.commitTransactions(transactions)
+ self.eth.TxPool().RemoveTransactions(current.lowGasTxs)
var (
uncles []*types.Header
@@ -352,7 +334,7 @@ func (self *worker) commitNewWork() {
// We only care about logging if we're actually mining
if atomic.LoadInt32(&self.mining) == 1 {
- glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles\n", self.current.block.Number(), tcount, len(uncles))
+ glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles\n", current.block.Number(), current.tcount, len(uncles))
}
for _, hash := range badUncles {
@@ -392,6 +374,71 @@ func (self *worker) commitUncle(uncle *types.Header) error {
return nil
}
+func (self *worker) commitTransactions(transactions types.Transactions) {
+ current := self.current
+
+ for _, tx := range transactions {
+ // We can skip err. It has already been validated in the tx pool
+ from, _ := tx.From()
+
+ // Check if it falls within margin. Txs from owned accounts are always processed.
+ if tx.GasPrice().Cmp(self.gasPrice) < 0 && !current.ownedAccounts.Has(from) {
+ // ignore the transaction and transactor. We ignore the transactor
+ // because nonce will fail after ignoring this transaction so there's
+ // no point
+ current.lowGasTransactors.Add(from)
+
+ glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(self.gasPrice), from[:4])
+ }
+
+ // Continue with the next transaction if the transaction sender is included in
+ // the low gas tx set. This will also remove the tx and all sequential transaction
+ // from this transactor
+ if current.lowGasTransactors.Has(from) {
+ // add tx to the low gas set. This will be removed at the end of the run
+ // owned accounts are ignored
+ if !current.ownedAccounts.Has(from) {
+ current.lowGasTxs = append(current.lowGasTxs, tx)
+ }
+ continue
+ }
+
+ // Move on to the next transaction when the transactor is in ignored transactions set
+ // This may occur when a transaction hits the gas limit. When a gas limit is hit and
+ // the transaction is processed (that could potentially be included in the block) it
+ // will throw a nonce error because the previous transaction hasn't been processed.
+ // Therefor we need to ignore any transaction after the ignored one.
+ if current.ignoredTransactors.Has(from) {
+ continue
+ }
+
+ self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0)
+
+ err := self.commitTransaction(tx)
+ switch {
+ case core.IsNonceErr(err) || core.IsInvalidTxErr(err):
+ // Remove invalid transactions
+ from, _ := tx.From()
+
+ self.chain.TxState().RemoveNonce(from, tx.Nonce())
+ current.remove.Add(tx.Hash())
+
+ if glog.V(logger.Detail) {
+ glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err)
+ }
+ case state.IsGasLimitErr(err):
+ from, _ := tx.From()
+ // ignore the transactor so no nonce errors will be thrown for this account
+ // next time the worker is run, they'll be picked up again.
+ current.ignoredTransactors.Add(from)
+
+ glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4])
+ default:
+ current.tcount++
+ }
+ }
+}
+
func (self *worker) commitTransaction(tx *types.Transaction) error {
snap := self.current.state.Copy()
receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true)
@@ -423,3 +470,11 @@ func gasprice(price *big.Int, pct int64) *big.Int {
p.Mul(p, big.NewInt(pct))
return p
}
+
+func accountAddressesSet(accounts []accounts.Account) *set.Set {
+ accountSet := set.New()
+ for _, account := range accounts {
+ accountSet.Add(account.Address)
+ }
+ return accountSet
+}
diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go
index a2bb503ff..f175835a8 100644
--- a/p2p/discover/udp_test.go
+++ b/p2p/discover/udp_test.go
@@ -9,7 +9,7 @@ import (
logpkg "log"
"net"
"os"
- "path"
+ "path/filepath"
"reflect"
"runtime"
"sync"
@@ -88,7 +88,7 @@ func (test *udpTest) waitPacketOut(validate interface{}) error {
func (test *udpTest) errorf(format string, args ...interface{}) error {
_, file, line, ok := runtime.Caller(2) // errorf + waitPacketOut
if ok {
- file = path.Base(file)
+ file = filepath.Base(file)
} else {
file = "???"
line = 1
diff --git a/rpc/api.go b/rpc/api.go
index 6ba0d93e2..066c81222 100644
--- a/rpc/api.go
+++ b/rpc/api.go
@@ -158,6 +158,17 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
*reply = newHexData(v)
+ case "eth_sign":
+ args := new(NewSigArgs)
+ if err := json.Unmarshal(req.Params, &args); err != nil {
+ return err
+ }
+ v, err := api.xeth().Sign(args.From, args.Data, false)
+ if err != nil {
+ return err
+ }
+ *reply = v
+
case "eth_sendTransaction", "eth_transact":
args := new(NewTxArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
@@ -175,16 +186,24 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err
}
*reply = v
- case "eth_call":
- args := new(CallArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
+ case "eth_estimateGas":
+ _, gas, err := api.doCall(req.Params)
+ if err != nil {
return err
}
- v, err := api.xethAtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
+ // TODO unwrap the parent method's ToHex call
+ if len(gas) == 0 {
+ *reply = newHexNum(0)
+ } else {
+ *reply = newHexNum(gas)
+ }
+ case "eth_call":
+ v, _, err := api.doCall(req.Params)
if err != nil {
return err
}
+
// TODO unwrap the parent method's ToHex call
if v == "0x0" {
*reply = newHexData([]byte{})
@@ -380,7 +399,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
}
*reply = NewLogsRes(api.xeth().AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics))
case "eth_getWork":
- api.xeth().SetMining(true)
+ api.xeth().SetMining(true, 0)
*reply = api.xeth().RemoteMining().GetWork()
case "eth_submitWork":
args := new(SubmitWorkArgs)
@@ -439,10 +458,18 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = newHexData(res)
case "shh_version":
+ // Short circuit if whisper is not running
+ if api.xeth().Whisper() == nil {
+ return NewNotAvailableError(req.Method, "whisper offline")
+ }
// Retrieves the currently running whisper protocol version
*reply = api.xeth().WhisperVersion()
case "shh_post":
+ // Short circuit if whisper is not running
+ if api.xeth().Whisper() == nil {
+ return NewNotAvailableError(req.Method, "whisper offline")
+ }
// Injects a new message into the whisper network
args := new(WhisperMessageArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
@@ -455,10 +482,18 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = true
case "shh_newIdentity":
+ // Short circuit if whisper is not running
+ if api.xeth().Whisper() == nil {
+ return NewNotAvailableError(req.Method, "whisper offline")
+ }
// Creates a new whisper identity to use for sending/receiving messages
*reply = api.xeth().Whisper().NewIdentity()
case "shh_hasIdentity":
+ // Short circuit if whisper is not running
+ if api.xeth().Whisper() == nil {
+ return NewNotAvailableError(req.Method, "whisper offline")
+ }
// Checks if an identity if owned or not
args := new(WhisperIdentityArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
@@ -467,6 +502,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = api.xeth().Whisper().HasIdentity(args.Identity)
case "shh_newFilter":
+ // Short circuit if whisper is not running
+ if api.xeth().Whisper() == nil {
+ return NewNotAvailableError(req.Method, "whisper offline")
+ }
// Create a new filter to watch and match messages with
args := new(WhisperFilterArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
@@ -476,6 +515,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = newHexNum(big.NewInt(int64(id)).Bytes())
case "shh_uninstallFilter":
+ // Short circuit if whisper is not running
+ if api.xeth().Whisper() == nil {
+ return NewNotAvailableError(req.Method, "whisper offline")
+ }
// Remove an existing filter watching messages
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
@@ -484,6 +527,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = api.xeth().UninstallWhisperFilter(args.Id)
case "shh_getFilterChanges":
+ // Short circuit if whisper is not running
+ if api.xeth().Whisper() == nil {
+ return NewNotAvailableError(req.Method, "whisper offline")
+ }
// Retrieve all the new messages arrived since the last request
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
@@ -492,12 +539,17 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
*reply = api.xeth().WhisperMessagesChanged(args.Id)
case "shh_getMessages":
+ // Short circuit if whisper is not running
+ if api.xeth().Whisper() == nil {
+ return NewNotAvailableError(req.Method, "whisper offline")
+ }
// Retrieve all the cached messages matching a specific, existing filter
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
*reply = api.xeth().WhisperMessages(args.Id)
+
case "eth_hashrate":
*reply = newHexNum(api.xeth().HashRate())
@@ -527,3 +579,12 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
glog.V(logger.Detail).Infof("Reply: %T %s\n", reply, reply)
return nil
}
+
+func (api *EthereumApi) doCall(params json.RawMessage) (string, string, error) {
+ args := new(CallArgs)
+ if err := json.Unmarshal(params, &args); err != nil {
+ return "", "", err
+ }
+
+ return api.xethAtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
+}
diff --git a/rpc/api_test.go b/rpc/api_test.go
index c6489557c..b49e27bd1 100644
--- a/rpc/api_test.go
+++ b/rpc/api_test.go
@@ -31,6 +31,7 @@ func TestWeb3Sha3(t *testing.T) {
}
func TestCompileSolidity(t *testing.T) {
+ t.Skip()
solc, err := compiler.New("")
if solc == nil {
@@ -45,7 +46,7 @@ func TestCompileSolidity(t *testing.T) {
jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}`
- expCode := "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056"
+ //expCode := "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056"
expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]`
expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}`
expDeveloperDoc := `{"methods":{}}`
@@ -75,9 +76,11 @@ func TestCompileSolidity(t *testing.T) {
t.Errorf("expected no error, got %v", err)
}
- if contract.Code != expCode {
- t.Errorf("Expected %s got %s", expCode, contract.Code)
- }
+ /*
+ if contract.Code != expCode {
+ t.Errorf("Expected %s got %s", expCode, contract.Code)
+ }
+ */
if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` {
t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source))
}
diff --git a/rpc/args.go b/rpc/args.go
index 58a750415..686872a59 100644
--- a/rpc/args.go
+++ b/rpc/args.go
@@ -166,6 +166,46 @@ type NewTxArgs struct {
BlockNumber int64
}
+type NewSigArgs struct {
+ From string
+ Data string
+}
+
+func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) {
+ var obj []json.RawMessage
+ var ext struct {
+ From string
+ Data string
+ }
+
+ // Decode byte slice to array of RawMessages
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return NewDecodeParamError(err.Error())
+ }
+
+ // Check for sufficient params
+ if len(obj) < 1 {
+ return NewInsufficientParamsError(len(obj), 1)
+ }
+
+ // Decode 0th RawMessage to temporary struct
+ if err := json.Unmarshal(obj[0], &ext); err != nil {
+ return NewDecodeParamError(err.Error())
+ }
+
+ if len(ext.From) == 0 {
+ return NewValidationError("from", "is required")
+ }
+
+ if len(ext.Data) == 0 {
+ return NewValidationError("data", "is required")
+ }
+
+ args.From = ext.From
+ args.Data = ext.Data
+ return nil
+}
+
func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
var obj []json.RawMessage
var ext struct {
diff --git a/rpc/http.go b/rpc/http.go
index 4760601d8..c5bb10c80 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -116,7 +116,7 @@ func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} {
switch reserr.(type) {
case nil:
response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply}
- case *NotImplementedError:
+ case *NotImplementedError, *NotAvailableError:
jsonerr := &RpcErrorObject{-32601, reserr.Error()}
response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:
diff --git a/rpc/jeth.go b/rpc/jeth.go
index 4739316b2..2097ac30d 100644
--- a/rpc/jeth.go
+++ b/rpc/jeth.go
@@ -2,7 +2,7 @@ package rpc
import (
"encoding/json"
-
+ "fmt"
"github.com/ethereum/go-ethereum/jsre"
"github.com/robertkrimen/otto"
)
@@ -35,7 +35,6 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
}
jsonreq, err := json.Marshal(reqif)
-
var reqs []RpcRequest
batch := true
err = json.Unmarshal(jsonreq, &reqs)
@@ -52,6 +51,7 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
var respif interface{}
err = self.ethApi.GetRequestReply(&req, &respif)
if err != nil {
+ fmt.Println("Error response:", err)
return self.err(call, -32603, err.Error(), req.Id)
}
call.Otto.Set("ret_jsonrpc", jsonrpcver)
diff --git a/rpc/types.go b/rpc/types.go
index 1784759a4..1f49a3dea 100644
--- a/rpc/types.go
+++ b/rpc/types.go
@@ -18,6 +18,7 @@ package rpc
import (
"encoding/binary"
+ "encoding/hex"
"encoding/json"
"fmt"
"math/big"
@@ -117,7 +118,13 @@ func newHexData(input interface{}) *hexdata {
binary.BigEndian.PutUint32(buff, input)
d.data = buff
case string: // hexstring
- d.data = common.Big(input).Bytes()
+ // aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded
+ bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
+ if err != nil {
+ d.isNil = true
+ } else {
+ d.data = bytes
+ }
default:
d.isNil = true
}
@@ -209,6 +216,22 @@ func NewNotImplementedError(method string) *NotImplementedError {
}
}
+type NotAvailableError struct {
+ Method string
+ Reason string
+}
+
+func (e *NotAvailableError) Error() string {
+ return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason)
+}
+
+func NewNotAvailableError(method string, reason string) *NotAvailableError {
+ return &NotAvailableError{
+ Method: method,
+ Reason: reason,
+ }
+}
+
type DecodeParamError struct {
err string
}
diff --git a/tests/block_test.go b/tests/block_test.go
index e72f2b548..0ba0aefa2 100644
--- a/tests/block_test.go
+++ b/tests/block_test.go
@@ -1,7 +1,7 @@
package tests
import (
- "path"
+ "path/filepath"
"testing"
"github.com/ethereum/go-ethereum/accounts"
@@ -99,7 +99,7 @@ func runBlockTest(name string, test *BlockTest, t *testing.T) {
}
func testEthConfig() *eth.Config {
- ks := crypto.NewKeyStorePassphrase(path.Join(common.DefaultDataDir(), "keys"))
+ ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"))
return &eth.Config{
DataDir: common.DefaultDataDir(),
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index 093c9be0c..ae2ae4033 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -113,7 +113,7 @@ func (t *BlockTest) InsertPreState(ethereum *eth.Ethereum) (*state.StateDB, erro
if acct.PrivateKey != "" {
privkey, err := hex.DecodeString(strings.TrimPrefix(acct.PrivateKey, "0x"))
err = crypto.ImportBlockTestKey(privkey)
- err = ethereum.AccountManager().TimedUnlock(addr, "", 999999*time.Second)
+ err = ethereum.AccountManager().TimedUnlock(common.BytesToAddress(addr), "", 999999*time.Second)
if err != nil {
return nil, err
}
diff --git a/update-license.go b/update-license.go
index e55732a53..ea7ab67c5 100644
--- a/update-license.go
+++ b/update-license.go
@@ -26,7 +26,7 @@ import (
"io/ioutil"
"os"
"os/exec"
- "path"
+ "path/filepath"
"regexp"
"runtime"
"sort"
@@ -144,7 +144,7 @@ func getFiles(out chan<- string) {
return
}
}
- ext := path.Ext(line)
+ ext := filepath.Ext(line)
for _, wantExt := range extensions {
if ext == wantExt {
goto send
diff --git a/xeth/xeth.go b/xeth/xeth.go
index ad8596803..0fe68d175 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -79,7 +79,6 @@ func New(eth *eth.Ethereum, frontend Frontend) *XEth {
xeth := &XEth{
backend: eth,
frontend: frontend,
- whisper: NewWhisper(eth.Whisper()),
quit: make(chan struct{}),
filterManager: filter.NewFilterManager(eth.EventMux()),
logQueue: make(map[int]*logQueue),
@@ -88,6 +87,9 @@ func New(eth *eth.Ethereum, frontend Frontend) *XEth {
messages: make(map[int]*whisperFilter),
agent: miner.NewRemoteAgent(),
}
+ if eth.Whisper() != nil {
+ xeth.whisper = NewWhisper(eth.Whisper())
+ }
eth.Miner().Register(xeth.agent)
if frontend == nil {
xeth.frontend = dummyFrontend{}
@@ -363,7 +365,7 @@ func (self *XEth) Accounts() []string {
accounts, _ := self.backend.AccountManager().Accounts()
accountAddresses := make([]string, len(accounts))
for i, ac := range accounts {
- accountAddresses[i] = common.ToHex(ac.Address)
+ accountAddresses[i] = ac.Address.Hex()
}
return accountAddresses
}
@@ -423,10 +425,10 @@ func (self *XEth) ClientVersion() string {
return self.backend.ClientVersion()
}
-func (self *XEth) SetMining(shouldmine bool) bool {
+func (self *XEth) SetMining(shouldmine bool, threads int) bool {
ismining := self.backend.IsMining()
if shouldmine && !ismining {
- err := self.backend.StartMining()
+ err := self.backend.StartMining(threads)
return err == nil
}
if ismining && !shouldmine {
@@ -771,7 +773,7 @@ func (self *XEth) PushTx(encodedTx string) (string, error) {
return tx.Hash().Hex(), nil
}
-func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
+func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
statedb := self.State().State() //self.eth.ChainManager().TransState()
var from *state.StateObject
if len(fromStr) == 0 {
@@ -779,12 +781,13 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
if err != nil || len(accounts) == 0 {
from = statedb.GetOrNewStateObject(common.Address{})
} else {
- from = statedb.GetOrNewStateObject(common.BytesToAddress(accounts[0].Address))
+ from = statedb.GetOrNewStateObject(accounts[0].Address)
}
} else {
from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr))
}
+ from.SetGasPool(self.backend.ChainManager().GasLimit())
msg := callmsg{
from: from,
to: common.HexToAddress(toStr),
@@ -805,14 +808,43 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
block := self.CurrentBlock()
vmenv := core.NewEnv(statedb, self.backend.ChainManager(), msg, block)
- res, err := vmenv.Call(msg.from, msg.to, msg.data, msg.gas, msg.gasPrice, msg.value)
- return common.ToHex(res), err
+ res, gas, err := core.ApplyMessage(vmenv, msg, from)
+ return common.ToHex(res), gas.String(), err
}
func (self *XEth) ConfirmTransaction(tx string) bool {
return self.frontend.ConfirmTransaction(tx)
}
+func (self *XEth) doSign(from common.Address, hash common.Hash, didUnlock bool) ([]byte, error) {
+ sig, err := self.backend.AccountManager().Sign(accounts.Account{Address: from}, hash.Bytes())
+ if err == accounts.ErrLocked {
+ if didUnlock {
+ return nil, fmt.Errorf("signer account still locked after successful unlock")
+ }
+ if !self.frontend.UnlockAccount(from.Bytes()) {
+ return nil, fmt.Errorf("could not unlock signer account")
+ }
+ // retry signing, the account should now be unlocked.
+ return self.doSign(from, hash, true)
+ } else if err != nil {
+ return nil, err
+ }
+ return sig, nil
+}
+
+func (self *XEth) Sign(fromStr, hashStr string, didUnlock bool) (string, error) {
+ var (
+ from = common.HexToAddress(fromStr)
+ hash = common.HexToHash(hashStr)
+ )
+ sig, err := self.doSign(from, hash, didUnlock)
+ if err != nil {
+ return "", err
+ }
+ return common.ToHex(sig), nil
+}
+
func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
// this minimalistic recoding is enough (works for natspec.js)
@@ -905,17 +937,9 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
}
func (self *XEth) sign(tx *types.Transaction, from common.Address, didUnlock bool) error {
- sig, err := self.backend.AccountManager().Sign(accounts.Account{Address: from.Bytes()}, tx.Hash().Bytes())
- if err == accounts.ErrLocked {
- if didUnlock {
- return fmt.Errorf("sender account still locked after successful unlock")
- }
- if !self.frontend.UnlockAccount(from.Bytes()) {
- return fmt.Errorf("could not unlock sender account")
- }
- // retry signing, the account should now be unlocked.
- return self.sign(tx, from, true)
- } else if err != nil {
+ hash := tx.Hash()
+ sig, err := self.doSign(from, hash, didUnlock)
+ if err != nil {
return err
}
tx.SetSignatureValues(sig)