From 40adb7feb657cd1cb2e4c7a02c8a9db95b18e67c Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 23 Feb 2015 11:28:20 +0100 Subject: [PATCH 01/36] Implement OS sensitive dataDirs --- accounts/accounts_test.go | 3 ++- cmd/ethereum/flags.go | 11 +++-------- cmd/mist/flags.go | 10 +++------- crypto/key_store_plain.go | 7 ------- crypto/key_store_test.go | 9 +++++---- ethutil/common.go | 12 ++++++++++++ 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index 30e8c6285..127334404 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -5,10 +5,11 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/randentropy" + "github.com/ethereum/go-ethereum/ethutil" ) func TestAccountManager(t *testing.T) { - ks := crypto.NewKeyStorePlain(crypto.DefaultDataDir()) + ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir()) am := NewAccountManager(ks) pass := "" // not used but required by API a1, err := am.NewAccount(pass) diff --git a/cmd/ethereum/flags.go b/cmd/ethereum/flags.go index 577bee442..7d410c8e4 100644 --- a/cmd/ethereum/flags.go +++ b/cmd/ethereum/flags.go @@ -26,10 +26,10 @@ import ( "fmt" "log" "os" - "os/user" "path" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/vm" @@ -79,12 +79,7 @@ var ( InputFile string ) -func defaultDataDir() string { - usr, _ := user.Current() - return path.Join(usr.HomeDir, ".ethereum") -} - -var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini") +var defaultConfigFile = path.Join(ethutil.DefaultDataDir(), "conf.ini") func Init() { // TODO: move common flag processing to cmd/util @@ -107,7 +102,7 @@ func Init() { flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)") flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given") flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)") - flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use") + flag.StringVar(&Datadir, "datadir", ethutil.DefaultDataDir(), "specifies the datadir to use") flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file") flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)") flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)") diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go index d9487de9e..d5ed60a21 100644 --- a/cmd/mist/flags.go +++ b/cmd/mist/flags.go @@ -26,13 +26,13 @@ import ( "fmt" "log" "os" - "os/user" "path" "path/filepath" "runtime" "bitbucket.org/kardianos/osext" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/vm" @@ -94,12 +94,8 @@ func defaultAssetPath() string { } return assetPath } -func defaultDataDir() string { - usr, _ := user.Current() - return path.Join(usr.HomeDir, ".ethereum") -} -var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini") +var defaultConfigFile = path.Join(ethutil.DefaultDataDir(), "conf.ini") func Init() { // TODO: move common flag processing to cmd/utils @@ -121,7 +117,7 @@ func Init() { flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)") flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given") flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)") - flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use") + flag.StringVar(&Datadir, "datadir", ethutil.DefaultDataDir(), "specifies the datadir to use") flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file") flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)") flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)") diff --git a/crypto/key_store_plain.go b/crypto/key_store_plain.go index 6b76962a0..255ae0ed7 100644 --- a/crypto/key_store_plain.go +++ b/crypto/key_store_plain.go @@ -30,7 +30,6 @@ import ( "io" "io/ioutil" "os" - "os/user" "path" ) @@ -48,12 +47,6 @@ type keyStorePlain struct { keysDirPath string } -// TODO: copied from cmd/ethereum/flags.go -func DefaultDataDir() string { - usr, _ := user.Current() - return path.Join(usr.HomeDir, ".ethereum") -} - func NewKeyStorePlain(path string) KeyStore2 { return &keyStorePlain{path} } diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go index 485d8f536..11531d460 100644 --- a/crypto/key_store_test.go +++ b/crypto/key_store_test.go @@ -2,12 +2,13 @@ package crypto import ( "github.com/ethereum/go-ethereum/crypto/randentropy" + "github.com/ethereum/go-ethereum/ethutil" "reflect" "testing" ) func TestKeyStorePlain(t *testing.T) { - ks := NewKeyStorePlain(DefaultDataDir()) + ks := NewKeyStorePlain(ethutil.DefaultDataDir()) pass := "" // not used but required by API k1, err := ks.GenerateNewKey(randentropy.Reader, pass) if err != nil { @@ -35,7 +36,7 @@ func TestKeyStorePlain(t *testing.T) { } func TestKeyStorePassphrase(t *testing.T) { - ks := NewKeyStorePassphrase(DefaultDataDir()) + ks := NewKeyStorePassphrase(ethutil.DefaultDataDir()) pass := "foo" k1, err := ks.GenerateNewKey(randentropy.Reader, pass) if err != nil { @@ -61,7 +62,7 @@ func TestKeyStorePassphrase(t *testing.T) { } func TestKeyStorePassphraseDecryptionFail(t *testing.T) { - ks := NewKeyStorePassphrase(DefaultDataDir()) + ks := NewKeyStorePassphrase(ethutil.DefaultDataDir()) pass := "foo" k1, err := ks.GenerateNewKey(randentropy.Reader, pass) if err != nil { @@ -89,7 +90,7 @@ func TestImportPreSaleKey(t *testing.T) { // python pyethsaletool.py genwallet // with password "foo" fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}" - ks := NewKeyStorePassphrase(DefaultDataDir()) + ks := NewKeyStorePassphrase(ethutil.DefaultDataDir()) pass := "foo" _, err := ImportPreSaleKey(ks, []byte(fileContent), pass) if err != nil { diff --git a/ethutil/common.go b/ethutil/common.go index 2ef2440c7..efc519732 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -3,10 +3,22 @@ package ethutil import ( "fmt" "math/big" + "os/user" + "path" "runtime" "time" ) +func DefaultDataDir() string { + usr, _ := user.Current() + if runtime.GOOS == "darwin" { + return path.Join(usr.HomeDir, "Library/Ethereum") + } else if runtime.GOOS == "windows" { + return path.Join(usr.HomeDir, "AppData/Roaming/Ethereum") + } else { + return path.Join(usr.HomeDir, ".ethereum") + } +} func IsWindows() bool { return runtime.GOOS == "windows" } From 6fd894aae00c8c0f9f33eae7353dd9afd97f4fa1 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 23 Feb 2015 11:54:23 +0100 Subject: [PATCH 02/36] Allow zero and negative block numbers 0 is genesis block. Xeth recognises -1 as current --- rpc/args.go | 7 ------- rpc/packages.go | 12 ++++-------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/rpc/args.go b/rpc/args.go index f730819fd..347f60410 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -22,13 +22,6 @@ func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) { return NewErrorResponse(ErrorDecodeArgs) } -func (obj *GetBlockArgs) requirements() error { - if obj.BlockNumber == 0 && obj.Hash == "" { - return NewErrorResponse("GetBlock requires either a block 'number' or a block 'hash' as argument") - } - return nil -} - type NewTxArgs struct { From string `json:"from"` To string `json:"to"` diff --git a/rpc/packages.go b/rpc/packages.go index b51bde7ce..2f694b823 100644 --- a/rpc/packages.go +++ b/rpc/packages.go @@ -177,15 +177,11 @@ func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error } func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { - return err - } - - if args.BlockNumber > 0 { - *reply = p.xeth.BlockByNumber(args.BlockNumber) - } else { + // This seems a bit precarious Maybe worth splitting to discrete functions + if len(args.Hash) > 0 { *reply = p.xeth.BlockByHash(args.Hash) + } else { + *reply = p.xeth.BlockByNumber(args.BlockNumber) } return nil } From 4142708d9d30a38c3071b895b2c42f8723830fb1 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 24 Feb 2015 08:51:58 +0100 Subject: [PATCH 03/36] Convert gas values to integers Fixes #370 --- rpc/packages.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/rpc/packages.go b/rpc/packages.go index b51bde7ce..4056494de 100644 --- a/rpc/packages.go +++ b/rpc/packages.go @@ -25,11 +25,6 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) -const ( - defaultGasPrice = "10000000000000" - defaultGas = "10000" -) - type EthereumApi struct { xeth *xeth.XEth quit chan struct{} @@ -45,17 +40,22 @@ type EthereumApi struct { register map[string][]*NewTxArgs db ethutil.Database + + defaultGasPrice *big.Int + defaultGas *big.Int } func NewEthereumApi(eth *xeth.XEth) *EthereumApi { db, _ := ethdb.NewLDBDatabase("dapps") api := &EthereumApi{ - xeth: eth, - quit: make(chan struct{}), - filterManager: filter.NewFilterManager(eth.Backend().EventMux()), - logs: make(map[int]*logFilter), - messages: make(map[int]*whisperFilter), - db: db, + xeth: eth, + quit: make(chan struct{}), + filterManager: filter.NewFilterManager(eth.Backend().EventMux()), + logs: make(map[int]*logFilter), + messages: make(map[int]*whisperFilter), + db: db, + defaultGasPrice: big.NewInt(10000000000000), + defaultGas: big.NewInt(10000), } go api.filterManager.Start() go api.start() @@ -192,11 +192,11 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { if len(args.Gas) == 0 { - args.Gas = defaultGas + args.Gas = p.defaultGas.String() } if len(args.GasPrice) == 0 { - args.GasPrice = defaultGasPrice + args.GasPrice = p.defaultGasPrice.String() } // TODO if no_private_key then @@ -525,7 +525,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } return p.AllLogs(args, reply) case "eth_gasPrice": - *reply = defaultGasPrice + *reply = "0x" + toHex(p.defaultGasPrice.Bytes()) return nil case "eth_register": args, err := req.ToRegisterArgs() From 3fcdafebc262f42bfa80fcffdeb322c37f6eacf3 Mon Sep 17 00:00:00 2001 From: Fabian Vogelsteller Date: Tue, 24 Feb 2015 16:18:27 +0100 Subject: [PATCH 04/36] topics fix --- rpc/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/util.go b/rpc/util.go index 00a418783..3e8ca3fef 100644 --- a/rpc/util.go +++ b/rpc/util.go @@ -82,7 +82,7 @@ type RpcServer interface { type Log struct { Address string `json:"address"` - Topic []string `json:"topics"` + Topic []string `json:"topic"` Data string `json:"data"` Number uint64 `json:"number"` } From 2a7ca69a8260b887a309e1c43a74b4e644c391fa Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 24 Feb 2015 16:46:43 +0100 Subject: [PATCH 05/36] Fix duplicate leading 0x --- rpc/packages.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/packages.go b/rpc/packages.go index 4056494de..e48005d89 100644 --- a/rpc/packages.go +++ b/rpc/packages.go @@ -525,7 +525,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } return p.AllLogs(args, reply) case "eth_gasPrice": - *reply = "0x" + toHex(p.defaultGasPrice.Bytes()) + *reply = toHex(p.defaultGasPrice.Bytes()) return nil case "eth_register": args, err := req.ToRegisterArgs() From 1e919a5e977f81b746568e50604b63d1dd393a6b Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 24 Feb 2015 17:32:11 +0100 Subject: [PATCH 06/36] Move amounts to global vars --- rpc/packages.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/rpc/packages.go b/rpc/packages.go index e48005d89..bf75d1ddb 100644 --- a/rpc/packages.go +++ b/rpc/packages.go @@ -25,6 +25,11 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) +var ( + defaultGasPrice = big.NewInt(10000000000000) + defaultGas = big.NewInt(10000) +) + type EthereumApi struct { xeth *xeth.XEth quit chan struct{} @@ -40,22 +45,17 @@ type EthereumApi struct { register map[string][]*NewTxArgs db ethutil.Database - - defaultGasPrice *big.Int - defaultGas *big.Int } func NewEthereumApi(eth *xeth.XEth) *EthereumApi { db, _ := ethdb.NewLDBDatabase("dapps") api := &EthereumApi{ - xeth: eth, - quit: make(chan struct{}), - filterManager: filter.NewFilterManager(eth.Backend().EventMux()), - logs: make(map[int]*logFilter), - messages: make(map[int]*whisperFilter), - db: db, - defaultGasPrice: big.NewInt(10000000000000), - defaultGas: big.NewInt(10000), + xeth: eth, + quit: make(chan struct{}), + filterManager: filter.NewFilterManager(eth.Backend().EventMux()), + logs: make(map[int]*logFilter), + messages: make(map[int]*whisperFilter), + db: db, } go api.filterManager.Start() go api.start() @@ -192,11 +192,11 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { if len(args.Gas) == 0 { - args.Gas = p.defaultGas.String() + args.Gas = defaultGas.String() } if len(args.GasPrice) == 0 { - args.GasPrice = p.defaultGasPrice.String() + args.GasPrice = defaultGasPrice.String() } // TODO if no_private_key then @@ -525,7 +525,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } return p.AllLogs(args, reply) case "eth_gasPrice": - *reply = toHex(p.defaultGasPrice.Bytes()) + *reply = toHex(defaultGasPrice.Bytes()) return nil case "eth_register": args, err := req.ToRegisterArgs() From 923950ccaaa4f9c1c0cebfbdd99fb0f16c47fd37 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 24 Feb 2015 18:03:10 +0100 Subject: [PATCH 07/36] Fix key store address hex decoding and accounts test Thanks to https://github.com/jaekwon for original fix! --- accounts/account_manager.go | 4 ++++ accounts/accounts_test.go | 14 +++++++++++++- crypto/key_store_plain.go | 7 +++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index da0bd8900..f7a7506ba 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -56,6 +56,10 @@ func NewAccountManager(keyStore crypto.KeyStore2) AccountManager { return *am } +func (am AccountManager) DeleteAccount(address []byte, auth string) error { + return am.keyStore.DeleteKey(address, auth) +} + func (am *AccountManager) Sign(fromAccount *Account, keyAuth string, toSign []byte) (signature []byte, err error) { key, err := am.keyStore.GetKey(fromAccount.Address, keyAuth) if err != nil { diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index 127334404..4e97de545 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -9,7 +9,7 @@ import ( ) func TestAccountManager(t *testing.T) { - ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir()) + ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir() + "/testaccounts") am := NewAccountManager(ks) pass := "" // not used but required by API a1, err := am.NewAccount(pass) @@ -18,4 +18,16 @@ func TestAccountManager(t *testing.T) { if err != nil { t.Fatal(err) } + + // Cleanup + accounts, err := am.Accounts() + if err != nil { + t.Fatal(err) + } + for _, account := range accounts { + err := am.DeleteAccount(account.Address, pass) + if err != nil { + t.Fatal(err) + } + } } diff --git a/crypto/key_store_plain.go b/crypto/key_store_plain.go index 255ae0ed7..338a4a2c3 100644 --- a/crypto/key_store_plain.go +++ b/crypto/key_store_plain.go @@ -119,8 +119,11 @@ func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) { } addresses = make([][]byte, len(fileInfos)) for i, fileInfo := range fileInfos { - addresses[i] = make([]byte, 40) - addresses[i] = []byte(fileInfo.Name()) + address, err := hex.DecodeString(fileInfo.Name()) + if err != nil { + continue + } + addresses[i] = address } return addresses, err } From 21becb0febdf11132a40ff69c6145d9babf8458d Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 24 Feb 2015 19:05:03 +0100 Subject: [PATCH 08/36] Cleanup RPC packcage --- rpc/args.go | 66 ++++++--------------- rpc/message.go | 150 ++++++++++++++++++++++-------------------------- rpc/packages.go | 5 +- 3 files changed, 90 insertions(+), 131 deletions(-) diff --git a/rpc/args.go b/rpc/args.go index 347f60410..e839da8bf 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -19,7 +19,7 @@ func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) { obj.Hash = argstr return } - return NewErrorResponse(ErrorDecodeArgs) + return errDecodeArgs } type NewTxArgs struct { @@ -57,7 +57,7 @@ func (obj *NewTxArgs) UnmarshalJSON(b []byte) (err error) { return } - return NewErrorResponse(ErrorDecodeArgs) + return errDecodeArgs } type PushTxArgs struct { @@ -70,12 +70,12 @@ func (obj *PushTxArgs) UnmarshalJSON(b []byte) (err error) { obj.Tx = arg0 return } - return NewErrorResponse(ErrorDecodeArgs) + return errDecodeArgs } func (a *PushTxArgs) requirementsPushTx() error { if a.Tx == "" { - return NewErrorResponse("PushTx requires a 'tx' as argument") + return NewErrorWithMessage(errArguments, "PushTx requires a 'tx' as argument") } return nil } @@ -86,14 +86,14 @@ type GetStorageArgs struct { func (obj *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { if err = json.Unmarshal(b, &obj.Address); err != nil { - return NewErrorResponse(ErrorDecodeArgs) + return errDecodeArgs } return } func (a *GetStorageArgs) requirements() error { if len(a.Address) == 0 { - return NewErrorResponse("GetStorageAt requires an 'address' value as argument") + return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument") } return nil } @@ -109,64 +109,39 @@ func (obj *GetStateArgs) UnmarshalJSON(b []byte) (err error) { obj.Address = arg0 return } - return NewErrorResponse(ErrorDecodeArgs) + return errDecodeArgs } func (a *GetStateArgs) requirements() error { if a.Address == "" { - return NewErrorResponse("GetStorageAt requires an 'address' value as argument") + return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument") } if a.Key == "" { - return NewErrorResponse("GetStorageAt requires an 'key' value as argument") + return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'key' value as argument") } return nil } -type GetStorageAtRes struct { - Key string `json:"key"` - Value string `json:"value"` -} - type GetTxCountArgs struct { Address string `json:"address"` } -// type GetTxCountRes struct { -// Nonce int `json:"nonce"` -// } - func (obj *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { arg0 := "" if err = json.Unmarshal(b, &arg0); err == nil { obj.Address = arg0 return } - return NewErrorResponse("Could not determine JSON parameters") + return errDecodeArgs } func (a *GetTxCountArgs) requirements() error { if a.Address == "" { - return NewErrorResponse("GetTxCountAt requires an 'address' value as argument") + return NewErrorWithMessage(errArguments, "GetTxCountAt requires an 'address' value as argument") } return nil } -// type GetPeerCountRes struct { -// PeerCount int `json:"peerCount"` -// } - -// type GetListeningRes struct { -// IsListening bool `json:"isListening"` -// } - -// type GetCoinbaseRes struct { -// Coinbase string `json:"coinbase"` -// } - -// type GetMiningRes struct { -// IsMining bool `json:"isMining"` -// } - type GetBalanceArgs struct { Address string } @@ -177,21 +152,16 @@ func (obj *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { obj.Address = arg0 return } - return NewErrorResponse("Could not determine JSON parameters") + return errDecodeArgs } func (a *GetBalanceArgs) requirements() error { if a.Address == "" { - return NewErrorResponse("GetBalanceAt requires an 'address' value as argument") + return NewErrorWithMessage(errArguments, "GetBalanceAt requires an 'address' value as argument") } return nil } -type BalanceRes struct { - Balance string `json:"balance"` - Address string `json:"address"` -} - type GetCodeAtArgs struct { Address string } @@ -202,12 +172,12 @@ func (obj *GetCodeAtArgs) UnmarshalJSON(b []byte) (err error) { obj.Address = arg0 return } - return NewErrorResponse(ErrorDecodeArgs) + return errDecodeArgs } func (a *GetCodeAtArgs) requirements() error { if a.Address == "" { - return NewErrorResponse("GetCodeAt requires an 'address' value as argument") + return NewErrorWithMessage(errArguments, "GetCodeAt requires an 'address' value as argument") } return nil } @@ -218,7 +188,7 @@ type Sha3Args struct { func (obj *Sha3Args) UnmarshalJSON(b []byte) (err error) { if err = json.Unmarshal(b, &obj.Data); err != nil { - return NewErrorResponse(ErrorDecodeArgs) + return errDecodeArgs } return } @@ -270,10 +240,10 @@ type DbArgs struct { func (a *DbArgs) requirements() error { if len(a.Database) == 0 { - return NewErrorResponse("DbPutArgs requires an 'Database' value as argument") + return NewErrorWithMessage(errArguments, "DbPutArgs requires an 'Database' value as argument") } if len(a.Key) == 0 { - return NewErrorResponse("DbPutArgs requires an 'Key' value as argument") + return NewErrorWithMessage(errArguments, "DbPutArgs requires an 'Key' value as argument") } return nil } diff --git a/rpc/message.go b/rpc/message.go index 825ede05b..18fbec931 100644 --- a/rpc/message.go +++ b/rpc/message.go @@ -25,12 +25,12 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) -const ( - ErrorArguments = "Error: Insufficient arguments" - ErrorNotImplemented = "Error: Method not implemented" - ErrorUnknown = "Error: Unknown error" - ErrorParseRequest = "Error: Could not parse request" - ErrorDecodeArgs = "Error: Could not decode arguments" +var ( + errArguments = errors.New("Error: Insufficient arguments") + errNotImplemented = errors.New("Error: Method not implemented") + errUnknown = errors.New("Error: Unknown error") + errParseRequest = errors.New("Error: Could not parse request") + errDecodeArgs = errors.New("Error: Could not decode arguments") ) type RpcRequest struct { @@ -58,76 +58,72 @@ type RpcErrorObject struct { // Data interface{} `json:"data"` } -func NewErrorResponse(msg string) error { - return errors.New(msg) -} - -func NewErrorResponseWithError(msg string, err error) error { - return fmt.Errorf("%s: %v", msg, err) +func NewErrorWithMessage(err error, msg string) error { + return fmt.Errorf("%s: %s", err.Error(), msg) } func (req *RpcRequest) ToSha3Args() (*Sha3Args, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(Sha3Args) r := bytes.NewReader(req.Params[0]) if err := json.NewDecoder(r).Decode(args); err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToGetBlockArgs() (*GetBlockArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(GetBlockArgs) r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToNewTxArgs() (*NewTxArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(NewTxArgs) r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToPushTxArgs() (*PushTxArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(PushTxArgs) r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToGetStateArgs() (*GetStateArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(GetStateArgs) @@ -135,234 +131,228 @@ func (req *RpcRequest) ToGetStateArgs() (*GetStateArgs, error) { r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToStorageAtArgs() (*GetStorageArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(GetStorageArgs) r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToGetTxCountArgs() (*GetTxCountArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(GetTxCountArgs) r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToGetBalanceArgs() (*GetBalanceArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(GetBalanceArgs) r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToGetCodeAtArgs() (*GetCodeAtArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(GetCodeAtArgs) r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToBoolArgs() (bool, error) { if len(req.Params) < 1 { - return false, NewErrorResponse(ErrorArguments) + return false, errArguments } var args bool err := json.Unmarshal(req.Params[0], &args) if err != nil { - return false, NewErrorResponse(ErrorDecodeArgs) + return false, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) return args, nil } func (req *RpcRequest) ToCompileArgs() (string, error) { if len(req.Params) < 1 { - return "", NewErrorResponse(ErrorArguments) + return "", errArguments } var args string err := json.Unmarshal(req.Params[0], &args) if err != nil { - return "", NewErrorResponse(ErrorDecodeArgs) + return "", errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) return args, nil } func (req *RpcRequest) ToFilterArgs() (*FilterOptions, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } args := new(FilterOptions) r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) return args, nil } func (req *RpcRequest) ToFilterStringArgs() (string, error) { if len(req.Params) < 1 { - return "", NewErrorResponse(ErrorArguments) + return "", errArguments } var args string err := json.Unmarshal(req.Params[0], &args) if err != nil { - return "", NewErrorResponse(ErrorDecodeArgs) + return "", errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) return args, nil } func (req *RpcRequest) ToUninstallFilterArgs() (int, error) { if len(req.Params) < 1 { - return 0, NewErrorResponse(ErrorArguments) + return 0, errArguments } var args int err := json.Unmarshal(req.Params[0], &args) if err != nil { - return 0, NewErrorResponse(ErrorDecodeArgs) + return 0, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", args, args) return args, nil } func (req *RpcRequest) ToFilterChangedArgs() (int, error) { if len(req.Params) < 1 { - return 0, NewErrorResponse(ErrorArguments) + return 0, errArguments } var id int r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(&id) if err != nil { - return 0, NewErrorResponse(ErrorDecodeArgs) + return 0, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", id, id) return id, nil } func (req *RpcRequest) ToDbPutArgs() (*DbArgs, error) { if len(req.Params) < 3 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } var args DbArgs err := json.Unmarshal(req.Params[0], &args.Database) if err != nil { - return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) } err = json.Unmarshal(req.Params[1], &args.Key) if err != nil { - return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) } err = json.Unmarshal(req.Params[2], &args.Value) if err != nil { - return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) } - rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil } func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) { if len(req.Params) < 2 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } var args DbArgs err := json.Unmarshal(req.Params[0], &args.Database) if err != nil { - return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) } err = json.Unmarshal(req.Params[1], &args.Key) if err != nil { - return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) } - rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil } func (req *RpcRequest) ToWhisperFilterArgs() (*xeth.Options, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } var args xeth.Options err := json.Unmarshal(req.Params[0], &args) if err != nil { - return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) } - rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil } func (req *RpcRequest) ToIdArgs() (int, error) { if len(req.Params) < 1 { - return 0, NewErrorResponse(ErrorArguments) + return 0, errArguments } var id int err := json.Unmarshal(req.Params[0], &id) if err != nil { - return 0, NewErrorResponse(ErrorDecodeArgs) + return 0, errDecodeArgs } - rpclogger.DebugDetailf("%T %v", id, id) + return id, nil } func (req *RpcRequest) ToWhisperPostArgs() (*WhisperMessageArgs, error) { if len(req.Params) < 1 { - return nil, NewErrorResponse(ErrorArguments) + return nil, errArguments } var args WhisperMessageArgs @@ -370,13 +360,13 @@ func (req *RpcRequest) ToWhisperPostArgs() (*WhisperMessageArgs, error) { if err != nil { return nil, err } - rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil } func (req *RpcRequest) ToWhisperHasIdentityArgs() (string, error) { if len(req.Params) < 1 { - return "", NewErrorResponse(ErrorArguments) + return "", errArguments } var args string @@ -384,13 +374,13 @@ func (req *RpcRequest) ToWhisperHasIdentityArgs() (string, error) { if err != nil { return "", err } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToRegisterArgs() (string, error) { if len(req.Params) < 1 { - return "", NewErrorResponse(ErrorArguments) + return "", errArguments } var args string @@ -398,13 +388,13 @@ func (req *RpcRequest) ToRegisterArgs() (string, error) { if err != nil { return "", err } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToWatchTxArgs() (string, error) { if len(req.Params) < 1 { - return "", NewErrorResponse(ErrorArguments) + return "", errArguments } var args string @@ -412,6 +402,6 @@ func (req *RpcRequest) ToWatchTxArgs() (string, error) { if err != nil { return "", err } - rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } diff --git a/rpc/packages.go b/rpc/packages.go index 0f00441d2..813fef949 100644 --- a/rpc/packages.go +++ b/rpc/packages.go @@ -9,7 +9,6 @@ For each request type, define the following: package rpc import ( - "fmt" "math/big" "strings" "sync" @@ -410,7 +409,7 @@ func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error { } func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { - // Spec at https://github.com/ethereum/wiki/wiki/Generic-ON-RPC + // Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC rpclogger.DebugDetailf("%T %s", req.Params, req.Params) switch req.Method { case "eth_coinbase": @@ -595,7 +594,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } return p.WhisperMessages(args, reply) default: - return NewErrorResponse(fmt.Sprintf("%v %s", ErrorNotImplemented, req.Method)) + return NewErrorWithMessage(errNotImplemented, req.Method) } rpclogger.DebugDetailf("Reply: %T %s", reply, reply) From 43acad81b3fbee608bf19e6ba32ba27b98d9a7ff Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 24 Feb 2015 19:11:01 +0100 Subject: [PATCH 09/36] Rename files --- rpc/{packages.go => api.go} | 0 rpc/{packages_test.go => api_test.go} | 0 rpc/{message.go => messages.go} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename rpc/{packages.go => api.go} (100%) rename rpc/{packages_test.go => api_test.go} (100%) rename rpc/{message.go => messages.go} (100%) diff --git a/rpc/packages.go b/rpc/api.go similarity index 100% rename from rpc/packages.go rename to rpc/api.go diff --git a/rpc/packages_test.go b/rpc/api_test.go similarity index 100% rename from rpc/packages_test.go rename to rpc/api_test.go diff --git a/rpc/message.go b/rpc/messages.go similarity index 100% rename from rpc/message.go rename to rpc/messages.go From c8e9abff53d869f74211e157abcb2827d181e106 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 24 Feb 2015 19:22:08 +0100 Subject: [PATCH 10/36] Remove errParseRequest --- rpc/http/server.go | 2 +- rpc/messages.go | 1 - rpc/ws/server.go | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rpc/http/server.go b/rpc/http/server.go index dd6ba68e3..fa66eed48 100644 --- a/rpc/http/server.go +++ b/rpc/http/server.go @@ -92,7 +92,7 @@ func (s *RpcHttpServer) apiHandler(api *rpc.EthereumApi) http.Handler { reqParsed, reqerr := JSON.ParseRequestBody(req) if reqerr != nil { - jsonerr := &rpc.RpcErrorObject{-32700, rpc.ErrorParseRequest} + jsonerr := &rpc.RpcErrorObject{-32700, "Error: Could not parse request"} JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) return } diff --git a/rpc/messages.go b/rpc/messages.go index 18fbec931..044f07545 100644 --- a/rpc/messages.go +++ b/rpc/messages.go @@ -29,7 +29,6 @@ var ( errArguments = errors.New("Error: Insufficient arguments") errNotImplemented = errors.New("Error: Method not implemented") errUnknown = errors.New("Error: Unknown error") - errParseRequest = errors.New("Error: Could not parse request") errDecodeArgs = errors.New("Error: Could not decode arguments") ) diff --git a/rpc/ws/server.go b/rpc/ws/server.go index b8cc2fa6b..2c2988f5d 100644 --- a/rpc/ws/server.go +++ b/rpc/ws/server.go @@ -99,7 +99,7 @@ func sockHandler(api *rpc.EthereumApi) websocket.Handler { // reqParsed, reqerr := JSON.ParseRequestBody(conn.Request()) if err := websocket.JSON.Receive(conn, &reqParsed); err != nil { - jsonerr := &rpc.RpcErrorObject{-32700, rpc.ErrorParseRequest} + jsonerr := &rpc.RpcErrorObject{-32700, "Error: Could not parse request"} JSON.Send(conn, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) continue } From 2e3a6e2559d02ba90957eaf333e571dec935e00a Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 24 Feb 2015 19:54:18 +0100 Subject: [PATCH 11/36] Consolidate related items --- rpc/api.go | 73 +++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 813fef949..21c85bbcc 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -25,8 +25,9 @@ import ( ) var ( - defaultGasPrice = big.NewInt(10000000000000) - defaultGas = big.NewInt(10000) + defaultGasPrice = big.NewInt(10000000000000) + defaultGas = big.NewInt(10000) + filterTickerTime = 15 * time.Second ) type EthereumApi struct { @@ -62,6 +63,39 @@ func NewEthereumApi(eth *xeth.XEth) *EthereumApi { return api } +func (self *EthereumApi) start() { + timer := time.NewTicker(filterTickerTime) +done: + for { + select { + case <-timer.C: + self.logMut.Lock() + self.messagesMut.Lock() + for id, filter := range self.logs { + if time.Since(filter.timeout) > 20*time.Second { + self.filterManager.UninstallFilter(id) + delete(self.logs, id) + } + } + + for id, filter := range self.messages { + if time.Since(filter.timeout) > 20*time.Second { + self.xeth.Whisper().Unwatch(id) + delete(self.messages, id) + } + } + self.logMut.Unlock() + self.messagesMut.Unlock() + case <-self.quit: + break done + } + } +} + +func (self *EthereumApi) stop() { + close(self.quit) +} + func (self *EthereumApi) Register(args string, reply *interface{}) error { self.regmut.Lock() defer self.regmut.Unlock() @@ -600,38 +634,3 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error rpclogger.DebugDetailf("Reply: %T %s", reply, reply) return nil } - -var filterTickerTime = 15 * time.Second - -func (self *EthereumApi) start() { - timer := time.NewTicker(filterTickerTime) -done: - for { - select { - case <-timer.C: - self.logMut.Lock() - self.messagesMut.Lock() - for id, filter := range self.logs { - if time.Since(filter.timeout) > 20*time.Second { - self.filterManager.UninstallFilter(id) - delete(self.logs, id) - } - } - - for id, filter := range self.messages { - if time.Since(filter.timeout) > 20*time.Second { - self.xeth.Whisper().Unwatch(id) - delete(self.messages, id) - } - } - self.logMut.Unlock() - self.messagesMut.Unlock() - case <-self.quit: - break done - } - } -} - -func (self *EthereumApi) stop() { - close(self.quit) -} From 9e0ab22dd01d677a853f1af2c865f8654081ca26 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 25 Feb 2015 09:57:49 +0100 Subject: [PATCH 12/36] Stub defaultBlock methods --- rpc/api.go | 21 +++++++++++++++++++++ rpc/messages.go | 13 +++++++++++++ 2 files changed, 34 insertions(+) diff --git a/rpc/api.go b/rpc/api.go index 21c85bbcc..908d37a80 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -45,6 +45,8 @@ type EthereumApi struct { register map[string][]*NewTxArgs db ethutil.Database + + defaultBlockAge int } func NewEthereumApi(eth *xeth.XEth) *EthereumApi { @@ -318,6 +320,17 @@ func (p *EthereumApi) SetMining(shouldmine bool, reply *interface{}) error { return nil } +func (p *EthereumApi) GetDefaultBlockAge(reply *interface{}) error { + *reply = p.defaultBlockAge + return nil +} + +func (p *EthereumApi) SetDefaultBlockAge(defaultBlockAge int, reply *interface{}) error { + p.defaultBlockAge = defaultBlockAge + *reply = true + return nil +} + func (p *EthereumApi) BlockNumber(reply *interface{}) error { *reply = p.xeth.Backend().ChainManager().CurrentBlock().Number() return nil @@ -458,6 +471,14 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.SetMining(args, reply) + case "eth_defaultBlock": + return p.GetDefaultBlockAge(reply) + case "eth_setDefaultBlock": + args, err := req.ToIntArgs() + if err != nil { + return err + } + return p.SetDefaultBlockAge(args, reply) case "eth_peerCount": return p.GetPeerCount(reply) case "eth_number": diff --git a/rpc/messages.go b/rpc/messages.go index 044f07545..b37d8229d 100644 --- a/rpc/messages.go +++ b/rpc/messages.go @@ -210,6 +210,19 @@ func (req *RpcRequest) ToBoolArgs() (bool, error) { return args, nil } +func (req *RpcRequest) ToIntArgs() (int, error) { + if len(req.Params) < 1 { + return 0, errArguments + } + + var args int + if err := json.Unmarshal(req.Params[0], &args); err != nil { + return 0, errArguments + } + + return args, nil +} + func (req *RpcRequest) ToCompileArgs() (string, error) { if len(req.Params) < 1 { return "", errArguments From ce2b2422d6efe9d8c4744a83bab8d80d9e94128d Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 25 Feb 2015 11:40:52 +0100 Subject: [PATCH 13/36] Update Go and Qt versions --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5499cf257..c4e39b05e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go go: - - 1.4.1 + - 1.4.2 before_install: - - sudo add-apt-repository ppa:beineri/opt-qt54 -y + - sudo add-apt-repository ppa:beineri/opt-qt541 -y - sudo apt-get update -qq - sudo apt-get install -yqq libgmp3-dev libreadline6-dev qt54quickcontrols qt54webengine install: From b296b36d2b2aaa2f81d26b3c133ace2714c58a7d Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 25 Feb 2015 17:29:23 +0100 Subject: [PATCH 14/36] Add automatic locking / unlocking of accounts * Change account signing API to two sign functions; Sign without passphrase - works if account is unlocked Sign with passphrase - always works and unlocks the account * Account stays unlocked for X ms and is then automatically locked --- accounts/account_manager.go | 48 ++++++++++++++++++++++++++------- accounts/accounts_test.go | 53 +++++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index f7a7506ba..4d63bd0f2 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -34,24 +34,33 @@ package accounts import ( crand "crypto/rand" + "errors" "github.com/ethereum/go-ethereum/crypto" + "sync" + "time" ) +var ErrLocked = errors.New("account is locked; please request passphrase") + // TODO: better name for this struct? type Account struct { Address []byte } type AccountManager struct { - keyStore crypto.KeyStore2 + keyStore crypto.KeyStore2 + unlockedKeys map[string]crypto.Key + unlockedMilliSeconds int + mutex sync.Mutex } -// TODO: get key by addr - modify KeyStore2 GetKey to work with addr - -// TODO: pass through passphrase for APIs which require access to private key? -func NewAccountManager(keyStore crypto.KeyStore2) AccountManager { +func NewAccountManager(keyStore crypto.KeyStore2, unlockMilliSeconds int) AccountManager { + keysMap := make(map[string]crypto.Key) am := &AccountManager{ - keyStore: keyStore, + keyStore: keyStore, + unlockedKeys: keysMap, + unlockedMilliSeconds: unlockMilliSeconds, + mutex: sync.Mutex{}, // for accessing unlockedKeys map } return *am } @@ -60,11 +69,26 @@ func (am AccountManager) DeleteAccount(address []byte, auth string) error { return am.keyStore.DeleteKey(address, auth) } -func (am *AccountManager) Sign(fromAccount *Account, keyAuth string, toSign []byte) (signature []byte, err error) { +func (am *AccountManager) Sign(fromAccount *Account, toSign []byte) (signature []byte, err error) { + am.mutex.Lock() + unlockedKey := am.unlockedKeys[string(fromAccount.Address)] + am.mutex.Unlock() + if unlockedKey.Address == nil { + return nil, ErrLocked + } + signature, err = crypto.Sign(toSign, unlockedKey.PrivateKey) + return signature, err +} + +func (am *AccountManager) SignLocked(fromAccount *Account, keyAuth string, toSign []byte) (signature []byte, err error) { key, err := am.keyStore.GetKey(fromAccount.Address, keyAuth) if err != nil { return nil, err } + am.mutex.Lock() + am.unlockedKeys[string(fromAccount.Address)] = *key + am.mutex.Unlock() + go unlockLater(am, fromAccount.Address) signature, err = crypto.Sign(toSign, key.PrivateKey) return signature, err } @@ -80,8 +104,6 @@ func (am AccountManager) NewAccount(auth string) (*Account, error) { return ua, err } -// set of accounts == set of keys in given key store -// TODO: do we need persistence of accounts as well? func (am *AccountManager) Accounts() ([]Account, error) { addresses, err := am.keyStore.GetKeyAddresses() if err != nil { @@ -97,3 +119,11 @@ func (am *AccountManager) Accounts() ([]Account, error) { } return accounts, err } + +func unlockLater(am *AccountManager, addr []byte) { + time.Sleep(time.Millisecond * time.Duration(am.unlockedMilliSeconds)) + am.mutex.Lock() + // TODO: how do we know the key is actually gone from memory? + delete(am.unlockedKeys, string(addr)) + am.mutex.Unlock() +} diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index 4e97de545..8f036fd1f 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -6,19 +6,68 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/ethutil" + "time" ) func TestAccountManager(t *testing.T) { ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir() + "/testaccounts") - am := NewAccountManager(ks) + am := NewAccountManager(ks, 100) pass := "" // not used but required by API a1, err := am.NewAccount(pass) toSign := randentropy.GetEntropyCSPRNG(32) - _, err = am.Sign(a1, pass, toSign) + _, err = am.SignLocked(a1, pass, toSign) if err != nil { t.Fatal(err) } + // Cleanup + time.Sleep(time.Millisecond * time.Duration(150)) // wait for locking + + accounts, err := am.Accounts() + if err != nil { + t.Fatal(err) + } + for _, account := range accounts { + err := am.DeleteAccount(account.Address, pass) + if err != nil { + t.Fatal(err) + } + } +} + +func TestAccountManagerLocking(t *testing.T) { + ks := crypto.NewKeyStorePassphrase(ethutil.DefaultDataDir() + "/testaccounts") + am := NewAccountManager(ks, 200) + pass := "foo" + a1, err := am.NewAccount(pass) + toSign := randentropy.GetEntropyCSPRNG(32) + + // Signing without passphrase fails because account is locked + _, err = am.Sign(a1, toSign) + if err != ErrLocked { + t.Fatal(err) + } + + // Signing with passphrase works + _, err = am.SignLocked(a1, pass, toSign) + if err != nil { + t.Fatal(err) + } + + // Signing without passphrase works because account is temp unlocked + _, err = am.Sign(a1, toSign) + if err != nil { + t.Fatal(err) + } + + // Signing without passphrase fails after automatic locking + time.Sleep(time.Millisecond * time.Duration(250)) + + _, err = am.Sign(a1, toSign) + if err != ErrLocked { + t.Fatal(err) + } + // Cleanup accounts, err := am.Accounts() if err != nil { From d1311c53eee916ae8472a3b7eaf40b5e94f7675f Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 25 Feb 2015 18:40:59 +0100 Subject: [PATCH 15/36] Address pull request comments * Use RWMutex instead of Mutex * Use time.Duration instead of int for unlock time * Use time.After with select instead of time.Sleep --- accounts/account_manager.go | 34 ++++++++++++++++++---------------- accounts/accounts_test.go | 2 +- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 4d63bd0f2..38dd6f736 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -48,19 +48,19 @@ type Account struct { } type AccountManager struct { - keyStore crypto.KeyStore2 - unlockedKeys map[string]crypto.Key - unlockedMilliSeconds int - mutex sync.Mutex + keyStore crypto.KeyStore2 + unlockedKeys map[string]crypto.Key + unlockMilliseconds time.Duration + mutex sync.RWMutex } -func NewAccountManager(keyStore crypto.KeyStore2, unlockMilliSeconds int) AccountManager { +func NewAccountManager(keyStore crypto.KeyStore2, unlockMilliseconds time.Duration) AccountManager { keysMap := make(map[string]crypto.Key) am := &AccountManager{ - keyStore: keyStore, - unlockedKeys: keysMap, - unlockedMilliSeconds: unlockMilliSeconds, - mutex: sync.Mutex{}, // for accessing unlockedKeys map + keyStore: keyStore, + unlockedKeys: keysMap, + unlockMilliseconds: unlockMilliseconds, + mutex: sync.RWMutex{}, // for accessing unlockedKeys map } return *am } @@ -70,9 +70,9 @@ func (am AccountManager) DeleteAccount(address []byte, auth string) error { } func (am *AccountManager) Sign(fromAccount *Account, toSign []byte) (signature []byte, err error) { - am.mutex.Lock() + am.mutex.RLock() unlockedKey := am.unlockedKeys[string(fromAccount.Address)] - am.mutex.Unlock() + am.mutex.RUnlock() if unlockedKey.Address == nil { return nil, ErrLocked } @@ -85,9 +85,9 @@ func (am *AccountManager) SignLocked(fromAccount *Account, keyAuth string, toSig if err != nil { return nil, err } - am.mutex.Lock() + am.mutex.RLock() am.unlockedKeys[string(fromAccount.Address)] = *key - am.mutex.Unlock() + am.mutex.RUnlock() go unlockLater(am, fromAccount.Address) signature, err = crypto.Sign(toSign, key.PrivateKey) return signature, err @@ -121,9 +121,11 @@ func (am *AccountManager) Accounts() ([]Account, error) { } func unlockLater(am *AccountManager, addr []byte) { - time.Sleep(time.Millisecond * time.Duration(am.unlockedMilliSeconds)) - am.mutex.Lock() + select { + case <-time.After(time.Millisecond * am.unlockMilliseconds): + } + am.mutex.RLock() // TODO: how do we know the key is actually gone from memory? delete(am.unlockedKeys, string(addr)) - am.mutex.Unlock() + am.mutex.RUnlock() } diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index 8f036fd1f..44d1d72f1 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -21,7 +21,7 @@ func TestAccountManager(t *testing.T) { } // Cleanup - time.Sleep(time.Millisecond * time.Duration(150)) // wait for locking + time.Sleep(time.Millisecond * 150) // wait for locking accounts, err := am.Accounts() if err != nil { From 23f265809170fae044be12851f5591f55495003a Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 25 Feb 2015 19:30:57 +0100 Subject: [PATCH 16/36] Remove unneeded initialisation of mutex --- accounts/account_manager.go | 1 - 1 file changed, 1 deletion(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 38dd6f736..90fed1343 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -60,7 +60,6 @@ func NewAccountManager(keyStore crypto.KeyStore2, unlockMilliseconds time.Durati keyStore: keyStore, unlockedKeys: keysMap, unlockMilliseconds: unlockMilliseconds, - mutex: sync.RWMutex{}, // for accessing unlockedKeys map } return *am } From 260ab739694b554c3cdec8531b98e0334753fbe1 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Wed, 25 Feb 2015 20:00:32 +0100 Subject: [PATCH 17/36] Validate block header gas limit * Add block header gas limit validation in ValidateBlock function, see eq 39 and 45 in yellow paper. Before it was calculated _for_ the block instead of validated. * Use the block header gas limit when setting the gas pool instead of calculating the value for the block. --- core/block_processor.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/block_processor.go b/core/block_processor.go index fd591a29d..f66d158b2 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -62,7 +62,7 @@ func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainM func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) { coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase) - coinbase.SetGasPool(CalcGasLimit(parent, block)) + coinbase.SetGasPool(block.Header().GasLimit) // Process the transactions on to parent state receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess) @@ -247,6 +247,11 @@ func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error { return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd) } + expl := CalcGasLimit(parent, block) + if expl.Cmp(block.Header().GasLimit) != 0 { + return fmt.Errorf("GasLimit check failed for block %v, %v", block.Header().GasLimit, expl) + } + if block.Time() < parent.Time() { return ValidationError("Block timestamp not after prev block (%v - %v)", block.Header().Time, parent.Header().Time) } From 8eeab7ea3fb7a01a610e46940141eecf7435873a Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 26 Feb 2015 02:24:20 +0100 Subject: [PATCH 18/36] Update osext import path --- cmd/mist/flags.go | 2 +- cmd/utils/cmd.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go index d5ed60a21..c4edb8dae 100644 --- a/cmd/mist/flags.go +++ b/cmd/mist/flags.go @@ -30,12 +30,12 @@ import ( "path/filepath" "runtime" - "bitbucket.org/kardianos/osext" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/vm" + "github.com/kardianos/osext" ) var ( diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index bc8fafafb..d78b6a94d 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -30,7 +30,6 @@ import ( "regexp" "runtime" - "bitbucket.org/kardianos/osext" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" @@ -42,6 +41,7 @@ import ( rpcws "github.com/ethereum/go-ethereum/rpc/ws" "github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/xeth" + "github.com/kardianos/osext" ) var clilogger = logger.NewLogger("CLI") From d5aed82b11589bb3f8b0df4544081fe898b8c724 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 26 Feb 2015 02:32:27 +0100 Subject: [PATCH 19/36] DRY DefaultAssetPath() --- cmd/mist/flags.go | 29 +---------------------------- cmd/utils/cmd.go | 26 -------------------------- ethutil/common.go | 26 ++++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 54 deletions(-) diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go index c4edb8dae..8dab59d80 100644 --- a/cmd/mist/flags.go +++ b/cmd/mist/flags.go @@ -68,33 +68,6 @@ var ( // flags specific to gui client var AssetPath string - -//TODO: If we re-use the one defined in cmd.go the binary osx image crashes. If somebody finds out why we can dry this up. -func defaultAssetPath() string { - var assetPath string - // If the current working directory is the go-ethereum dir - // assume a debug build and use the source directory as - // asset directory. - pwd, _ := os.Getwd() - if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist") { - assetPath = path.Join(pwd, "assets") - } else { - switch runtime.GOOS { - case "darwin": - // Get Binary Directory - exedir, _ := osext.ExecutableFolder() - assetPath = filepath.Join(exedir, "../Resources") - case "linux": - assetPath = "/usr/share/mist" - case "windows": - assetPath = "./assets" - default: - assetPath = "." - } - } - return assetPath -} - var defaultConfigFile = path.Join(ethutil.DefaultDataDir(), "conf.ini") func Init() { @@ -122,7 +95,7 @@ func Init() { flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)") flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)") - flag.StringVar(&AssetPath, "asset_path", defaultAssetPath(), "absolute path to GUI assets directory") + flag.StringVar(&AssetPath, "asset_path", ethutil.DefaultAssetPath(), "absolute path to GUI assets directory") // Network stuff var ( diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index d78b6a94d..a65476e69 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -41,7 +41,6 @@ import ( rpcws "github.com/ethereum/go-ethereum/rpc/ws" "github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/xeth" - "github.com/kardianos/osext" ) var clilogger = logger.NewLogger("CLI") @@ -132,31 +131,6 @@ func StartEthereum(ethereum *eth.Ethereum) { }) } -func DefaultAssetPath() string { - var assetPath string - // If the current working directory is the go-ethereum dir - // assume a debug build and use the source directory as - // asset directory. - pwd, _ := os.Getwd() - if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist") { - assetPath = path.Join(pwd, "assets") - } else { - switch runtime.GOOS { - case "darwin": - // Get Binary Directory - exedir, _ := osext.ExecutableFolder() - assetPath = filepath.Join(exedir, "../Resources") - case "linux": - assetPath = "/usr/share/mist" - case "windows": - assetPath = "./assets" - default: - assetPath = "." - } - } - return assetPath -} - func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) { var err error diff --git a/ethutil/common.go b/ethutil/common.go index efc519732..d439dcedc 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -2,6 +2,7 @@ package ethutil import ( "fmt" + "github.com/kardianos/osext" "math/big" "os/user" "path" @@ -9,6 +10,31 @@ import ( "time" ) +func DefaultAssetPath() string { + var assetPath string + // If the current working directory is the go-ethereum dir + // assume a debug build and use the source directory as + // asset directory. + pwd, _ := os.Getwd() + if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist") { + assetPath = path.Join(pwd, "assets") + } else { + switch runtime.GOOS { + case "darwin": + // Get Binary Directory + exedir, _ := osext.ExecutableFolder() + assetPath = filepath.Join(exedir, "../Resources") + case "linux": + assetPath = "/usr/share/mist" + case "windows": + assetPath = "./assets" + default: + assetPath = "." + } + } + return assetPath +} + func DefaultDataDir() string { usr, _ := user.Current() if runtime.GOOS == "darwin" { From 739b7cc2ac7de9c6c24547be00de81338089d724 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 26 Feb 2015 02:36:06 +0100 Subject: [PATCH 20/36] Cleanup imports --- cmd/mist/flags.go | 2 -- cmd/utils/cmd.go | 3 --- ethutil/common.go | 5 ++++- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go index 8dab59d80..0010df826 100644 --- a/cmd/mist/flags.go +++ b/cmd/mist/flags.go @@ -27,7 +27,6 @@ import ( "log" "os" "path" - "path/filepath" "runtime" "github.com/ethereum/go-ethereum/crypto" @@ -35,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/vm" - "github.com/kardianos/osext" ) var ( diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index a65476e69..a36c10e3b 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -25,10 +25,7 @@ import ( "fmt" "os" "os/signal" - "path" - "path/filepath" "regexp" - "runtime" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" diff --git a/ethutil/common.go b/ethutil/common.go index d439dcedc..c4e7415dc 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -2,12 +2,15 @@ package ethutil import ( "fmt" - "github.com/kardianos/osext" "math/big" + "os" "os/user" "path" + "path/filepath" "runtime" "time" + + "github.com/kardianos/osext" ) func DefaultAssetPath() string { From 57cea0c623ed808ad134bd62610afb6b842f5d0d Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 26 Feb 2015 02:48:55 +0100 Subject: [PATCH 21/36] Update godeps --- Godeps/Godeps.json | 11 ++++---- .../src/bitbucket.org/kardianos/osext/LICENSE | 20 -------------- .../src/github.com/kardianos/osext/LICENSE | 27 +++++++++++++++++++ .../src/github.com/kardianos/osext/README.md | 14 ++++++++++ .../kardianos/osext/osext.go | 5 ---- .../kardianos/osext/osext_plan9.go | 18 ++++++------- .../kardianos/osext/osext_procfs.go | 7 +++-- .../kardianos/osext/osext_sysctl.go | 0 .../kardianos/osext/osext_test.go | 0 .../kardianos/osext/osext_windows.go | 0 10 files changed, 60 insertions(+), 42 deletions(-) delete mode 100644 Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE create mode 100644 Godeps/_workspace/src/github.com/kardianos/osext/LICENSE create mode 100644 Godeps/_workspace/src/github.com/kardianos/osext/README.md rename Godeps/_workspace/src/{bitbucket.org => github.com}/kardianos/osext/osext.go (87%) rename Godeps/_workspace/src/{bitbucket.org => github.com}/kardianos/osext/osext_plan9.go (51%) rename Godeps/_workspace/src/{bitbucket.org => github.com}/kardianos/osext/osext_procfs.go (74%) rename Godeps/_workspace/src/{bitbucket.org => github.com}/kardianos/osext/osext_sysctl.go (100%) rename Godeps/_workspace/src/{bitbucket.org => github.com}/kardianos/osext/osext_test.go (100%) rename Godeps/_workspace/src/{bitbucket.org => github.com}/kardianos/osext/osext_windows.go (100%) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 9b7306530..b66ea932f 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,15 +1,10 @@ { "ImportPath": "github.com/ethereum/go-ethereum", - "GoVersion": "go1.4.1", + "GoVersion": "go1.4.2", "Packages": [ "./..." ], "Deps": [ - { - "ImportPath": "bitbucket.org/kardianos/osext", - "Comment": "null-13", - "Rev": "5d3ddcf53a508cc2f7404eaebf546ef2cb5cdb6e" - }, { "ImportPath": "code.google.com/p/go-uuid/uuid", "Comment": "null-12", @@ -37,6 +32,10 @@ "ImportPath": "github.com/jackpal/go-nat-pmp", "Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1" }, + { + "ImportPath": "github.com/kardianos/osext", + "Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a" + }, { "ImportPath": "github.com/obscuren/otto", "Rev": "cf13cc4228c5e5ce0fe27a7aea90bc10091c4f19" diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE b/Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE deleted file mode 100644 index 18527a28f..000000000 --- a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2012 Daniel Theophanes - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/LICENSE b/Godeps/_workspace/src/github.com/kardianos/osext/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kardianos/osext/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/kardianos/osext/README.md b/Godeps/_workspace/src/github.com/kardianos/osext/README.md new file mode 100644 index 000000000..820e1ecb5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/kardianos/osext/README.md @@ -0,0 +1,14 @@ +### Extensions to the "os" package. + +## Find the current Executable and ExecutableFolder. + +There is sometimes utility in finding the current executable file +that is running. This can be used for upgrading the current executable +or finding resources located relative to the executable file. + +Multi-platform and supports: + * Linux + * OS X + * Windows + * Plan 9 + * BSDs. diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext.go similarity index 87% rename from Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go rename to Godeps/_workspace/src/github.com/kardianos/osext/osext.go index 37efbb221..4ed4b9aa3 100644 --- a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go +++ b/Godeps/_workspace/src/github.com/kardianos/osext/osext.go @@ -25,8 +25,3 @@ func ExecutableFolder() (string, error) { folder, _ := filepath.Split(p) return folder, nil } - -// Depricated. Same as Executable(). -func GetExePath() (exePath string, err error) { - return Executable() -} diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_plan9.go similarity index 51% rename from Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go rename to Godeps/_workspace/src/github.com/kardianos/osext/osext_plan9.go index 4468a73a7..655750c54 100644 --- a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go +++ b/Godeps/_workspace/src/github.com/kardianos/osext/osext_plan9.go @@ -5,16 +5,16 @@ package osext import ( - "syscall" - "os" - "strconv" + "os" + "strconv" + "syscall" ) func executable() (string, error) { - f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text") - if err != nil { - return "", err - } - defer f.Close() - return syscall.Fd2path(int(f.Fd())) + f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text") + if err != nil { + return "", err + } + defer f.Close() + return syscall.Fd2path(int(f.Fd())) } diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go similarity index 74% rename from Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go rename to Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go index 546fec915..a50021ad5 100644 --- a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go +++ b/Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux netbsd openbsd +// +build linux netbsd openbsd solaris dragonfly package osext import ( "errors" + "fmt" "os" "runtime" ) @@ -18,8 +19,10 @@ func executable() (string, error) { return os.Readlink("/proc/self/exe") case "netbsd": return os.Readlink("/proc/curproc/exe") - case "openbsd": + case "openbsd", "dragonfly": return os.Readlink("/proc/curproc/file") + case "solaris": + return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid())) } return "", errors.New("ExecPath not implemented for " + runtime.GOOS) } diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_sysctl.go similarity index 100% rename from Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go rename to Godeps/_workspace/src/github.com/kardianos/osext/osext_sysctl.go diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go similarity index 100% rename from Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go rename to Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go diff --git a/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go b/Godeps/_workspace/src/github.com/kardianos/osext/osext_windows.go similarity index 100% rename from Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go rename to Godeps/_workspace/src/github.com/kardianos/osext/osext_windows.go From 5ab0eaa06d2f5879b9b22778988410bd0c73dcc0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Feb 2015 11:14:54 +0100 Subject: [PATCH 22/36] wip --- accounts/account_manager.go | 1 + rpc/api.go | 146 ++++++++++++++++++++++++++++-------- xeth/world.go | 32 -------- xeth/xeth.go | 22 +++++- 4 files changed, 132 insertions(+), 69 deletions(-) delete mode 100644 xeth/world.go diff --git a/accounts/account_manager.go b/accounts/account_manager.go index f7a7506ba..f1ad450e6 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -34,6 +34,7 @@ package accounts import ( crand "crypto/rand" + "github.com/ethereum/go-ethereum/crypto" ) diff --git a/rpc/api.go b/rpc/api.go index 21c85bbcc..f337324d0 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -9,6 +9,7 @@ For each request type, define the following: package rpc import ( + "fmt" "math/big" "strings" "sync" @@ -19,8 +20,10 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event/filter" "github.com/ethereum/go-ethereum/state" + "github.com/ethereum/go-ethereum/ui" "github.com/ethereum/go-ethereum/xeth" ) @@ -31,7 +34,10 @@ var ( ) type EthereumApi struct { - xeth *xeth.XEth + eth *xeth.XEth + xethMu sync.RWMutex + mux *event.TypeMux + quit chan struct{} filterManager *filter.FilterManager @@ -50,7 +56,8 @@ type EthereumApi struct { func NewEthereumApi(eth *xeth.XEth) *EthereumApi { db, _ := ethdb.NewLDBDatabase("dapps") api := &EthereumApi{ - xeth: eth, + eth: eth, + mux: eth.Backend().EventMux(), quit: make(chan struct{}), filterManager: filter.NewFilterManager(eth.Backend().EventMux()), logs: make(map[int]*logFilter), @@ -65,9 +72,25 @@ func NewEthereumApi(eth *xeth.XEth) *EthereumApi { func (self *EthereumApi) start() { timer := time.NewTicker(filterTickerTime) + events := self.mux.Subscribe(core.ChainEvent{}) + done: for { select { + case ev := <-events.Chan(): + switch ev.(type) { + case core.ChainEvent: + // fixme + const something = 1337 + if something < 0 { + chain := self.xeth().Backend().ChainManager() + block := chain.GetBlockByNumber(chain.CurrentBlock().Number().Uint64() - something) + if block != nil { + statedb := state.New(block.Root(), self.db) + self.useState(statedb) + } + } + } case <-timer.C: self.logMut.Lock() self.messagesMut.Lock() @@ -80,7 +103,7 @@ done: for id, filter := range self.messages { if time.Since(filter.timeout) > 20*time.Second { - self.xeth.Whisper().Unwatch(id) + self.xeth().Whisper().Unwatch(id) delete(self.messages, id) } } @@ -128,7 +151,7 @@ func (self *EthereumApi) WatchTx(args string, reply *interface{}) error { func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error { var id int - filter := core.NewFilter(self.xeth.Backend()) + filter := core.NewFilter(self.xeth().Backend()) filter.SetOptions(toFilterOptions(args)) filter.LogsCallback = func(logs state.Logs) { self.logMut.Lock() @@ -153,7 +176,7 @@ func (self *EthereumApi) UninstallFilter(id int, reply *interface{}) error { func (self *EthereumApi) NewFilterString(args string, reply *interface{}) error { var id int - filter := core.NewFilter(self.xeth.Backend()) + filter := core.NewFilter(self.xeth().Backend()) callback := func(block *types.Block) { self.logMut.Lock() @@ -198,7 +221,7 @@ func (self *EthereumApi) Logs(id int, reply *interface{}) error { } func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error { - filter := core.NewFilter(self.xeth.Backend()) + filter := core.NewFilter(self.xeth().Backend()) filter.SetOptions(toFilterOptions(args)) *reply = toLogs(filter.Find()) @@ -209,13 +232,28 @@ func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { // This seems a bit precarious Maybe worth splitting to discrete functions if len(args.Hash) > 0 { - *reply = p.xeth.BlockByHash(args.Hash) + *reply = p.xeth().BlockByHash(args.Hash) } else { - *reply = p.xeth.BlockByNumber(args.BlockNumber) + *reply = p.xeth().BlockByNumber(args.BlockNumber) } return nil } +/* +func unlockAccount(server, account *Account) bool { + pwd, status := server.PasswordDialog() + switch status { + case Ok: + if !account.Unlock([]byte(pwd)) { + return unlockAccount(account) + } + return true + default: + return false + } +} +*/ + func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { if len(args.Gas) == 0 { args.Gas = defaultGas.String() @@ -226,18 +264,35 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { } // TODO if no_private_key then - if _, exists := p.register[args.From]; exists { - p.register[args.From] = append(p.register[args.From], args) - } else { - result, _ := p.xeth.Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) - *reply = result - } + //if _, exists := p.register[args.From]; exists { + // p.register[args.From] = append(p.register[args.From], args) + //} else { + /* + account := accounts.Get(fromHex(args.From)) + if account != nil { + if account.Unlocked() { + if !unlockAccount(account) { + return + } + } + + result, _ := account.Transact(fromHex(args.To), fromHex(args.Value), fromHex(args.Gas), fromHex(args.GasPrice), fromHex(args.Data)) + if len(result) > 0 { + *reply = toHex(result) + } + } else if _, exists := p.register[args.From]; exists { + p.register[ags.From] = append(p.register[args.From], args) + } + */ + result, _ := p.xeth().Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) + *reply = result + //} return nil } func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error { - result, err := p.xeth.Call( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) + result, err := p.xeth().Call( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) if err != nil { return err } @@ -251,7 +306,7 @@ func (p *EthereumApi) PushTx(args *PushTxArgs, reply *interface{}) error { if err != nil { return err } - result, _ := p.xeth.PushTx(args.Tx) + result, _ := p.xeth().PushTx(args.Tx) *reply = result return nil } @@ -262,7 +317,7 @@ func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error { return err } - state := p.xeth.State().SafeGet(args.Address) + state := p.xeth().State().SafeGet(args.Address) value := state.StorageString(args.Key) var hx string @@ -284,42 +339,42 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) err return err } - *reply = p.xeth.State().SafeGet(args.Address).Storage() + *reply = p.xeth().State().SafeGet(args.Address).Storage() return nil } func (p *EthereumApi) GetPeerCount(reply *interface{}) error { - *reply = p.xeth.PeerCount() + *reply = p.xeth().PeerCount() return nil } func (p *EthereumApi) GetIsListening(reply *interface{}) error { - *reply = p.xeth.IsListening() + *reply = p.xeth().IsListening() return nil } func (p *EthereumApi) GetCoinbase(reply *interface{}) error { - *reply = p.xeth.Coinbase() + *reply = p.xeth().Coinbase() return nil } func (p *EthereumApi) Accounts(reply *interface{}) error { - *reply = p.xeth.Accounts() + *reply = p.xeth().Accounts() return nil } func (p *EthereumApi) GetIsMining(reply *interface{}) error { - *reply = p.xeth.IsMining() + *reply = p.xeth().IsMining() return nil } func (p *EthereumApi) SetMining(shouldmine bool, reply *interface{}) error { - *reply = p.xeth.SetMining(shouldmine) + *reply = p.xeth().SetMining(shouldmine) return nil } func (p *EthereumApi) BlockNumber(reply *interface{}) error { - *reply = p.xeth.Backend().ChainManager().CurrentBlock().Number() + *reply = p.xeth().Backend().ChainManager().CurrentBlock().Number() return nil } @@ -328,7 +383,7 @@ func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) err if err != nil { return err } - *reply = p.xeth.TxCountAt(args.Address) + *reply = p.xeth().TxCountAt(args.Address) return nil } @@ -337,7 +392,7 @@ func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *interface{}) err if err != nil { return err } - state := p.xeth.State().SafeGet(args.Address) + state := p.xeth().State().SafeGet(args.Address) *reply = toHex(state.Balance().Bytes()) return nil } @@ -347,7 +402,7 @@ func (p *EthereumApi) GetCodeAt(args *GetCodeAtArgs, reply *interface{}) error { if err != nil { return err } - *reply = p.xeth.CodeAt(args.Address) + *reply = p.xeth().CodeAt(args.Address) return nil } @@ -394,7 +449,7 @@ func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error { } func (p *EthereumApi) NewWhisperIdentity(reply *interface{}) error { - *reply = p.xeth.Whisper().NewIdentity() + *reply = p.xeth().Whisper().NewIdentity() return nil } @@ -405,7 +460,7 @@ func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) e defer p.messagesMut.Unlock() p.messages[id].add(msg) // = append(p.messages[id], msg) } - id = p.xeth.Whisper().Watch(args) + id = p.xeth().Whisper().Watch(args) p.messages[id] = &whisperFilter{timeout: time.Now()} *reply = id return nil @@ -423,7 +478,7 @@ func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error { } func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error { - err := p.xeth.Whisper().Post(args.Payload, args.To, args.From, args.Topic, args.Priority, args.Ttl) + err := p.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topic, args.Priority, args.Ttl) if err != nil { return err } @@ -433,12 +488,12 @@ func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) } func (p *EthereumApi) HasWhisperIdentity(args string, reply *interface{}) error { - *reply = p.xeth.Whisper().HasIdentity(args) + *reply = p.xeth().Whisper().HasIdentity(args) return nil } func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error { - *reply = p.xeth.Whisper().Messages(id) + *reply = p.xeth().Whisper().Messages(id) return nil } @@ -634,3 +689,28 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error rpclogger.DebugDetailf("Reply: %T %s", reply, reply) return nil } + +func (self *EthereumApi) xeth() *xeth.XEth { + self.xethMu.RLock() + defer self.xethMu.RUnlock() + + return self.eth +} + +func (self *EthereumApi) useState(statedb *state.StateDB) { + self.xethMu.Lock() + defer self.xethMu.Unlock() + + self.eth = self.xeth().UseState(statedb) +} + +func t(f ui.Frontend) { + // Call the password dialog + ret, err := f.Call("PasswordDialog") + if err != nil { + fmt.Println(err) + } + // Get the first argument + t, _ := ret.Get(0) + fmt.Println("return:", t) +} diff --git a/xeth/world.go b/xeth/world.go deleted file mode 100644 index 9cbdd9461..000000000 --- a/xeth/world.go +++ /dev/null @@ -1,32 +0,0 @@ -package xeth - -import "github.com/ethereum/go-ethereum/state" - -type State struct { - xeth *XEth -} - -func NewState(xeth *XEth) *State { - return &State{xeth} -} - -func (self *State) State() *state.StateDB { - return self.xeth.chainManager.TransState() -} - -func (self *State) Get(addr string) *Object { - return &Object{self.State().GetStateObject(fromHex(addr))} -} - -func (self *State) SafeGet(addr string) *Object { - return &Object{self.safeGet(addr)} -} - -func (self *State) safeGet(addr string) *state.StateObject { - object := self.State().GetStateObject(fromHex(addr)) - if object == nil { - object = state.NewStateObject(fromHex(addr), self.xeth.eth.Db()) - } - - return object -} diff --git a/xeth/xeth.go b/xeth/xeth.go index 2985ce982..d4c188fec 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/whisper" ) @@ -54,13 +55,26 @@ func New(eth Backend) *XEth { whisper: NewWhisper(eth.Whisper()), miner: eth.Miner(), } - xeth.state = NewState(xeth) + xeth.state = NewState(xeth, xeth.chainManager.TransState()) return xeth } -func (self *XEth) Backend() Backend { return self.eth } -func (self *XEth) State() *State { return self.state } +func (self *XEth) Backend() Backend { return self.eth } +func (self *XEth) UseState(statedb *state.StateDB) *XEth { + xeth := &XEth{ + eth: self.eth, + blockProcessor: self.blockProcessor, + chainManager: self.chainManager, + whisper: self.whisper, + miner: self.miner, + } + + xeth.state = NewState(xeth, statedb) + return xeth +} +func (self *XEth) State() *State { return self.state } + func (self *XEth) Whisper() *Whisper { return self.whisper } func (self *XEth) Miner() *miner.Miner { return self.miner } @@ -229,7 +243,7 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st } var ( - statedb = self.chainManager.TransState() + statedb = self.State().State() //self.chainManager.TransState() key = self.eth.KeyManager().KeyPair() from = statedb.GetOrNewStateObject(key.Address()) block = self.chainManager.CurrentBlock() From a1c830cd3c53bce7748d97e3f99fe6a90d526adb Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Feb 2015 12:06:10 +0100 Subject: [PATCH 23/36] Defautl block --- cmd/mist/assets/examples/info.html | 7 +- .../assets/ext/ethereum.js/dist/ethereum.js | 816 +++++++++++------- rpc/api.go | 63 +- 3 files changed, 529 insertions(+), 357 deletions(-) diff --git a/cmd/mist/assets/examples/info.html b/cmd/mist/assets/examples/info.html index 2a405c280..3b958a494 100644 --- a/cmd/mist/assets/examples/info.html +++ b/cmd/mist/assets/examples/info.html @@ -62,6 +62,8 @@ web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8545')); + eth.defaultBlock = -2 + document.querySelector("#number").innerHTML = eth.number; document.querySelector("#coinbase").innerHTML = eth.coinbase document.querySelector("#peer_count").innerHTML = eth.peerCount; @@ -72,8 +74,9 @@ document.querySelector("#mining").innerHTML = eth.mining; document.querySelector("#listening").innerHTML = eth.listening; eth.watch('chain').changed(function() { - document.querySelector("#number").innerHTML = eth.number; - }); + document.querySelector("#number").innerHTML = eth.number; + }); + diff --git a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js index 83b598b3f..522b77ebf 100644 --- a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js +++ b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js @@ -53,7 +53,6 @@ var inputTypes = types.inputTypes(); /// @returns bytes representation of input params var formatInput = function (inputs, params) { var bytes = ""; - var padding = c.ETH_PADDING * 2; /// first we iterate in search for dynamic inputs.forEach(function (input, index) { @@ -110,6 +109,7 @@ var formatOutput = function (outs, output) { output = output.slice(dynamicPartLength); outs.forEach(function (out, i) { + /*jshint maxcomplexity:6 */ var typeMatch = false; for (var j = 0; j < outputTypes.length && !typeMatch; j++) { typeMatch = outputTypes[j].type(outs[i].type); @@ -210,7 +210,7 @@ module.exports = { }; -},{"./const":2,"./formatters":6,"./types":11,"./utils":12,"./web3":13}],2:[function(require,module,exports){ +},{"./const":2,"./formatters":8,"./types":14,"./utils":15,"./web3":17}],2:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -264,7 +264,8 @@ module.exports = { ETH_PADDING: 32, ETH_SIGNATURE_LENGTH: 4, ETH_UNITS: ETH_UNITS, - ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN } + ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, + ETH_POLLING_TIMEOUT: 1000 }; @@ -340,6 +341,7 @@ var addFunctionsToContract = function (contract, desc, address) { var typeName = utils.extractTypeName(method.name); var impl = function () { + /*jshint maxcomplexity:7 */ var params = Array.prototype.slice.call(arguments); var signature = abi.signatureFromAscii(method.name); var parsed = inputParser[displayName][typeName].apply(null, params); @@ -416,11 +418,11 @@ var addEventsToContract = function (contract, desc, address) { var signature = abi.eventSignatureFromAscii(e.name); var event = eventImpl.inputParser(address, signature, e); var o = event.apply(null, params); - o._onWatchEventResult = function (data) { + var outputFormatter = function (data) { var parser = eventImpl.outputParser(e); return parser(data); }; - return web3.eth.watch(o); + return web3.eth.watch(o, undefined, undefined, outputFormatter); }; // this property should be used by eth.filter to check if object is an event @@ -487,7 +489,131 @@ var contract = function (address, desc) { module.exports = contract; -},{"./abi":1,"./event":4,"./utils":12,"./web3":13}],4:[function(require,module,exports){ +},{"./abi":1,"./event":6,"./utils":15,"./web3":17}],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 . +*/ +/** @file db.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/// @returns an array of objects describing web3.db api methods +var methods = function () { + return [ + { name: 'put', call: 'db_put' }, + { name: 'get', call: 'db_get' }, + { name: 'putString', call: 'db_putString' }, + { name: 'getString', call: 'db_getString' } + ]; +}; + +module.exports = { + methods: methods +}; + +},{}],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 . +*/ +/** @file eth.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/// @returns an array of objects describing web3.eth api methods +var methods = function () { + var blockCall = function (args) { + return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; + }; + + var transactionCall = function (args) { + return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; + }; + + var uncleCall = function (args) { + return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; + }; + + var transactionCountCall = function (args) { + return typeof args[0] === "string" ? 'eth_transactionCountByHash' : 'eth_transactionCountByNumber'; + }; + + var uncleCountCall = function (args) { + return typeof args[0] === "string" ? 'eth_uncleCountByHash' : 'eth_uncleCountByNumber'; + }; + + return [ + { name: 'balanceAt', call: 'eth_balanceAt' }, + { name: 'stateAt', call: 'eth_stateAt' }, + { name: 'storageAt', call: 'eth_storageAt' }, + { name: 'countAt', call: 'eth_countAt'}, + { name: 'codeAt', call: 'eth_codeAt' }, + { name: 'transact', call: 'eth_transact' }, + { name: 'call', call: 'eth_call' }, + { name: 'block', call: blockCall }, + { name: 'transaction', call: transactionCall }, + { name: 'uncle', call: uncleCall }, + { name: 'compilers', call: 'eth_compilers' }, + { name: 'flush', call: 'eth_flush' }, + { name: 'lll', call: 'eth_lll' }, + { name: 'solidity', call: 'eth_solidity' }, + { name: 'serpent', call: 'eth_serpent' }, + { name: 'logs', call: 'eth_logs' }, + { name: 'transactionCount', call: transactionCountCall }, + { name: 'uncleCount', call: uncleCountCall } + ]; +}; + +/// @returns an array of objects describing web3.eth api properties +var properties = function () { + return [ + { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, + { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, + { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, + { name: 'gasPrice', getter: 'eth_gasPrice' }, + { name: 'accounts', getter: 'eth_accounts' }, + { name: 'peerCount', getter: 'eth_peerCount' }, + { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, + { name: 'number', getter: 'eth_number'} + ]; +}; + +module.exports = { + methods: methods, + properties: properties +}; + + +},{}],6:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -571,9 +697,9 @@ var getArgumentsObject = function (inputs, indexed, notIndexed) { return inputs.reduce(function (acc, current) { var value; if (current.indexed) - value = indexed.splice(0, 1)[0]; + value = indexedCopy.splice(0, 1)[0]; else - value = notIndexed.splice(0, 1)[0]; + value = notIndexedCopy.splice(0, 1)[0]; acc[current.name] = value; return acc; @@ -589,6 +715,7 @@ var outputParser = function (event) { args: {} }; + output.topics = output.topic; // fallback for go-ethereum if (!output.topic) { return result; } @@ -624,7 +751,7 @@ module.exports = { }; -},{"./abi":1,"./utils":12}],5:[function(require,module,exports){ +},{"./abi":1,"./utils":15}],7:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -650,84 +777,96 @@ module.exports = { * @date 2014 */ -var web3 = require('./web3'); // jshint ignore:line +/// Should be called to check if filter implementation is valid +/// @returns true if it is, otherwise false +var implementationIsValid = function (i) { + return !!i && + typeof i.newFilter === 'function' && + typeof i.getMessages === 'function' && + typeof i.uninstallFilter === 'function' && + typeof i.startPolling === 'function' && + typeof i.stopPolling === 'function'; +}; -/// should be used when we want to watch something +/// 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 (typeof options === 'string') { + return options; + } + + options = options || {}; + + if (options.topics) { + console.warn('"topics" is deprecated, is "topic" instead'); + } + + // evaluate lazy properties + return { + to: options.to, + topic: options.topic, + earliest: options.earliest, + latest: options.latest, + max: options.max, + skip: options.skip, + address: options.address + }; +}; + +/// Should be used when we want to watch something /// it's using inner polling mechanism and is notified about changes -/// TODO: change 'options' name cause it may be not the best matching one, since we have events -var Filter = function(options, impl) { - - if (typeof options !== "string") { - - // topics property is deprecated, warn about it! - if (options.topics) { - console.warn('"topics" is deprecated, use "topic" instead'); - } - - this._onWatchResult = options._onWatchEventResult; - - // evaluate lazy properties - options = { - to: options.to, - topic: options.topic, - earliest: options.earliest, - latest: options.latest, - max: options.max, - skip: options.skip, - address: options.address - }; - +/// @param options are filter options +/// @param implementation, an abstract polling implementation +/// @param formatter (optional), callback function which formats output before 'real' callback +var filter = function(options, implementation, formatter) { + if (!implementationIsValid(implementation)) { + console.error('filter implemenation is invalid'); + return; } + + options = getOptions(options); + var callbacks = []; + var filterId = implementation.newFilter(options); + var onMessages = function (messages) { + messages.forEach(function (message) { + message = formatter ? formatter(message) : message; + callbacks.forEach(function (callback) { + callback(message); + }); + }); + }; + + implementation.startPolling(filterId, onMessages, implementation.uninstallFilter); + + var changed = function (callback) { + callbacks.push(callback); + }; + + var messages = function () { + return implementation.getMessages(filterId); + }; - this.impl = impl; - this.callbacks = []; + var uninstall = function () { + implementation.stopPolling(filterId); + implementation.uninstallFilter(filterId); + callbacks = []; + }; - this.id = impl.newFilter(options); - web3.provider.startPolling({method: impl.changed, params: [this.id]}, this.id, this.trigger.bind(this)); + return { + changed: changed, + arrived: changed, + happened: changed, + messages: messages, + logs: messages, + uninstall: uninstall + }; }; -/// alias for changed* -Filter.prototype.arrived = function(callback) { - this.changed(callback); -}; -Filter.prototype.happened = function(callback) { - this.changed(callback); -}; +module.exports = filter; -/// gets called when there is new eth/shh message -Filter.prototype.changed = function(callback) { - this.callbacks.push(callback); -}; -/// trigger calling new message from people -Filter.prototype.trigger = function(messages) { - for (var i = 0; i < this.callbacks.length; i++) { - for (var j = 0; j < messages.length; j++) { - var message = this._onWatchResult ? this._onWatchResult(messages[j]) : messages[j]; - this.callbacks[i].call(this, message); - } - } -}; - -/// should be called to uninstall current filter -Filter.prototype.uninstall = function() { - this.impl.uninstallFilter(this.id); - web3.provider.stopPolling(this.id); -}; - -/// should be called to manually trigger getting latest messages from the client -Filter.prototype.messages = function() { - return this.impl.getMessages(this.id); -}; - -/// alias for messages -Filter.prototype.logs = function () { - return this.messages(); -}; - -module.exports = Filter; - -},{"./web3":13}],6:[function(require,module,exports){ +},{}],8:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -770,6 +909,7 @@ var padLeft = function (string, chars, sign) { /// If the value is floating point, round it down /// @returns right-aligned byte representation of int var formatInputInt = function (value) { + /*jshint maxcomplexity:7 */ var padding = c.ETH_PADDING * 2; if (value instanceof BigNumber || typeof value === 'number') { if (typeof value === 'number') @@ -883,7 +1023,7 @@ module.exports = { }; -},{"./const":2,"./utils":12}],7:[function(require,module,exports){ +},{"./const":2,"./utils":15}],9:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -918,20 +1058,22 @@ var HttpSyncProvider = function (host) { HttpSyncProvider.prototype.send = function (payload) { //var data = formatJsonRpcObject(payload); - + var request = new XMLHttpRequest(); request.open('POST', this.host, false); request.send(JSON.stringify(payload)); - - // check request.status + var result = request.responseText; + // check request.status + if(request.status !== 200) + return; return JSON.parse(result); }; module.exports = HttpSyncProvider; -},{}],8:[function(require,module,exports){ +},{}],10:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -998,111 +1140,7 @@ module.exports = { -},{}],9:[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 . -*/ -/** @file providermanager.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * Marian Oancea - * Gav Wood - * @date 2014 - */ - -var web3 = require('./web3'); -var jsonrpc = require('./jsonrpc'); - - -/** - * Provider manager object prototype - * It's responsible for passing messages to providers - * If no provider is set it's responsible for queuing requests - * It's also responsible for polling the ethereum node for incoming messages - * Default poll timeout is 12 seconds - * If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling, - * and provider manager polling mechanism is not used - */ -var ProviderManager = function() { - this.polls = []; - this.provider = undefined; - - var self = this; - var poll = function () { - self.polls.forEach(function (data) { - var result = self.send(data.data); - - if (!(result instanceof Array) || result.length === 0) { - return; - } - - data.callback(result); - }); - - setTimeout(poll, 1000); - }; - poll(); -}; - -/// sends outgoing requests -/// @params data - an object with at least 'method' property -ProviderManager.prototype.send = function(data) { - var payload = jsonrpc.toPayload(data.method, data.params); - - if (this.provider === undefined) { - console.error('provider is not set'); - return null; - } - - var result = this.provider.send(payload); - - if (!jsonrpc.isValidResponse(result)) { - console.log(result); - return null; - } - - return result.result; -}; - -/// setups provider, which will be used for sending messages -ProviderManager.prototype.set = function(provider) { - this.provider = provider; -}; - -/// this method is only used, when we do not have native qt bindings and have to do polling on our own -/// should be callled, on start watching for eth/shh changes -ProviderManager.prototype.startPolling = function (data, pollId, callback) { - this.polls.push({data: data, id: pollId, callback: callback}); -}; - -/// should be called to stop polling for certain watch changes -ProviderManager.prototype.stopPolling = function (pollId) { - for (var i = this.polls.length; i--;) { - var poll = this.polls[i]; - if (poll.id === pollId) { - this.polls.splice(i, 1); - } - } -}; - -module.exports = ProviderManager; - - -},{"./jsonrpc":8,"./web3":13}],10:[function(require,module,exports){ +},{}],11:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1137,7 +1175,160 @@ QtSyncProvider.prototype.send = function (payload) { module.exports = QtSyncProvider; -},{}],11:[function(require,module,exports){ +},{}],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 . +*/ +/** @file requestmanager.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Gav Wood + * @date 2014 + */ + +var jsonrpc = require('./jsonrpc'); +var c = require('./const'); + +/** + * 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 + */ +var requestManager = function() { + var polls = []; + var timeout = null; + var provider; + + var send = function (data) { + var payload = jsonrpc.toPayload(data.method, data.params); + + if (!provider) { + console.error('provider is not set'); + return null; + } + + var result = provider.send(payload); + + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + return null; + } + + return result.result; + }; + + var setProvider = function (p) { + provider = p; + }; + + /*jshint maxparams:4 */ + var startPolling = function (data, pollId, callback, uninstall) { + polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall}); + }; + /*jshint maxparams:3 */ + + var stopPolling = function (pollId) { + for (var i = polls.length; i--;) { + var poll = polls[i]; + if (poll.id === pollId) { + polls.splice(i, 1); + } + } + }; + + var reset = function () { + polls.forEach(function (poll) { + poll.uninstall(poll.id); + }); + polls = []; + + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + poll(); + }; + + var poll = function () { + polls.forEach(function (data) { + var result = send(data.data); + if (!(result instanceof Array) || result.length === 0) { + return; + } + data.callback(result); + }); + timeout = setTimeout(poll, c.ETH_POLLING_TIMEOUT); + }; + + poll(); + + return { + send: send, + setProvider: setProvider, + startPolling: startPolling, + stopPolling: stopPolling, + reset: reset + }; +}; + +module.exports = requestManager; + + +},{"./const":2,"./jsonrpc":10}],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 . +*/ +/** @file shh.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/// @returns an array of objects describing web3.shh api methods +var methods = function () { + return [ + { name: 'post', call: 'shh_post' }, + { name: 'newIdentity', call: 'shh_newIdentity' }, + { name: 'haveIdentity', call: 'shh_haveIdentity' }, + { name: 'newGroup', call: 'shh_newGroup' }, + { name: 'addToGroup', call: 'shh_addToGroup' } + ]; +}; + +module.exports = { + methods: methods +}; + + +},{}],14:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1218,7 +1409,7 @@ module.exports = { }; -},{"./formatters":6}],12:[function(require,module,exports){ +},{"./formatters":8}],15:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1328,6 +1519,7 @@ var filterEvents = function (json) { /// TODO: use BigNumber.js to parse int /// TODO: add tests for it! var toEth = function (str) { + /*jshint maxcomplexity:7 */ var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; var unit = 0; var units = c.ETH_UNITS; @@ -1362,7 +1554,58 @@ module.exports = { }; -},{"./const":2}],13:[function(require,module,exports){ +},{"./const":2}],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 . +*/ +/** @file watches.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/// @returns an array of objects describing web3.eth.watch api methods +var eth = function () { + var newFilter = function (args) { + return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; + }; + + return [ + { name: 'newFilter', call: newFilter }, + { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, + { name: 'getMessages', call: 'eth_filterLogs' } + ]; +}; + +/// @returns an array of objects describing web3.shh.watch api methods +var shh = function () { + return [ + { name: 'newFilter', call: 'shh_newFilter' }, + { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, + { name: 'getMessages', call: 'shh_getMessages' } + ]; +}; + +module.exports = { + eth: eth, + shh: shh +}; + + +},{}],17:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1392,7 +1635,13 @@ if ("build" !== 'build') {/* var BigNumber = require('bignumber.js'); */} +var eth = require('./eth'); +var db = require('./db'); +var shh = require('./shh'); +var watches = require('./watches'); +var filter = require('./filter'); var utils = require('./utils'); +var requestManager = require('./requestmanager'); /// @returns an array of objects describing web3 api methods var web3Methods = function () { @@ -1401,100 +1650,6 @@ var web3Methods = function () { ]; }; -/// @returns an array of objects describing web3.eth api methods -var ethMethods = function () { - var blockCall = function (args) { - return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; - }; - - var transactionCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; - }; - - var uncleCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; - }; - - var methods = [ - { name: 'balanceAt', call: 'eth_balanceAt' }, - { name: 'register', call: 'eth_register' }, - { name: 'unregister', call: 'eth_unregister' }, - { name: 'stateAt', call: 'eth_stateAt' }, - { name: 'storageAt', call: 'eth_storageAt' }, - { name: 'countAt', call: 'eth_countAt'}, - { name: 'codeAt', call: 'eth_codeAt' }, - { name: 'transact', call: 'eth_transact' }, - { name: 'call', call: 'eth_call' }, - { name: 'block', call: blockCall }, - { name: 'transaction', call: transactionCall }, - { name: 'uncle', call: uncleCall }, - { name: 'compilers', call: 'eth_compilers' }, - { name: 'flush', call: 'eth_flush' }, - { name: 'lll', call: 'eth_lll' }, - { name: 'solidity', call: 'eth_solidity' }, - { name: 'serpent', call: 'eth_serpent' }, - { name: 'logs', call: 'eth_logs' } - ]; - return methods; -}; - -/// @returns an array of objects describing web3.eth api properties -var ethProperties = function () { - return [ - { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, - { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, - { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, - { name: 'gasPrice', getter: 'eth_gasPrice' }, - { name: 'accounts', getter: 'eth_accounts' }, - { name: 'peerCount', getter: 'eth_peerCount' }, - { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, - { name: 'number', getter: 'eth_number'} - ]; -}; - -/// @returns an array of objects describing web3.db api methods -var dbMethods = function () { - return [ - { name: 'put', call: 'db_put' }, - { name: 'get', call: 'db_get' }, - { name: 'putString', call: 'db_putString' }, - { name: 'getString', call: 'db_getString' } - ]; -}; - -/// @returns an array of objects describing web3.shh api methods -var shhMethods = function () { - return [ - { name: 'post', call: 'shh_post' }, - { name: 'newIdentity', call: 'shh_newIdentity' }, - { name: 'haveIdentity', call: 'shh_haveIdentity' }, - { name: 'newGroup', call: 'shh_newGroup' }, - { name: 'addToGroup', call: 'shh_addToGroup' } - ]; -}; - -/// @returns an array of objects describing web3.eth.watch api methods -var ethWatchMethods = function () { - var newFilter = function (args) { - return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; - }; - - return [ - { name: 'newFilter', call: newFilter }, - { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, - { name: 'getMessages', call: 'eth_filterLogs' } - ]; -}; - -/// @returns an array of objects describing web3.shh.watch api methods -var shhWatchMethods = function () { - return [ - { name: 'newFilter', call: 'shh_newFilter' }, - { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, - { name: 'getMessages', call: 'shh_getMessages' } - ]; -}; - /// creates methods in a given object based on method description on input /// setups api calls for these methods var setupMethods = function (obj, methods) { @@ -1502,7 +1657,7 @@ var setupMethods = function (obj, methods) { obj[method.name] = function () { var args = Array.prototype.slice.call(arguments); var call = typeof method.call === 'function' ? method.call(args) : method.call; - return web3.provider.send({ + return web3.manager.send({ method: call, params: args }); @@ -1516,14 +1671,14 @@ var setupProperties = function (obj, properties) { properties.forEach(function (property) { var proto = {}; proto.get = function () { - return web3.provider.send({ + return web3.manager.send({ method: property.getter }); }; if (property.setter) { proto.set = function (val) { - return web3.provider.send({ + return web3.manager.send({ method: property.setter, params: [val] }); @@ -1533,10 +1688,32 @@ var setupProperties = function (obj, properties) { }); }; +/*jshint maxparams:4 */ +var startPolling = function (method, id, callback, uninstall) { + web3.manager.startPolling({ + method: method, + params: [id] + }, id, callback, uninstall); +}; +/*jshint maxparams:3 */ + +var stopPolling = function (id) { + web3.manager.stopPolling(id); +}; + +var ethWatch = { + startPolling: startPolling.bind(null, 'eth_changed'), + stopPolling: stopPolling +}; + +var shhWatch = { + startPolling: startPolling.bind(null, 'shh_changed'), + stopPolling: stopPolling +}; + /// setups web3 object, and it's in-browser executed methods var web3 = { - _callbacks: {}, - _events: {}, + manager: requestManager(), providers: {}, /// @returns ascii string representation of hex value prefixed with 0x @@ -1575,12 +1752,15 @@ var web3 = { /// @param filter may be a string, object or event /// @param indexed is optional, this is an object with optional event indexed params /// @param options is optional, this is an object with optional event options ('max'...) - watch: function (filter, indexed, options) { - if (filter._isEvent) { - return filter(indexed, options); + /// TODO: fix it, 4 params? no way + /*jshint maxparams:4 */ + watch: function (fil, indexed, options, formatter) { + if (fil._isEvent) { + return fil(indexed, options); } - return new web3.filter(filter, ethWatch); + return filter(fil, ethWatch, formatter); } + /*jshint maxparams:3 */ }, /// db object prototype @@ -1588,54 +1768,44 @@ var web3 = { /// shh object prototype shh: { - /// @param filter may be a string, object or event - watch: function (filter, indexed) { - return new web3.filter(filter, shhWatch); + watch: function (fil) { + return filter(fil, shhWatch); } }, + setProvider: function (provider) { + web3.manager.setProvider(provider); + }, + + /// Should be called to reset state of web3 object + /// Resets everything except manager + reset: function () { + web3.manager.reset(); + } }; /// setups all api methods setupMethods(web3, web3Methods()); -setupMethods(web3.eth, ethMethods()); -setupProperties(web3.eth, ethProperties()); -setupMethods(web3.db, dbMethods()); -setupMethods(web3.shh, shhMethods()); - -var ethWatch = { - changed: 'eth_changed' -}; - -setupMethods(ethWatch, ethWatchMethods()); - -var shhWatch = { - changed: 'shh_changed' -}; - -setupMethods(shhWatch, shhWatchMethods()); - -web3.setProvider = function(provider) { - web3.provider.set(provider); -}; +setupMethods(web3.eth, eth.methods()); +setupProperties(web3.eth, eth.properties()); +setupMethods(web3.db, db.methods()); +setupMethods(web3.shh, shh.methods()); +setupMethods(ethWatch, watches.eth()); +setupMethods(shhWatch, watches.shh()); module.exports = web3; -},{"./utils":12}],"web3":[function(require,module,exports){ +},{"./db":4,"./eth":5,"./filter":7,"./requestmanager":12,"./shh":13,"./utils":15,"./watches":16}],"web3":[function(require,module,exports){ var web3 = require('./lib/web3'); -var ProviderManager = require('./lib/providermanager'); -web3.provider = new ProviderManager(); -web3.filter = require('./lib/filter'); web3.providers.HttpSyncProvider = require('./lib/httpsync'); web3.providers.QtSyncProvider = require('./lib/qtsync'); web3.eth.contract = require('./lib/contract'); web3.abi = require('./lib/abi'); - module.exports = web3; -},{"./lib/abi":1,"./lib/contract":3,"./lib/filter":5,"./lib/httpsync":7,"./lib/providermanager":9,"./lib/qtsync":10,"./lib/web3":13}]},{},["web3"]) +},{"./lib/abi":1,"./lib/contract":3,"./lib/httpsync":9,"./lib/qtsync":11,"./lib/web3":17}]},{},["web3"]) //# sourceMappingURL=ethereum.js.map diff --git a/rpc/api.go b/rpc/api.go index 3b4a914ef..28024c206 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -52,19 +52,20 @@ type EthereumApi struct { db ethutil.Database - defaultBlockAge int + defaultBlockAge int64 } func NewEthereumApi(eth *xeth.XEth) *EthereumApi { db, _ := ethdb.NewLDBDatabase("dapps") api := &EthereumApi{ - eth: eth, - mux: eth.Backend().EventMux(), - quit: make(chan struct{}), - filterManager: filter.NewFilterManager(eth.Backend().EventMux()), - logs: make(map[int]*logFilter), - messages: make(map[int]*whisperFilter), - db: db, + eth: eth, + mux: eth.Backend().EventMux(), + quit: make(chan struct{}), + filterManager: filter.NewFilterManager(eth.Backend().EventMux()), + logs: make(map[int]*logFilter), + messages: make(map[int]*whisperFilter), + db: db, + defaultBlockAge: -1, } go api.filterManager.Start() go api.start() @@ -72,6 +73,22 @@ func NewEthereumApi(eth *xeth.XEth) *EthereumApi { return api } +func (self *EthereumApi) setStateByBlockNumber(num int64) { + chain := self.xeth().Backend().ChainManager() + var block *types.Block + + if self.defaultBlockAge < 0 { + num = chain.CurrentBlock().Number().Int64() + num + 1 + } + block = chain.GetBlockByNumber(uint64(num)) + + if block != nil { + self.useState(state.New(block.Root(), self.xeth().Backend().Db())) + } else { + self.useState(chain.State()) + } +} + func (self *EthereumApi) start() { timer := time.NewTicker(filterTickerTime) events := self.mux.Subscribe(core.ChainEvent{}) @@ -83,12 +100,7 @@ done: switch ev.(type) { case core.ChainEvent: if self.defaultBlockAge < 0 { - chain := self.xeth().Backend().ChainManager() - block := chain.GetBlockByNumber(chain.CurrentBlock().Number().Uint64() - uint64(self.defaultBlockAge)) - if block != nil { - statedb := state.New(block.Root(), self.db) - self.useState(statedb) - } + self.setStateByBlockNumber(self.defaultBlockAge) } } case <-timer.C: @@ -239,21 +251,6 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { return nil } -/* -func unlockAccount(server, account *Account) bool { - pwd, status := server.PasswordDialog() - switch status { - case Ok: - if !account.Unlock([]byte(pwd)) { - return unlockAccount(account) - } - return true - default: - return false - } -} -*/ - func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { if len(args.Gas) == 0 { args.Gas = defaultGas.String() @@ -378,8 +375,10 @@ func (p *EthereumApi) GetDefaultBlockAge(reply *interface{}) error { return nil } -func (p *EthereumApi) SetDefaultBlockAge(defaultBlockAge int, reply *interface{}) error { +func (p *EthereumApi) SetDefaultBlockAge(defaultBlockAge int64, reply *interface{}) error { p.defaultBlockAge = defaultBlockAge + p.setStateByBlockNumber(p.defaultBlockAge) + *reply = true return nil } @@ -531,7 +530,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error if err != nil { return err } - return p.SetDefaultBlockAge(args, reply) + return p.SetDefaultBlockAge(int64(args), reply) case "eth_peerCount": return p.GetPeerCount(reply) case "eth_number": @@ -720,7 +719,7 @@ func (self *EthereumApi) useState(statedb *state.StateDB) { self.xethMu.Lock() defer self.xethMu.Unlock() - self.eth = self.xeth().UseState(statedb) + self.eth = self.eth.UseState(statedb) } func t(f ui.Frontend) { From e235b57234a68a8a39cfe7691a1825d8c6bb3443 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Feb 2015 18:39:05 +0100 Subject: [PATCH 24/36] Fixed consensus issue for refunding * Refund should _always_ go to the origin --- core/block_processor.go | 8 ++++---- core/state_transition.go | 9 ++++++--- miner/worker.go | 4 ++-- state/dump.go | 2 +- state/state_object.go | 21 +++++++++++++++------ vm/vm.go | 5 +++-- 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/core/block_processor.go b/core/block_processor.go index f66d158b2..7eaeb5be0 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -48,9 +48,8 @@ type BlockProcessor struct { func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { sm := &BlockProcessor{ - db: db, - mem: make(map[string]*big.Int), - //Pow: ðash.Ethash{}, + db: db, + mem: make(map[string]*big.Int), Pow: ezp.New(), bc: chainManager, eventMux: eventMux, @@ -100,7 +99,8 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated // Notify all subscribers if !transientProcess { go self.eventMux.Post(TxPostEvent{tx}) - go self.eventMux.Post(statedb.Logs()) + logs := statedb.Logs() + go self.eventMux.Post(logs) } return receipt, txGas, err diff --git a/core/state_transition.go b/core/state_transition.go index 36ffa23d9..a065c4f6b 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -126,7 +126,7 @@ func (self *StateTransition) BuyGas() error { self.AddGas(self.msg.Gas()) self.initialGas.Set(self.msg.Gas()) - sender.SubAmount(MessageGasValue(self.msg)) + sender.SubBalance(MessageGasValue(self.msg)) return nil } @@ -251,13 +251,16 @@ func (self *StateTransition) RefundGas() { coinbase, sender := self.Coinbase(), self.From() // Return remaining gas remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice()) - sender.AddAmount(remaining) + fmt.Println("REFUND:", remaining) + sender.AddBalance(remaining) uhalf := new(big.Int).Div(self.GasUsed(), ethutil.Big2) for addr, ref := range self.state.Refunds() { refund := ethutil.BigMin(uhalf, ref) self.gas.Add(self.gas, refund) - self.state.AddBalance([]byte(addr), refund.Mul(refund, self.msg.GasPrice())) + addToIt := refund.Mul(refund, self.msg.GasPrice()) + fmt.Println("ADD TO IT", addToIt) + self.state.AddBalance([]byte(addr), addToIt) } coinbase.RefundGas(self.gas, self.msg.GasPrice()) diff --git a/miner/worker.go b/miner/worker.go index 1f3a52ab5..4f0909302 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -197,7 +197,7 @@ gasLimit: } self.eth.TxPool().RemoveSet(remove) - self.current.coinbase.AddAmount(core.BlockReward) + self.current.coinbase.AddBalance(core.BlockReward) self.current.state.Update(ethutil.Big0) self.push() @@ -225,7 +225,7 @@ func (self *worker) commitUncle(uncle *types.Header) error { } uncleAccount := self.current.state.GetAccount(uncle.Coinbase) - uncleAccount.AddAmount(uncleReward) + uncleAccount.AddBalance(uncleReward) self.current.coinbase.AddBalance(uncleReward) diff --git a/state/dump.go b/state/dump.go index 81895f1a3..073f89414 100644 --- a/state/dump.go +++ b/state/dump.go @@ -35,7 +35,7 @@ func (self *StateDB) Dump() []byte { storageIt := stateObject.State.trie.Iterator() for storageIt.Next() { - account.Storage[ethutil.Bytes2Hex(it.Key)] = ethutil.Bytes2Hex(it.Value) + account.Storage[ethutil.Bytes2Hex(storageIt.Key)] = ethutil.Bytes2Hex(storageIt.Value) } world.Accounts[ethutil.Bytes2Hex(it.Key)] = account } diff --git a/state/state_object.go b/state/state_object.go index 477b912a1..487952a02 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -19,6 +19,14 @@ func (self Code) String() string { type Storage map[string]*ethutil.Value +func (self Storage) String() (str string) { + for key, value := range self { + str += fmt.Sprintf("%X : %X\n", key, value.Bytes()) + } + + return +} + func (self Storage) Copy() Storage { cpy := make(Storage) for key, value := range self { @@ -119,10 +127,9 @@ func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value { } func (self *StateObject) SetStorage(key *big.Int, value *ethutil.Value) { self.SetState(key.Bytes(), value) - self.dirty = true } -func (self *StateObject) Storage() map[string]*ethutil.Value { +func (self *StateObject) Storage() Storage { return self.storage } @@ -172,20 +179,22 @@ func (c *StateObject) AddBalance(amount *big.Int) { statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount) } -func (c *StateObject) AddAmount(amount *big.Int) { c.AddBalance(amount) } func (c *StateObject) SubBalance(amount *big.Int) { c.SetBalance(new(big.Int).Sub(c.balance, amount)) statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount) } -func (c *StateObject) SubAmount(amount *big.Int) { c.SubBalance(amount) } func (c *StateObject) SetBalance(amount *big.Int) { c.balance = amount c.dirty = true } +func (c *StateObject) St() Storage { + return c.storage +} + // // Gas setters and getters // @@ -198,7 +207,7 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error { return fmt.Errorf("insufficient amount: %v, %v", c.balance, total) } - c.SubAmount(total) + c.SubBalance(total) c.dirty = true @@ -221,7 +230,7 @@ func (self *StateObject) BuyGas(gas, price *big.Int) error { rGas := new(big.Int).Set(gas) rGas.Mul(rGas, price) - self.AddAmount(rGas) + self.AddBalance(rGas) self.dirty = true diff --git a/vm/vm.go b/vm/vm.go index f9efeed96..7aeeea661 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -664,6 +664,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I } addr = ref.Address() + fmt.Printf("CREATE %X\n", addr) stack.Push(ethutil.BigD(addr)) } @@ -727,7 +728,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I self.Printf(" => (%x) %v", receiver.Address()[:4], balance) - receiver.AddAmount(balance) + receiver.AddBalance(balance) statedb.Delete(context.Address()) fallthrough @@ -828,7 +829,7 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo // 0 => non 0 mult = ethutil.Big3 } else if len(val) > 0 && len(y.Bytes()) == 0 { - statedb.Refund(caller.Address(), GasSStoreRefund) + statedb.Refund(self.env.Origin(), GasSStoreRefund) mult = ethutil.Big0 } else { From 3ab05ddd25aa805510ac304c1fd8f7c5c620dff4 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Feb 2015 20:26:37 +0100 Subject: [PATCH 25/36] Bump --- cmd/ethereum/main.go | 2 +- cmd/mist/assets/qml/main.qml | 2 +- cmd/mist/main.go | 2 +- core/state_transition.go | 5 +---- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 9c840de58..45e6f7b93 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -37,7 +37,7 @@ import ( const ( ClientIdentifier = "Ethereum(G)" - Version = "0.8.5" + Version = "0.8.6" ) var clilogger = logger.NewLogger("CLI") diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index e80bd87e0..a909916b7 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -964,7 +964,7 @@ ApplicationWindow { anchors.top: parent.top anchors.topMargin: 30 font.pointSize: 12 - text: "

Mist (0.8.5)


Development

Jeffrey Wilcke
Viktor Trón
Felix Lange
Taylor Gerring
Daniel Nagy
Gustav Simonsson

UX/UI

Alex van de Sande
Fabian Vogelsteller" + text: "

Mist (0.8.6)


Development

Jeffrey Wilcke
Viktor Trón
Felix Lange
Taylor Gerring
Daniel Nagy
Gustav Simonsson

UX/UI

Alex van de Sande
Fabian Vogelsteller" } } diff --git a/cmd/mist/main.go b/cmd/mist/main.go index ad6eb5bec..3abe16767 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -36,7 +36,7 @@ import ( const ( ClientIdentifier = "Mist" - Version = "0.8.5" + Version = "0.8.6" ) var ethereum *eth.Ethereum diff --git a/core/state_transition.go b/core/state_transition.go index a065c4f6b..7331fdd4a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -251,16 +251,13 @@ func (self *StateTransition) RefundGas() { coinbase, sender := self.Coinbase(), self.From() // Return remaining gas remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice()) - fmt.Println("REFUND:", remaining) sender.AddBalance(remaining) uhalf := new(big.Int).Div(self.GasUsed(), ethutil.Big2) for addr, ref := range self.state.Refunds() { refund := ethutil.BigMin(uhalf, ref) self.gas.Add(self.gas, refund) - addToIt := refund.Mul(refund, self.msg.GasPrice()) - fmt.Println("ADD TO IT", addToIt) - self.state.AddBalance([]byte(addr), addToIt) + self.state.AddBalance([]byte(addr), refund.Mul(refund, self.msg.GasPrice())) } coinbase.RefundGas(self.gas, self.msg.GasPrice()) From cc5c8a444dbc23501ba1a131eb2334f4b5e1ce9f Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Feb 2015 20:36:54 +0100 Subject: [PATCH 26/36] Report proper database directory. Closes #397 --- eth/backend.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eth/backend.go b/eth/backend.go index 0e5d24429..f67f9c78b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -147,7 +147,8 @@ func New(config *Config) (*Ethereum, error) { d, _ := db.Get([]byte("ProtocolVersion")) protov := ethutil.NewValue(d).Uint() if protov != ProtocolVersion && protov != 0 { - return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, ProtocolVersion, ethutil.Config.ExecPath+"/database") + path := path.Join(config.DataDir, "database") + return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, ProtocolVersion, path) } // Create new keymanager From d90b71bc5518e1f794268fe58f62525302287bd3 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 26 Feb 2015 21:01:40 +0100 Subject: [PATCH 27/36] Check source directroy for assets as last resort --- ethutil/common.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ethutil/common.go b/ethutil/common.go index c4e7415dc..5fd9a06e2 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -15,11 +15,13 @@ import ( func DefaultAssetPath() string { var assetPath string + pwd, _ := os.Getwd() + srcdir := path.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. - pwd, _ := os.Getwd() - if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist") { + if pwd == srcdir { assetPath = path.Join(pwd, "assets") } else { switch runtime.GOOS { @@ -34,6 +36,12 @@ func DefaultAssetPath() string { default: assetPath = "." } + + // 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") + } } return assetPath } From a39c73672efd542b53b79fdadb89afec93498cc1 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 26 Feb 2015 21:04:26 +0100 Subject: [PATCH 28/36] bump last resort check out of ifelse --- ethutil/common.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ethutil/common.go b/ethutil/common.go index 5fd9a06e2..9b66763b8 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -36,13 +36,14 @@ func DefaultAssetPath() string { default: assetPath = "." } - - // 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") - } } + + // 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") + } + return assetPath } From ad3a21f260a0b13048046f6c76fe5f47bdcc46de Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Fri, 27 Feb 2015 00:52:01 +0100 Subject: [PATCH 29/36] Bump to latest versions for Docker --- Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1f6555d1a..b7e23aaab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:14.04.1 +FROM ubuntu:14.04.2 ## Environment setup ENV HOME /root @@ -12,22 +12,22 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get upgrade -y RUN apt-get install -y git mercurial build-essential software-properties-common wget pkg-config libgmp3-dev libreadline6-dev libpcre3-dev libpcre++-dev -## Install Qt5.4 (not required for CLI) -# RUN add-apt-repository ppa:beineri/opt-qt54-trusty -y +## Install Qt5.4.1 (not required for CLI) +# RUN add-apt-repository ppa:beineri/opt-qt541-trusty -y # RUN apt-get update -y # RUN apt-get install -y qt54quickcontrols qt54webengine mesa-common-dev libglu1-mesa-dev # ENV PKG_CONFIG_PATH /opt/qt54/lib/pkgconfig # Install Golang -RUN wget https://storage.googleapis.com/golang/go1.4.1.linux-amd64.tar.gz +RUN wget https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz RUN tar -C /usr/local -xzf go*.tar.gz && go version # this is a workaround, to make sure that docker's cache is invalidated whenever the git repo changes ADD https://api.github.com/repos/ethereum/go-ethereum/git/refs/heads/develop file_does_not_exist ## Fetch and install go-ethereum -RUN go get -v github.com/tools/godep -RUN go get -v -d github.com/ethereum/go-ethereum/... +RUN go get github.com/tools/godep +RUN go get -d github.com/ethereum/go-ethereum/... WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum RUN git checkout develop RUN godep restore From f6e821fd335911c26d515094ac0af4fa69526279 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Sat, 28 Feb 2015 01:00:42 +0100 Subject: [PATCH 30/36] Add flag to set RPC port --- cmd/ethereum/flags.go | 70 ++++++++++++++++++++++--------------------- cmd/ethereum/main.go | 2 +- cmd/mist/flags.go | 52 ++++++++++++++++---------------- cmd/mist/main.go | 2 +- cmd/utils/cmd.go | 4 +-- rpc/http/server.go | 4 +-- 6 files changed, 69 insertions(+), 65 deletions(-) diff --git a/cmd/ethereum/flags.go b/cmd/ethereum/flags.go index 7d410c8e4..79a60039c 100644 --- a/cmd/ethereum/flags.go +++ b/cmd/ethereum/flags.go @@ -36,40 +36,41 @@ import ( ) var ( - Identifier string - KeyRing string - DiffTool bool - DiffType string - KeyStore string - StartRpc bool - StartWebSockets bool - RpcPort int - WsPort int - OutboundPort string - ShowGenesis bool - AddPeer string - MaxPeer int - GenAddr bool - BootNodes string - NodeKey *ecdsa.PrivateKey - NAT nat.Interface - SecretFile string - ExportDir string - NonInteractive bool - Datadir string - LogFile string - ConfigFile string - DebugFile string - LogLevel int - LogFormat string - Dump bool - DumpHash string - DumpNumber int - VmType int - ImportChain string - SHH bool - Dial bool - PrintVersion bool + Identifier string + KeyRing string + DiffTool bool + DiffType string + KeyStore string + StartRpc bool + StartWebSockets bool + RpcListenAddress string + RpcPort int + WsPort int + OutboundPort string + ShowGenesis bool + AddPeer string + MaxPeer int + GenAddr bool + BootNodes string + NodeKey *ecdsa.PrivateKey + NAT nat.Interface + SecretFile string + ExportDir string + NonInteractive bool + Datadir string + LogFile string + ConfigFile string + DebugFile string + LogLevel int + LogFormat string + Dump bool + DumpHash string + DumpNumber int + VmType int + ImportChain string + SHH bool + Dial bool + PrintVersion bool ) // flags specific to cli client @@ -93,6 +94,7 @@ func Init() { flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use") flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)") + flag.StringVar(&RpcListenAddress, "rpcaddr", "127.0.0.1", "address for json-rpc server to listen on") flag.IntVar(&RpcPort, "rpcport", 8545, "port to start json-rpc server on") flag.IntVar(&WsPort, "wsport", 40404, "port to start websocket rpc server on") flag.BoolVar(&StartRpc, "rpc", false, "start rpc server") diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 45e6f7b93..8afab5d28 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -128,7 +128,7 @@ func main() { } if StartRpc { - utils.StartRpc(ethereum, RpcPort) + utils.StartRpc(ethereum, RpcListenAddress, RpcPort) } if StartWebSockets { diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go index 0010df826..1ffeb1915 100644 --- a/cmd/mist/flags.go +++ b/cmd/mist/flags.go @@ -37,31 +37,32 @@ import ( ) var ( - Identifier string - KeyRing string - KeyStore string - StartRpc bool - StartWebSockets bool - RpcPort int - WsPort int - OutboundPort string - ShowGenesis bool - AddPeer string - MaxPeer int - GenAddr bool - BootNodes string - NodeKey *ecdsa.PrivateKey - NAT nat.Interface - SecretFile string - ExportDir string - NonInteractive bool - Datadir string - LogFile string - ConfigFile string - DebugFile string - LogLevel int - VmType int - MinerThreads int + Identifier string + KeyRing string + KeyStore string + StartRpc bool + StartWebSockets bool + RpcListenAddress string + RpcPort int + WsPort int + OutboundPort string + ShowGenesis bool + AddPeer string + MaxPeer int + GenAddr bool + BootNodes string + NodeKey *ecdsa.PrivateKey + NAT nat.Interface + SecretFile string + ExportDir string + NonInteractive bool + Datadir string + LogFile string + ConfigFile string + DebugFile string + LogLevel int + VmType int + MinerThreads int ) // flags specific to gui client @@ -79,6 +80,7 @@ func Init() { flag.StringVar(&Identifier, "id", "", "Custom client identifier") flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use") flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)") + flag.StringVar(&RpcListenAddress, "rpcaddr", "127.0.0.1", "address for json-rpc server to listen on") flag.IntVar(&RpcPort, "rpcport", 8545, "port to start json-rpc server on") flag.IntVar(&WsPort, "wsport", 40404, "port to start websocket rpc server on") flag.BoolVar(&StartRpc, "rpc", true, "start rpc server") diff --git a/cmd/mist/main.go b/cmd/mist/main.go index 3abe16767..c9a07bfde 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -73,7 +73,7 @@ func run() error { utils.KeyTasks(ethereum.KeyManager(), KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive) if StartRpc { - utils.StartRpc(ethereum, RpcPort) + utils.StartRpc(ethereum, RpcListenAddress, RpcPort) } if StartWebSockets { diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index a36c10e3b..5c7ec3221 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -160,9 +160,9 @@ func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, Secre clilogger.Infof("Main address %x\n", keyManager.Address()) } -func StartRpc(ethereum *eth.Ethereum, RpcPort int) { +func StartRpc(ethereum *eth.Ethereum, RpcListenAddress string, RpcPort int) { var err error - ethereum.RpcServer, err = rpchttp.NewRpcHttpServer(xeth.New(ethereum), RpcPort) + ethereum.RpcServer, err = rpchttp.NewRpcHttpServer(xeth.New(ethereum), RpcListenAddress, RpcPort) if err != nil { clilogger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err) } else { diff --git a/rpc/http/server.go b/rpc/http/server.go index fa66eed48..063c333d0 100644 --- a/rpc/http/server.go +++ b/rpc/http/server.go @@ -29,8 +29,8 @@ import ( var rpchttplogger = logger.NewLogger("RPC-HTTP") var JSON rpc.JsonWrapper -func NewRpcHttpServer(pipe *xeth.XEth, port int) (*RpcHttpServer, error) { - sport := fmt.Sprintf("127.0.0.1:%d", port) +func NewRpcHttpServer(pipe *xeth.XEth, address string, port int) (*RpcHttpServer, error) { + sport := fmt.Sprintf("%s:%d", address, port) l, err := net.Listen("tcp", sport) if err != nil { return nil, err From ea0517b5396efc7bd47f820ec0263f68f76f29a4 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Sat, 28 Feb 2015 01:04:54 +0100 Subject: [PATCH 31/36] Report RPC listening address in logs --- rpc/http/server.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rpc/http/server.go b/rpc/http/server.go index 063c333d0..452b7c9af 100644 --- a/rpc/http/server.go +++ b/rpc/http/server.go @@ -41,6 +41,7 @@ func NewRpcHttpServer(pipe *xeth.XEth, address string, port int) (*RpcHttpServer quit: make(chan bool), pipe: pipe, port: port, + addr: address, }, nil } @@ -49,6 +50,7 @@ type RpcHttpServer struct { listener net.Listener pipe *xeth.XEth port int + addr string } func (s *RpcHttpServer) exitHandler() { @@ -69,7 +71,7 @@ func (s *RpcHttpServer) Stop() { } func (s *RpcHttpServer) Start() { - rpchttplogger.Infof("Starting RPC-HTTP server on port %d", s.port) + rpchttplogger.Infof("Starting RPC-HTTP server on %s:%d", s.addr, s.port) go s.exitHandler() api := rpc.NewEthereumApi(s.pipe) From 6ea7aae29ca71e6fe857b85296b574e09df57184 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 28 Feb 2015 19:15:57 +0100 Subject: [PATCH 32/36] Removed some methods from the JS REPL --- cmd/ethereum/flags.go | 6 ++++ cmd/ethereum/main.go | 3 ++ cmd/ethereum/repl/repl.go | 1 + core/genesis.go | 2 -- javascript/javascript_runtime.go | 58 +++++--------------------------- javascript/js_lib.go | 2 +- state/dump.go | 7 ++-- 7 files changed, 25 insertions(+), 54 deletions(-) diff --git a/cmd/ethereum/flags.go b/cmd/ethereum/flags.go index 7d410c8e4..c42083160 100644 --- a/cmd/ethereum/flags.go +++ b/cmd/ethereum/flags.go @@ -135,6 +135,12 @@ func Init() { flag.Parse() + // When the javascript console is started log to a file instead + // of stdout + if StartJsConsole { + LogFile = path.Join(Datadir, "ethereum.log") + } + var err error if NAT, err = nat.Parse(*natstr); err != nil { log.Fatalf("-nat: %v", err) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 45e6f7b93..1562165cd 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -137,6 +137,9 @@ func main() { utils.StartEthereum(ethereum) + latestBlock := ethereum.ChainManager().CurrentBlock() + fmt.Printf("Welcome to the FRONTIER\n") + if StartJsConsole { InitJsConsole(ethereum) } else if len(InputFile) > 0 { diff --git a/cmd/ethereum/repl/repl.go b/cmd/ethereum/repl/repl.go index 4a7880ff4..11b812617 100644 --- a/cmd/ethereum/repl/repl.go +++ b/cmd/ethereum/repl/repl.go @@ -60,6 +60,7 @@ func (self *JSRepl) Start() { if !self.running { self.running = true repllogger.Infoln("init JS Console") + reader := bufio.NewReader(self.history) for { line, err := reader.ReadString('\n') diff --git a/core/genesis.go b/core/genesis.go index 75b4fc100..decffc541 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -51,8 +51,6 @@ func GenesisBlock(db ethutil.Database) *types.Block { statedb.Sync() genesis.Header().Root = statedb.Root() - fmt.Printf("+++ genesis +++\nRoot: %x\nHash: %x\n", genesis.Header().Root, genesis.Hash()) - return genesis } diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go index 0aa0f73e2..beaca45b9 100644 --- a/javascript/javascript_runtime.go +++ b/javascript/javascript_runtime.go @@ -24,7 +24,7 @@ var jsrelogger = logger.NewLogger("JSRE") type JSRE struct { ethereum *eth.Ethereum Vm *otto.Otto - pipe *xeth.XEth + xeth *xeth.XEth events event.Subscription @@ -67,7 +67,7 @@ func NewJSRE(ethereum *eth.Ethereum) *JSRE { // We have to make sure that, whoever calls this, calls "Stop" go re.mainLoop() - re.Bind("eth", &JSEthereum{re.pipe, re.Vm, ethereum}) + re.Bind("eth", &JSEthereum{re.xeth, re.Vm, ethereum}) re.initStdFuncs() @@ -113,12 +113,10 @@ func (self *JSRE) mainLoop() { func (self *JSRE) initStdFuncs() { t, _ := self.Vm.Get("eth") eth := t.Object() - eth.Set("watch", self.watch) - eth.Set("addPeer", self.addPeer) + eth.Set("connect", self.connect) eth.Set("require", self.require) eth.Set("stopMining", self.stopMining) eth.Set("startMining", self.startMining) - eth.Set("execBlock", self.execBlock) eth.Set("dump", self.dump) eth.Set("export", self.export) } @@ -152,7 +150,8 @@ func (self *JSRE) dump(call otto.FunctionCall) otto.Value { } statedb := state.New(block.Root(), self.ethereum.Db()) - v, _ := self.Vm.ToValue(statedb.Dump()) + + v, _ := self.Vm.ToValue(statedb.RawDump()) return v } @@ -167,36 +166,7 @@ func (self *JSRE) startMining(call otto.FunctionCall) otto.Value { return v } -// eth.watch -func (self *JSRE) watch(call otto.FunctionCall) otto.Value { - addr, _ := call.Argument(0).ToString() - var storageAddr string - var cb otto.Value - var storageCallback bool - if len(call.ArgumentList) > 2 { - storageCallback = true - storageAddr, _ = call.Argument(1).ToString() - cb = call.Argument(2) - } else { - cb = call.Argument(1) - } - - if storageCallback { - self.objectCb[addr+storageAddr] = append(self.objectCb[addr+storageAddr], cb) - - // event := "storage:" + string(ethutil.Hex2Bytes(addr)) + ":" + string(ethutil.Hex2Bytes(storageAddr)) - // self.ethereum.EventMux().Subscribe(event, self.changeChan) - } else { - self.objectCb[addr] = append(self.objectCb[addr], cb) - - // event := "object:" + string(ethutil.Hex2Bytes(addr)) - // self.ethereum.EventMux().Subscribe(event, self.changeChan) - } - - return otto.UndefinedValue() -} - -func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value { +func (self *JSRE) connect(call otto.FunctionCall) otto.Value { nodeURL, err := call.Argument(0).ToString() if err != nil { return otto.FalseValue() @@ -222,22 +192,12 @@ func (self *JSRE) require(call otto.FunctionCall) otto.Value { return t } -func (self *JSRE) execBlock(call otto.FunctionCall) otto.Value { - hash, err := call.Argument(0).ToString() - if err != nil { - return otto.UndefinedValue() - } - - err = utils.BlockDo(self.ethereum, ethutil.Hex2Bytes(hash)) - if err != nil { - fmt.Println(err) +func (self *JSRE) export(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) == 0 { + fmt.Println("err: require file name") return otto.FalseValue() } - return otto.TrueValue() -} - -func (self *JSRE) export(call otto.FunctionCall) otto.Value { fn, err := call.Argument(0).ToString() if err != nil { fmt.Println(err) diff --git a/javascript/js_lib.go b/javascript/js_lib.go index dd1fe5f4d..f828ca389 100644 --- a/javascript/js_lib.go +++ b/javascript/js_lib.go @@ -16,7 +16,7 @@ function pp(object) { str += " ]"; } else if(typeof(object) === "object") { str += "{ "; - var last = Object.keys(object).sort().pop() + var last = Object.keys(object).pop() for(var k in object) { str += k + ": " + pp(object[k]); diff --git a/state/dump.go b/state/dump.go index 073f89414..2c611d76b 100644 --- a/state/dump.go +++ b/state/dump.go @@ -20,7 +20,7 @@ type World struct { Accounts map[string]Account `json:"accounts"` } -func (self *StateDB) Dump() []byte { +func (self *StateDB) RawDump() World { world := World{ Root: ethutil.Bytes2Hex(self.trie.Root()), Accounts: make(map[string]Account), @@ -39,8 +39,11 @@ func (self *StateDB) Dump() []byte { } world.Accounts[ethutil.Bytes2Hex(it.Key)] = account } + return world +} - json, err := json.MarshalIndent(world, "", " ") +func (self *StateDB) Dump() []byte { + json, err := json.MarshalIndent(self.RawDump(), "", " ") if err != nil { fmt.Println("dump err", err) } From 7adf065b10a0a05aea759e7f29a2a4acfa0f5521 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 28 Feb 2015 20:14:01 +0100 Subject: [PATCH 33/36] Simple effective VM optimisation Added a debug flag to the VM which determines if VM output is shown regardless of the log level set. --- vm/vm.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/vm/vm.go b/vm/vm.go index 7aeeea661..1f386d47c 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -16,6 +16,8 @@ type Vm struct { logStr string err error + // For logging + debug bool Dbg Debugger @@ -32,7 +34,7 @@ func New(env Environment) *Vm { lt = LogTyDiff } - return &Vm{env: env, logTy: lt, Recoverable: true} + return &Vm{debug: false, env: env, logTy: lt, Recoverable: true} } func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { @@ -938,17 +940,21 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context * } func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine { - if self.logTy == LogTyPretty { - self.logStr += fmt.Sprintf(format, v...) + if self.debug { + if self.logTy == LogTyPretty { + self.logStr += fmt.Sprintf(format, v...) + } } return self } func (self *Vm) Endl() VirtualMachine { - if self.logTy == LogTyPretty { - vmlogger.Debugln(self.logStr) - self.logStr = "" + if self.debug { + if self.logTy == LogTyPretty { + vmlogger.Debugln(self.logStr) + self.logStr = "" + } } return self From 7ab13e0f17e9d1b783d93bd4952cc6d7cc77ea7f Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 28 Feb 2015 20:24:20 +0100 Subject: [PATCH 34/36] Unused variable --- cmd/ethereum/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 1562165cd..f72b11e14 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -137,7 +137,6 @@ func main() { utils.StartEthereum(ethereum) - latestBlock := ethereum.ChainManager().CurrentBlock() fmt.Printf("Welcome to the FRONTIER\n") if StartJsConsole { From fdf939a6f9b5360d76415c7118969d92af2774f9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 28 Feb 2015 23:01:41 +0100 Subject: [PATCH 35/36] Fixed miner threads for ethereum CLI --- cmd/ethereum/flags.go | 3 +++ cmd/ethereum/main.go | 37 +++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/cmd/ethereum/flags.go b/cmd/ethereum/flags.go index 1a0c13c82..356571a23 100644 --- a/cmd/ethereum/flags.go +++ b/cmd/ethereum/flags.go @@ -27,6 +27,7 @@ import ( "log" "os" "path" + "runtime" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethutil" @@ -71,6 +72,7 @@ var ( SHH bool Dial bool PrintVersion bool + MinerThreads int ) // flags specific to cli client @@ -121,6 +123,7 @@ func Init() { flag.BoolVar(&StartMining, "mine", false, "start dagger mining") flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console") flag.BoolVar(&PrintVersion, "version", false, "prints version number") + flag.IntVar(&MinerThreads, "minerthreads", runtime.NumCPU(), "number of miner threads") // Network stuff var ( diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 07ef0d5dd..f79f948d1 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -62,20 +62,21 @@ func main() { utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") ethereum, err := eth.New(ð.Config{ - Name: p2p.MakeName(ClientIdentifier, Version), - KeyStore: KeyStore, - DataDir: Datadir, - LogFile: LogFile, - LogLevel: LogLevel, - LogFormat: LogFormat, - MaxPeers: MaxPeer, - Port: OutboundPort, - NAT: NAT, - KeyRing: KeyRing, - Shh: true, - Dial: Dial, - BootNodes: BootNodes, - NodeKey: NodeKey, + Name: p2p.MakeName(ClientIdentifier, Version), + KeyStore: KeyStore, + DataDir: Datadir, + LogFile: LogFile, + LogLevel: LogLevel, + LogFormat: LogFormat, + MaxPeers: MaxPeer, + Port: OutboundPort, + NAT: NAT, + KeyRing: KeyRing, + Shh: true, + Dial: Dial, + BootNodes: BootNodes, + NodeKey: NodeKey, + MinerThreads: MinerThreads, }) if err != nil { @@ -113,10 +114,6 @@ func main() { return } - if StartMining { - utils.StartMining(ethereum) - } - if len(ImportChain) > 0 { start := time.Now() err := utils.ImportChain(ethereum, ImportChain) @@ -139,6 +136,10 @@ func main() { fmt.Printf("Welcome to the FRONTIER\n") + if StartMining { + ethereum.Miner().Start() + } + if StartJsConsole { InitJsConsole(ethereum) } else if len(InputFile) > 0 { From 65cad14f9b27db396d036f47814d4843d947ac43 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 28 Feb 2015 23:09:49 +0100 Subject: [PATCH 36/36] Report debug hash rate --- miner/miner.go | 7 +------ miner/worker.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/miner/miner.go b/miner/miner.go index 0cc2361c8..6b416be8e 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -52,10 +52,5 @@ func (self *Miner) Stop() { } func (self *Miner) HashRate() int64 { - var tot int64 - for _, agent := range self.worker.agents { - tot += agent.Pow().GetHashrate() - } - - return tot + return self.worker.HashRate() } diff --git a/miner/worker.go b/miner/worker.go index 4f0909302..afce68c35 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -5,6 +5,7 @@ import ( "math/big" "sort" "sync" + "time" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -111,6 +112,8 @@ func (self *worker) register(agent Agent) { func (self *worker) update() { events := self.mux.Subscribe(core.ChainEvent{}, core.NewMinedBlockEvent{}) + timer := time.NewTicker(2 * time.Second) + out: for { select { @@ -129,6 +132,8 @@ out: agent.Stop() } break out + case <-timer.C: + minerlogger.Debugln("Hash rate:", self.HashRate(), "Khash") } } @@ -244,3 +249,12 @@ func (self *worker) commitTransaction(tx *types.Transaction) error { return nil } + +func (self *worker) HashRate() int64 { + var tot int64 + for _, agent := range self.agents { + tot += agent.Pow().GetHashrate() + } + + return tot +}