From 540c79e6adbb5b616e6a1e0612d69dfba07776c9 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 2 Mar 2015 09:36:14 -0600 Subject: [PATCH 01/82] Rename "net" methods --- rpc/api.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 28024c2062..782b5848cc 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -513,8 +513,6 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error switch req.Method { case "eth_coinbase": return p.GetCoinbase(reply) - case "eth_listening": - return p.GetIsListening(reply) case "eth_mining": return p.GetIsMining(reply) case "eth_setMining": @@ -531,8 +529,6 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.SetDefaultBlockAge(int64(args), reply) - case "eth_peerCount": - return p.GetPeerCount(reply) case "eth_number": return p.BlockNumber(reply) case "eth_accounts": @@ -668,6 +664,10 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.DbGet(args, reply) + case "net_listening": + return p.GetIsListening(reply) + case "net_peerCount": + return p.GetPeerCount(reply) case "shh_newIdentity": return p.NewWhisperIdentity(reply) case "shh_newFilter": From fb2488235a9cc187047810817ce3fa34c5087c67 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 2 Mar 2015 09:36:48 -0600 Subject: [PATCH 02/82] Remove defunct calls --- rpc/api.go | 92 ++++++++++++++++++------------------------------------ 1 file changed, 30 insertions(+), 62 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 782b5848cc..28ec049167 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -52,20 +52,20 @@ type EthereumApi struct { db ethutil.Database - defaultBlockAge int64 + // 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, - defaultBlockAge: -1, + 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() @@ -73,36 +73,36 @@ func NewEthereumApi(eth *xeth.XEth) *EthereumApi { return api } -func (self *EthereumApi) setStateByBlockNumber(num int64) { - chain := self.xeth().Backend().ChainManager() - var block *types.Block +// 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 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()) - } -} +// 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{}) + // events := self.mux.Subscribe(core.ChainEvent{}) done: for { select { - case ev := <-events.Chan(): - switch ev.(type) { - case core.ChainEvent: - if self.defaultBlockAge < 0 { - self.setStateByBlockNumber(self.defaultBlockAge) - } - } + // case ev := <-events.Chan(): + // switch ev.(type) { + // case core.ChainEvent: + // if self.defaultBlockAge < 0 { + // self.setStateByBlockNumber(self.defaultBlockAge) + // } + // } case <-timer.C: self.logMut.Lock() self.messagesMut.Lock() @@ -365,24 +365,6 @@ func (p *EthereumApi) GetIsMining(reply *interface{}) error { return nil } -func (p *EthereumApi) SetMining(shouldmine bool, reply *interface{}) error { - *reply = p.xeth().SetMining(shouldmine) - return nil -} - -func (p *EthereumApi) GetDefaultBlockAge(reply *interface{}) error { - *reply = p.defaultBlockAge - return nil -} - -func (p *EthereumApi) SetDefaultBlockAge(defaultBlockAge int64, reply *interface{}) error { - p.defaultBlockAge = defaultBlockAge - p.setStateByBlockNumber(p.defaultBlockAge) - - *reply = true - return nil -} - func (p *EthereumApi) BlockNumber(reply *interface{}) error { *reply = p.xeth().Backend().ChainManager().CurrentBlock().Number() return nil @@ -515,20 +497,6 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.GetCoinbase(reply) case "eth_mining": return p.GetIsMining(reply) - case "eth_setMining": - args, err := req.ToBoolArgs() - if err != nil { - 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(int64(args), reply) case "eth_number": return p.BlockNumber(reply) case "eth_accounts": From e7b33e9ae796cd0ba4c840ee3fead85132ebdccd Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 2 Mar 2015 13:21:57 -0600 Subject: [PATCH 03/82] Move p2p.MakeName to ethutil.MakeVersion --- cmd/ethereum/main.go | 3 +-- cmd/mist/main.go | 4 ++-- ethutil/common.go | 4 ++++ p2p/server.go | 10 +--------- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index f79f948d1e..afb90d72a3 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/state" ) @@ -62,7 +61,7 @@ func main() { utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") ethereum, err := eth.New(ð.Config{ - Name: p2p.MakeName(ClientIdentifier, Version), + Name: ethutil.MakeVersion(ClientIdentifier, Version), KeyStore: KeyStore, DataDir: Datadir, LogFile: LogFile, diff --git a/cmd/mist/main.go b/cmd/mist/main.go index c9a07bfde7..0ece20d2cc 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -28,8 +28,8 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/ui/qt/webengine" "github.com/obscuren/qml" ) @@ -52,7 +52,7 @@ func run() error { config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") ethereum, err := eth.New(ð.Config{ - Name: p2p.MakeName(ClientIdentifier, Version), + Name: ethutil.MakeVersion(ClientIdentifier, Version), KeyStore: KeyStore, DataDir: Datadir, LogFile: LogFile, diff --git a/ethutil/common.go b/ethutil/common.go index 9b66763b81..88f4cd1899 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -13,6 +13,10 @@ import ( "github.com/kardianos/osext" ) +func MakeVersion(name, version string) string { + return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) +} + func DefaultAssetPath() string { var assetPath string pwd, _ := os.Getwd() diff --git a/p2p/server.go b/p2p/server.go index 3ea2538d1c..25da04cd96 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "net" - "runtime" "sync" "time" @@ -24,13 +23,6 @@ const ( var srvlog = logger.NewLogger("P2P Server") var srvjslog = logger.NewJsonLogger() -// MakeName creates a node name that follows the ethereum convention -// for such names. It adds the operation system name and Go runtime version -// the name. -func MakeName(name, version string) string { - return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) -} - // Server manages all peer connections. // // The fields of Server are used as configuration parameters. @@ -45,7 +37,7 @@ type Server struct { MaxPeers int // Name sets the node name of this server. - // Use MakeName to create a name that follows existing conventions. + // Use ethutil.MakeVersion to create a name that follows existing conventions. Name string // Bootstrap nodes are used to establish connectivity From c92e48ce3f9de2196a713b24d5c27d6c664826d9 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 4 Mar 2015 07:54:10 -0600 Subject: [PATCH 04/82] Add client_version RPC message --- eth/backend.go | 5 ++++- rpc/api.go | 2 ++ xeth/xeth.go | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/eth/backend.go b/eth/backend.go index f67f9c78b0..27d3f3b3f9 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -132,7 +132,8 @@ type Ethereum struct { logger ethlogger.LogSystem - Mining bool + Mining bool + version string } func New(config *Config) (*Ethereum, error) { @@ -175,6 +176,7 @@ func New(config *Config) (*Ethereum, error) { blacklist: p2p.NewBlacklist(), eventMux: &event.TypeMux{}, logger: logger, + version: config.Name, } eth.chainManager = core.NewChainManager(db, eth.EventMux()) @@ -231,6 +233,7 @@ func (s *Ethereum) PeerCount() int { return s.net.PeerCoun func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } func (s *Ethereum) Coinbase() []byte { return nil } // TODO +func (s *Ethereum) ClientVersion() string { return s.version } // Start the ethereum func (s *Ethereum) Start() error { diff --git a/rpc/api.go b/rpc/api.go index 28ec049167..f023709caa 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -668,6 +668,8 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.WhisperMessages(args, reply) + case "client_version": + *reply = p.eth.GetClientVersion() default: return NewErrorWithMessage(errNotImplemented, req.Method) } diff --git a/xeth/xeth.go b/xeth/xeth.go index d4c188fec2..8dd905b86b 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -36,6 +36,7 @@ type Backend interface { EventMux() *event.TypeMux Whisper() *whisper.Whisper Miner() *miner.Miner + ClientVersion() string } type XEth struct { @@ -164,6 +165,10 @@ func (self *XEth) IsContract(address string) bool { return len(self.State().SafeGet(address).Code()) > 0 } +func (self *XEth) GetClientVersion() string { + return self.eth.ClientVersion() +} + func (self *XEth) SecretToAddress(key string) string { pair, err := crypto.NewKeyPairFromSec(fromHex(key)) if err != nil { From c3deafabda6e97c0077609ab375be69936d512d4 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 4 Mar 2015 08:22:59 -0600 Subject: [PATCH 05/82] Update NewTXArgs to accept hex --- rpc/api.go | 15 +++++++++------ rpc/args.go | 34 +++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index f023709caa..406960fce3 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -252,12 +252,12 @@ 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.String() + if args.Gas == ethutil.Big0 { + args.Gas = defaultGas } - if len(args.GasPrice) == 0 { - args.GasPrice = defaultGasPrice.String() + if args.GasPrice == ethutil.Big0 { + args.GasPrice = defaultGasPrice } // TODO if no_private_key then @@ -281,7 +281,10 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { 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) + result, err := p.xeth().Transact( /* TODO specify account */ args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) + if err != nil { + return err + } *reply = result //} @@ -289,7 +292,7 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { } 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.String(), args.Gas.String(), args.GasPrice.String(), args.Data) if err != nil { return err } diff --git a/rpc/args.go b/rpc/args.go index e839da8bf9..5686cbdec9 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -1,8 +1,12 @@ package rpc -import "encoding/json" +import ( + "encoding/json" + "math/big" -import "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/ethutil" +) type GetBlockArgs struct { BlockNumber int32 @@ -23,12 +27,12 @@ func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) { } type NewTxArgs struct { - From string `json:"from"` - To string `json:"to"` - Value string `json:"value"` - Gas string `json:"gas"` - GasPrice string `json:"gasPrice"` - Data string `json:"data"` + From string `json:"from"` + To string `json:"to"` + Value *big.Int `json:"value"` + Gas *big.Int `json:"gas"` + GasPrice *big.Int `json:"gasPrice"` + Data string `json:"data"` } func (obj *NewTxArgs) UnmarshalJSON(b []byte) (err error) { @@ -40,18 +44,18 @@ func (obj *NewTxArgs) UnmarshalJSON(b []byte) (err error) { Gas string GasPrice string Data string - Code string + // Code string } if err = json.Unmarshal(b, &ext); err == nil { - if len(ext.Data) == 0 { - ext.Data = ext.Code - } + // if len(ext.Data) == 0 { + // ext.Data = ext.Code + // } obj.From = ext.From obj.To = ext.To - obj.Value = ext.Value - obj.Gas = ext.Gas - obj.GasPrice = ext.GasPrice + obj.Value = ethutil.Big(ext.Value) + obj.Gas = ethutil.Big(ext.Gas) + obj.GasPrice = ethutil.Big(ext.GasPrice) obj.Data = ext.Data return From 93141f4b6d0b986138853af58aacb08be1ca2bfb Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 4 Mar 2015 08:27:34 -0600 Subject: [PATCH 06/82] Return block number as hex --- rpc/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/api.go b/rpc/api.go index 406960fce3..d1158afe99 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -369,7 +369,7 @@ func (p *EthereumApi) GetIsMining(reply *interface{}) error { } func (p *EthereumApi) BlockNumber(reply *interface{}) error { - *reply = p.xeth().Backend().ChainManager().CurrentBlock().Number() + *reply = toHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes()) return nil } From 9b0f00baf35b7eaa323f2679a07da198369b249e Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 4 Mar 2015 08:41:13 -0600 Subject: [PATCH 07/82] Output net_peerCount as hex --- rpc/api.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpc/api.go b/rpc/api.go index d1158afe99..d82432d79a 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -344,7 +344,8 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) err } func (p *EthereumApi) GetPeerCount(reply *interface{}) error { - *reply = p.xeth().PeerCount() + c := p.xeth().PeerCount() + *reply = toHex(big.NewInt(int64(c)).Bytes()) return nil } From 15765517417001a8dffc1180d7621b4284f4443e Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 4 Mar 2015 14:13:31 -0600 Subject: [PATCH 08/82] Output 0x0 instead of 0x --- rpc/util.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rpc/util.go b/rpc/util.go index 3e8ca3fef0..69c7b629f2 100644 --- a/rpc/util.go +++ b/rpc/util.go @@ -63,7 +63,12 @@ func (self JsonWrapper) ParseRequestBody(req *http.Request) (RpcRequest, error) } func toHex(b []byte) string { - return "0x" + ethutil.Bytes2Hex(b) + hex := ethutil.Bytes2Hex(b) + // Prefer output of "0x0" instead of "0x" + if len(hex) == 0 { + hex = "0" + } + return "0x" + hex } func fromHex(s string) []byte { if len(s) > 1 { From 90ceb5218cec788de83842e9b38d63af24af5ffd Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 5 Mar 2015 08:58:46 -0600 Subject: [PATCH 09/82] Update RPC methods Re-order to match spec and explicitly list unimplemented methods --- rpc/api.go | 205 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 118 insertions(+), 87 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index d82432d79a..d0181463d9 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -344,8 +344,7 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) err } func (p *EthereumApi) GetPeerCount(reply *interface{}) error { - c := p.xeth().PeerCount() - *reply = toHex(big.NewInt(int64(c)).Bytes()) + *reply = p.xeth().PeerCount() return nil } @@ -370,7 +369,7 @@ func (p *EthereumApi) GetIsMining(reply *interface{}) error { } func (p *EthereumApi) BlockNumber(reply *interface{}) error { - *reply = toHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes()) + *reply = p.xeth().Backend().ChainManager().CurrentBlock().Number() return nil } @@ -497,51 +496,68 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error // Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC rpclogger.DebugDetailf("%T %s", req.Params, req.Params) switch req.Method { + case "web3_sha3": + args, err := req.ToSha3Args() + if err != nil { + return err + } + return p.Sha3(args, reply) + case "net_listening": + return p.GetIsListening(reply) + case "net_peerCount": + return p.GetPeerCount(reply) case "eth_coinbase": return p.GetCoinbase(reply) case "eth_mining": return p.GetIsMining(reply) - case "eth_number": - return p.BlockNumber(reply) + case "eth_gasPrice": + *reply = toHex(defaultGasPrice.Bytes()) + return nil case "eth_accounts": return p.Accounts(reply) - case "eth_countAt": - args, err := req.ToGetTxCountArgs() - if err != nil { - return err - } - return p.GetTxCountAt(args, reply) - case "eth_codeAt": - args, err := req.ToGetCodeAtArgs() - if err != nil { - return err - } - return p.GetCodeAt(args, reply) - case "eth_balanceAt": + case "eth_blockNumber": + return p.BlockNumber(reply) + case "eth_getBalance": + // TODO handle defaultBlock args, err := req.ToGetBalanceArgs() if err != nil { return err } return p.GetBalanceAt(args, reply) - case "eth_stateAt": + case "eth_getStorage": + // TODO handle defaultBlock args, err := req.ToGetStateArgs() if err != nil { return err } return p.GetStateAt(args, reply) - case "eth_storageAt": + case "eth_getStorageAt": + // TODO handle defaultBlock args, err := req.ToStorageAtArgs() if err != nil { return err } return p.GetStorageAt(args, reply) - case "eth_blockByNumber", "eth_blockByHash": - args, err := req.ToGetBlockArgs() + case "eth_getTransactionCount": + // TODO handle defaultBlock + args, err := req.ToGetTxCountArgs() if err != nil { return err } - return p.GetBlock(args, reply) - case "eth_transact": + return p.GetTxCountAt(args, reply) + case "eth_getBlockTransactionCountByHash": + case "eth_getBlockTransactionCountByNumber": + case "eth_getUncleCountByBlockHash": + case "eth_getUncleCountByBlockNumber": + return errNotImplemented + case "eth_getData": + // TODO handle defaultBlock + args, err := req.ToGetCodeAtArgs() + if err != nil { + return err + } + return p.GetCodeAt(args, reply) + case "eth_sendTransaction": args, err := req.ToNewTxArgs() if err != nil { return err @@ -553,77 +569,91 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.Call(args, reply) + case "eth_flush": + return errNotImplemented + case "eth_getBlockByNumber", "eth_getBlockByHash": + // TODO handle second param for "include transaction objects" + args, err := req.ToGetBlockArgs() + if err != nil { + return err + } + return p.GetBlock(args, reply) + case "eth_getTransactionByHash": + case "eth_getTransactionByBlockHashAndIndex": + case "eth_getTransactionByBlockNumberAndIndex": + case "eth_getUncleByBlockHashAndIndex": + case "eth_getUncleByBlockNumberAndIndex": + return errNotImplemented + case "eth_getCompilers": + return p.GetCompilers(reply) + case "eth_compileSolidity": + case "eth_compileLLL": + return errNotImplemented + case "eth_compileSerpent": + args, err := req.ToCompileArgs() + if err != nil { + return err + } + return p.CompileSerpent(args, reply) case "eth_newFilter": args, err := req.ToFilterArgs() if err != nil { return err } return p.NewFilter(args, reply) - case "eth_newFilterString": - args, err := req.ToFilterStringArgs() - if err != nil { - return err - } - return p.NewFilterString(args, reply) + // case "eth_newFilterString": + // args, err := req.ToFilterStringArgs() + // if err != nil { + // return err + // } + // return p.NewFilterString(args, reply) + case "eth_newBlockFilter": + return errNotImplemented case "eth_uninstallFilter": args, err := req.ToUninstallFilterArgs() if err != nil { return err } return p.UninstallFilter(args, reply) - case "eth_changed": + case "eth_getFilterChanges": args, err := req.ToIdArgs() if err != nil { return err } return p.FilterChanged(args, reply) - case "eth_filterLogs": + case "eth_getFilterLogs": args, err := req.ToIdArgs() if err != nil { return err } return p.Logs(args, reply) - case "eth_logs": + case "eth_getLogs": args, err := req.ToFilterArgs() if err != nil { return err } return p.AllLogs(args, reply) - case "eth_gasPrice": - *reply = toHex(defaultGasPrice.Bytes()) - return nil - case "eth_register": - args, err := req.ToRegisterArgs() - if err != nil { - return err - } - return p.Register(args, reply) - case "eth_unregister": - args, err := req.ToRegisterArgs() - if err != nil { - return err - } - return p.Unregister(args, reply) - case "eth_watchTx": - args, err := req.ToWatchTxArgs() - if err != nil { - return err - } - return p.WatchTx(args, reply) - case "eth_compilers": - return p.GetCompilers(reply) - case "eth_serpent": - args, err := req.ToCompileArgs() - if err != nil { - return err - } - return p.CompileSerpent(args, reply) - case "web3_sha3": - args, err := req.ToSha3Args() - if err != nil { - return err - } - return p.Sha3(args, reply) + case "eth_getWork": + case "eth_submitWork": + return errNotImplemented + // case "eth_register": + // args, err := req.ToRegisterArgs() + // if err != nil { + // return err + // } + // return p.Register(args, reply) + // case "eth_unregister": + // args, err := req.ToRegisterArgs() + // if err != nil { + // return err + // } + // return p.Unregister(args, reply) + // case "eth_watchTx": + // args, err := req.ToWatchTxArgs() + // if err != nil { + // return err + // } + // return p.WatchTx(args, reply) case "db_put": args, err := req.ToDbPutArgs() if err != nil { @@ -636,36 +666,37 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.DbGet(args, reply) - case "net_listening": - return p.GetIsListening(reply) - case "net_peerCount": - return p.GetPeerCount(reply) - case "shh_newIdentity": - return p.NewWhisperIdentity(reply) - case "shh_newFilter": - args, err := req.ToWhisperFilterArgs() - if err != nil { - return err - } - return p.NewWhisperFilter(args, reply) - case "shh_changed": - args, err := req.ToIdArgs() - if err != nil { - return err - } - return p.MessagesChanged(args, reply) case "shh_post": args, err := req.ToWhisperPostArgs() if err != nil { return err } return p.WhisperPost(args, reply) - case "shh_haveIdentity": + case "shh_newIdentity": + return p.NewWhisperIdentity(reply) + case "shh_hasIdentity": args, err := req.ToWhisperHasIdentityArgs() if err != nil { return err } return p.HasWhisperIdentity(args, reply) + case "shh_newGroup": + case "shh_addToGroup": + return errNotImplemented + case "shh_newFilter": + args, err := req.ToWhisperFilterArgs() + if err != nil { + return err + } + return p.NewWhisperFilter(args, reply) + case "shh_uninstallFilter": + return errNotImplemented + case "shh_changed": + args, err := req.ToIdArgs() + if err != nil { + return err + } + return p.MessagesChanged(args, reply) case "shh_getMessages": args, err := req.ToIdArgs() if err != nil { From 0d8d51084f2e6622170f5c2b03c92450cb35b25f Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 5 Mar 2015 11:07:05 -0600 Subject: [PATCH 10/82] Final name changes --- rpc/api.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index d0181463d9..9d00ae3403 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -571,7 +571,8 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.Call(args, reply) case "eth_flush": return errNotImplemented - case "eth_getBlockByNumber", "eth_getBlockByHash": + case "eth_getBlockByNumber": + case "eth_getBlockByHash": // TODO handle second param for "include transaction objects" args, err := req.ToGetBlockArgs() if err != nil { @@ -601,12 +602,12 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.NewFilter(args, reply) - // case "eth_newFilterString": - // args, err := req.ToFilterStringArgs() - // if err != nil { - // return err - // } - // return p.NewFilterString(args, reply) + case "eth_newBlockFilter": + args, err := req.ToFilterStringArgs() + if err != nil { + return err + } + return p.NewFilterString(args, reply) case "eth_newBlockFilter": return errNotImplemented case "eth_uninstallFilter": @@ -691,7 +692,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.NewWhisperFilter(args, reply) case "shh_uninstallFilter": return errNotImplemented - case "shh_changed": + case "shh_getFilterChanges": args, err := req.ToIdArgs() if err != nil { return err From a6599404e49387af8cc5302b7f22005133af4ebf Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 5 Mar 2015 11:14:47 -0600 Subject: [PATCH 11/82] Revert to hex response --- rpc/api.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 9d00ae3403..b97558bdad 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -344,7 +344,8 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) err } func (p *EthereumApi) GetPeerCount(reply *interface{}) error { - *reply = p.xeth().PeerCount() + c := p.xeth().PeerCount() + *reply = toHex(big.NewInt(int64(c)).Bytes()) return nil } @@ -369,7 +370,7 @@ func (p *EthereumApi) GetIsMining(reply *interface{}) error { } func (p *EthereumApi) BlockNumber(reply *interface{}) error { - *reply = p.xeth().Backend().ChainManager().CurrentBlock().Number() + *reply = toHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes()) return nil } @@ -608,8 +609,6 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.NewFilterString(args, reply) - case "eth_newBlockFilter": - return errNotImplemented case "eth_uninstallFilter": args, err := req.ToUninstallFilterArgs() if err != nil { From 9f5e9eb38d7bf00ec24ce4ae09f910236f776641 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 5 Mar 2015 12:26:21 -0600 Subject: [PATCH 12/82] Resolve storage/storageat --- rpc/api.go | 10 +++++----- rpc/args.go | 6 +++--- rpc/messages.go | 24 ++++++++++++++++-------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index b97558bdad..24491833d8 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -311,7 +311,7 @@ func (p *EthereumApi) PushTx(args *PushTxArgs, reply *interface{}) error { return nil } -func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error { +func (p *EthereumApi) GetStorageAt(args *GetStorageAtArgs, reply *interface{}) error { err := args.requirements() if err != nil { return err @@ -333,7 +333,7 @@ func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error { return nil } -func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) error { +func (p *EthereumApi) GetStorage(args *GetStorageArgs, reply *interface{}) error { err := args.requirements() if err != nil { return err @@ -527,14 +527,14 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.GetBalanceAt(args, reply) case "eth_getStorage": // TODO handle defaultBlock - args, err := req.ToGetStateArgs() + args, err := req.ToStorageArgs() if err != nil { return err } - return p.GetStateAt(args, reply) + return p.GetStorage(args, reply) case "eth_getStorageAt": // TODO handle defaultBlock - args, err := req.ToStorageAtArgs() + args, err := req.ToGetStorageAtArgs() if err != nil { return err } diff --git a/rpc/args.go b/rpc/args.go index 5686cbdec9..42d9855b75 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -102,12 +102,12 @@ func (a *GetStorageArgs) requirements() error { return nil } -type GetStateArgs struct { +type GetStorageAtArgs struct { Address string Key string } -func (obj *GetStateArgs) UnmarshalJSON(b []byte) (err error) { +func (obj *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { arg0 := "" if err = json.Unmarshal(b, &arg0); err == nil { obj.Address = arg0 @@ -116,7 +116,7 @@ func (obj *GetStateArgs) UnmarshalJSON(b []byte) (err error) { return errDecodeArgs } -func (a *GetStateArgs) requirements() error { +func (a *GetStorageAtArgs) requirements() error { if a.Address == "" { return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument") } diff --git a/rpc/messages.go b/rpc/messages.go index b37d8229d6..3ab99d2c51 100644 --- a/rpc/messages.go +++ b/rpc/messages.go @@ -120,23 +120,31 @@ func (req *RpcRequest) ToPushTxArgs() (*PushTxArgs, error) { return args, nil } -func (req *RpcRequest) ToGetStateArgs() (*GetStateArgs, error) { - if len(req.Params) < 1 { +func (req *RpcRequest) ToGetStorageAtArgs() (*GetStorageAtArgs, error) { + if len(req.Params) < 2 { return nil, errArguments } - args := new(GetStateArgs) - // TODO need to pass both arguments - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { + args := new(GetStorageAtArgs) + var arg0, arg1 string + + r0 := bytes.NewReader(req.Params[0]) + if err := json.NewDecoder(r0).Decode(arg0); err != nil { return nil, errDecodeArgs } + r1 := bytes.NewReader(req.Params[1]) + if err := json.NewDecoder(r1).Decode(arg1); err != nil { + return nil, errDecodeArgs + } + + args.Address = arg0 + args.Key = arg1 + return args, nil } -func (req *RpcRequest) ToStorageAtArgs() (*GetStorageArgs, error) { +func (req *RpcRequest) ToStorageArgs() (*GetStorageArgs, error) { if len(req.Params) < 1 { return nil, errArguments } From ef9d825cfaa274a4063bf7dcbf19c033e29b90aa Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 5 Mar 2015 21:37:45 -0600 Subject: [PATCH 13/82] Refactor & cleanup --- javascript/types.go | 2 +- rpc/api.go | 282 ++++++++++-------------- rpc/args.go | 524 ++++++++++++++++++++++++++++++++++---------- rpc/messages.go | 400 +++------------------------------ xeth/xeth.go | 12 +- 5 files changed, 553 insertions(+), 667 deletions(-) diff --git a/javascript/types.go b/javascript/types.go index 17f1b739e8..d580d14f5f 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -62,7 +62,7 @@ type JSEthereum struct { func (self *JSEthereum) Block(v interface{}) otto.Value { if number, ok := v.(int64); ok { - return self.toVal(&JSBlock{self.XEth.BlockByNumber(int32(number)), self}) + return self.toVal(&JSBlock{self.XEth.BlockByNumber(uint64(number)), self}) } else if hash, ok := v.(string); ok { return self.toVal(&JSBlock{self.XEth.BlockByHash(hash), self}) } diff --git a/rpc/api.go b/rpc/api.go index 24491833d8..35c9c51d72 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -1,14 +1,7 @@ -/* -For each request type, define the following: - -1. RpcRequest "To" method [message.go], which does basic validation and conversion to "Args" type via json.Decoder() -2. json.Decoder() calls "UnmarshalON" defined on each "Args" struct -3. EthereumApi method, taking the "Args" type and replying with an interface to be marshalled to ON - -*/ package rpc import ( + "encoding/json" "fmt" "math/big" "strings" @@ -150,16 +143,16 @@ func (self *EthereumApi) Unregister(args string, reply *interface{}) error { return nil } -func (self *EthereumApi) WatchTx(args string, reply *interface{}) error { - self.regmut.Lock() - defer self.regmut.Unlock() +// func (self *EthereumApi) WatchTx(args string, reply *interface{}) error { +// self.regmut.Lock() +// defer self.regmut.Unlock() - txs := self.register[args] - self.register[args] = nil +// txs := self.register[args] +// self.register[args] = nil - *reply = txs - return nil -} +// *reply = txs +// return nil +// } func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error { var id int @@ -241,16 +234,6 @@ func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error return nil } -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) - } else { - *reply = p.xeth().BlockByNumber(args.BlockNumber) - } - return nil -} - func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { if args.Gas == ethutil.Big0 { args.Gas = defaultGas @@ -301,22 +284,27 @@ func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error { return nil } -func (p *EthereumApi) PushTx(args *PushTxArgs, reply *interface{}) error { - err := args.requirementsPushTx() - if err != nil { +func (p *EthereumApi) GetBalance(args *GetBalanceArgs, reply *interface{}) error { + if err := args.requirements(); err != nil { return err } - result, _ := p.xeth().PushTx(args.Tx) - *reply = result + state := p.xeth().State().SafeGet(args.Address) + *reply = toHex(state.Balance().Bytes()) + return nil +} + +func (p *EthereumApi) GetStorage(args *GetStorageArgs, reply *interface{}) error { + if err := args.requirements(); err != nil { + return err + } + *reply = p.xeth().State().SafeGet(args.Address).Storage() return nil } func (p *EthereumApi) GetStorageAt(args *GetStorageAtArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { + if err := args.requirements(); err != nil { return err } - state := p.xeth().State().SafeGet(args.Address) value := state.StorageString(args.Key) @@ -333,47 +321,6 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageAtArgs, reply *interface{}) e return nil } -func (p *EthereumApi) GetStorage(args *GetStorageArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { - return err - } - - *reply = p.xeth().State().SafeGet(args.Address).Storage() - return nil -} - -func (p *EthereumApi) GetPeerCount(reply *interface{}) error { - c := p.xeth().PeerCount() - *reply = toHex(big.NewInt(int64(c)).Bytes()) - return nil -} - -func (p *EthereumApi) GetIsListening(reply *interface{}) error { - *reply = p.xeth().IsListening() - return nil -} - -func (p *EthereumApi) GetCoinbase(reply *interface{}) error { - *reply = p.xeth().Coinbase() - return nil -} - -func (p *EthereumApi) Accounts(reply *interface{}) error { - *reply = p.xeth().Accounts() - return nil -} - -func (p *EthereumApi) GetIsMining(reply *interface{}) error { - *reply = p.xeth().IsMining() - return nil -} - -func (p *EthereumApi) BlockNumber(reply *interface{}) error { - *reply = toHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes()) - return nil -} - func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) error { err := args.requirements() if err != nil { @@ -383,19 +330,8 @@ func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) err return nil } -func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { - return err - } - state := p.xeth().State().SafeGet(args.Address) - *reply = toHex(state.Balance().Bytes()) - return nil -} - -func (p *EthereumApi) GetCodeAt(args *GetCodeAtArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { +func (p *EthereumApi) GetData(args *GetDataArgs, reply *interface{}) error { + if err := args.requirements(); err != nil { return err } *reply = p.xeth().CodeAt(args.Address) @@ -408,8 +344,8 @@ func (p *EthereumApi) GetCompilers(reply *interface{}) error { return nil } -func (p *EthereumApi) CompileSerpent(script string, reply *interface{}) error { - res, err := ethutil.Compile(script, false) +func (p *EthereumApi) CompileSerpent(args *CompileArgs, reply *interface{}) error { + res, err := ethutil.Compile(args.Source, false) if err != nil { return err } @@ -417,14 +353,8 @@ func (p *EthereumApi) CompileSerpent(script string, reply *interface{}) error { return nil } -func (p *EthereumApi) Sha3(args *Sha3Args, reply *interface{}) error { - *reply = toHex(crypto.Sha3(fromHex(args.Data))) - return nil -} - func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { + if err := args.requirements(); err != nil { return err } @@ -434,8 +364,7 @@ func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error { } func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { + if err := args.requirements(); err != nil { return err } @@ -449,14 +378,18 @@ func (p *EthereumApi) NewWhisperIdentity(reply *interface{}) error { return nil } -func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) error { +func (p *EthereumApi) NewWhisperFilter(args *WhisperFilterArgs, reply *interface{}) error { var id int - args.Fn = func(msg xeth.WhisperMessage) { + opts := new(xeth.Options) + opts.From = args.From + opts.To = args.To + opts.Topics = args.Topics + opts.Fn = func(msg xeth.WhisperMessage) { p.messagesMut.Lock() 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(opts) p.messages[id] = &whisperFilter{timeout: time.Now()} *reply = id return nil @@ -498,51 +431,50 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error rpclogger.DebugDetailf("%T %s", req.Params, req.Params) switch req.Method { case "web3_sha3": - args, err := req.ToSha3Args() - if err != nil { + args := new(Sha3Args) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.Sha3(args, reply) + *reply = toHex(crypto.Sha3(fromHex(args.Data))) case "net_listening": - return p.GetIsListening(reply) + *reply = p.xeth().IsListening() case "net_peerCount": - return p.GetPeerCount(reply) + *reply = toHex(big.NewInt(int64(p.xeth().PeerCount())).Bytes()) case "eth_coinbase": - return p.GetCoinbase(reply) + *reply = p.xeth().Coinbase() case "eth_mining": - return p.GetIsMining(reply) + *reply = p.xeth().IsMining() case "eth_gasPrice": *reply = toHex(defaultGasPrice.Bytes()) - return nil case "eth_accounts": - return p.Accounts(reply) + *reply = p.xeth().Accounts() case "eth_blockNumber": - return p.BlockNumber(reply) + *reply = toHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes()) case "eth_getBalance": - // TODO handle defaultBlock - args, err := req.ToGetBalanceArgs() - if err != nil { + // TODO handle BlockNumber + args := new(GetBalanceArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.GetBalanceAt(args, reply) + return p.GetBalance(args, reply) case "eth_getStorage": - // TODO handle defaultBlock - args, err := req.ToStorageArgs() - if err != nil { + // TODO handle BlockNumber + args := new(GetStorageArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.GetStorage(args, reply) case "eth_getStorageAt": - // TODO handle defaultBlock - args, err := req.ToGetStorageAtArgs() - if err != nil { + // TODO handle BlockNumber + args := new(GetStorageAtArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.GetStorageAt(args, reply) case "eth_getTransactionCount": - // TODO handle defaultBlock - args, err := req.ToGetTxCountArgs() - if err != nil { + // TODO handle BlockNumber + args := new(GetTxCountArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.GetTxCountAt(args, reply) @@ -552,34 +484,40 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error case "eth_getUncleCountByBlockNumber": return errNotImplemented case "eth_getData": - // TODO handle defaultBlock - args, err := req.ToGetCodeAtArgs() - if err != nil { + // TODO handle BlockNumber + args := new(GetDataArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.GetCodeAt(args, reply) + return p.GetData(args, reply) case "eth_sendTransaction": - args, err := req.ToNewTxArgs() - if err != nil { + args := new(NewTxArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.Transact(args, reply) case "eth_call": - args, err := req.ToNewTxArgs() - if err != nil { + args := new(NewTxArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.Call(args, reply) case "eth_flush": return errNotImplemented - case "eth_getBlockByNumber": case "eth_getBlockByHash": // TODO handle second param for "include transaction objects" - args, err := req.ToGetBlockArgs() - if err != nil { + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.GetBlock(args, reply) + *reply = p.xeth().BlockByHash(args.BlockHash) + case "eth_getBlockByNumber": + // TODO handle second param for "include transaction objects" + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + *reply = p.xeth().BlockByNumber(args.BlockNumber) case "eth_getTransactionByHash": case "eth_getTransactionByBlockHashAndIndex": case "eth_getTransactionByBlockNumberAndIndex": @@ -592,44 +530,44 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error case "eth_compileLLL": return errNotImplemented case "eth_compileSerpent": - args, err := req.ToCompileArgs() - if err != nil { + args := new(CompileArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.CompileSerpent(args, reply) case "eth_newFilter": - args, err := req.ToFilterArgs() - if err != nil { + args := new(FilterOptions) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.NewFilter(args, reply) case "eth_newBlockFilter": - args, err := req.ToFilterStringArgs() - if err != nil { + args := new(FilterStringArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.NewFilterString(args, reply) + return p.NewFilterString(args.Word, reply) case "eth_uninstallFilter": - args, err := req.ToUninstallFilterArgs() - if err != nil { + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.UninstallFilter(args, reply) + return p.UninstallFilter(args.Id, reply) case "eth_getFilterChanges": - args, err := req.ToIdArgs() - if err != nil { + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.FilterChanged(args, reply) + return p.FilterChanged(args.Id, reply) case "eth_getFilterLogs": - args, err := req.ToIdArgs() - if err != nil { + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.Logs(args, reply) + return p.Logs(args.Id, reply) case "eth_getLogs": - args, err := req.ToFilterArgs() - if err != nil { + args := new(FilterOptions) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.AllLogs(args, reply) @@ -655,54 +593,54 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error // } // return p.WatchTx(args, reply) case "db_put": - args, err := req.ToDbPutArgs() - if err != nil { + args := new(DbArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.DbPut(args, reply) case "db_get": - args, err := req.ToDbGetArgs() - if err != nil { + args := new(DbArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.DbGet(args, reply) case "shh_post": - args, err := req.ToWhisperPostArgs() - if err != nil { + args := new(WhisperMessageArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.WhisperPost(args, reply) case "shh_newIdentity": return p.NewWhisperIdentity(reply) case "shh_hasIdentity": - args, err := req.ToWhisperHasIdentityArgs() - if err != nil { + args := new(WhisperIdentityArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.HasWhisperIdentity(args, reply) + return p.HasWhisperIdentity(args.Identity, reply) case "shh_newGroup": case "shh_addToGroup": return errNotImplemented case "shh_newFilter": - args, err := req.ToWhisperFilterArgs() - if err != nil { + args := new(WhisperFilterArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.NewWhisperFilter(args, reply) case "shh_uninstallFilter": return errNotImplemented case "shh_getFilterChanges": - args, err := req.ToIdArgs() - if err != nil { + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.MessagesChanged(args, reply) + return p.MessagesChanged(args.Id, reply) case "shh_getMessages": - args, err := req.ToIdArgs() - if err != nil { + args := new(FilterIdArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { return err } - return p.WhisperMessages(args, reply) + return p.WhisperMessages(args.Id, reply) case "client_version": *reply = p.eth.GetClientVersion() default: diff --git a/rpc/args.go b/rpc/args.go index 42d9855b75..3284a232ac 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -1,6 +1,7 @@ package rpc import ( + "bytes" "encoding/json" "math/big" @@ -8,180 +9,248 @@ import ( "github.com/ethereum/go-ethereum/ethutil" ) -type GetBlockArgs struct { - BlockNumber int32 - Hash string +type GetBlockByHashArgs struct { + BlockHash string + Transactions bool } -func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) { - argint, argstr := int32(0), "" - if err = json.Unmarshal(b, &argint); err == nil { - obj.BlockNumber = argint - return +func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs } - if err = json.Unmarshal(b, &argstr); err == nil { - obj.Hash = argstr - return + + if len(obj) < 1 { + return errArguments } - return errDecodeArgs + args.BlockHash = obj[0].(string) + + if len(obj) > 1 { + args.Transactions = obj[1].(bool) + } + + return nil +} + +type GetBlockByNumberArgs struct { + BlockNumber uint64 + Transactions bool +} + +func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errArguments + } + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[0].(string))) + + if len(obj) > 1 { + args.Transactions = obj[1].(bool) + } + + return nil } type NewTxArgs struct { - From string `json:"from"` - To string `json:"to"` - Value *big.Int `json:"value"` - Gas *big.Int `json:"gas"` - GasPrice *big.Int `json:"gasPrice"` - Data string `json:"data"` + From string + To string + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data string } -func (obj *NewTxArgs) UnmarshalJSON(b []byte) (err error) { - // Data can be either specified as "data" or "code" :-/ - var ext struct { - From string - To string - Value string - Gas string - GasPrice string - Data string - // Code string +func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + From string `json:"from"` + To string `json:"to"` + Value string `json:"value"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Data string `json:"data"` } - if err = json.Unmarshal(b, &ext); err == nil { - // if len(ext.Data) == 0 { - // ext.Data = ext.Code - // } - obj.From = ext.From - obj.To = ext.To - obj.Value = ethutil.Big(ext.Value) - obj.Gas = ethutil.Big(ext.Gas) - obj.GasPrice = ethutil.Big(ext.GasPrice) - obj.Data = ext.Data - - return + if err = json.Unmarshal(b, &obj); err != nil { + return errDecodeArgs } - return errDecodeArgs -} - -type PushTxArgs struct { - Tx string `json:"tx"` -} - -func (obj *PushTxArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Tx = arg0 - return + if len(obj) < 1 { + return errArguments } - return errDecodeArgs -} + args.From = obj[0].From + args.To = obj[0].To + args.Value = ethutil.Big(obj[0].Value) + args.Gas = ethutil.Big(obj[0].Gas) + args.GasPrice = ethutil.Big(obj[0].GasPrice) + args.Data = obj[0].Data -func (a *PushTxArgs) requirementsPushTx() error { - if a.Tx == "" { - return NewErrorWithMessage(errArguments, "PushTx requires a 'tx' as argument") - } return nil } type GetStorageArgs struct { - Address string + Address string + BlockNumber uint64 } -func (obj *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { - if err = json.Unmarshal(b, &obj.Address); err != nil { +func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { return errDecodeArgs } - return + + if len(obj) < 1 { + return errArguments + } + args.Address = obj[0].(string) + + if len(obj) > 1 { + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) + } + + return nil } -func (a *GetStorageArgs) requirements() error { - if len(a.Address) == 0 { - return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument") +func (args *GetStorageArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } return nil } type GetStorageAtArgs struct { - Address string - Key string + Address string + Key string + BlockNumber uint64 } -func (obj *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Address = arg0 - return +func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs } - return errDecodeArgs + + if len(obj) < 2 { + return errArguments + } + args.Address = obj[0].(string) + args.Key = obj[1].(string) + + if len(obj) > 2 { + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[2].(string))) + } + + return nil } -func (a *GetStorageAtArgs) requirements() error { - if a.Address == "" { - return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'address' value as argument") +func (args *GetStorageAtArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } - if a.Key == "" { - return NewErrorWithMessage(errArguments, "GetStorageAt requires an 'key' value as argument") + + if len(args.Key) == 0 { + return NewErrorWithMessage(errArguments, "Key cannot be blank") } return nil } type GetTxCountArgs struct { - Address string `json:"address"` + Address string + BlockNumber uint64 } -func (obj *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Address = arg0 - return +func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs } - return errDecodeArgs + + if len(obj) < 1 { + return errArguments + + } + args.Address = obj[0].(string) + + if len(obj) > 1 { + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) + } + + return nil } -func (a *GetTxCountArgs) requirements() error { - if a.Address == "" { - return NewErrorWithMessage(errArguments, "GetTxCountAt requires an 'address' value as argument") +func (args *GetTxCountArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } return nil } type GetBalanceArgs struct { - Address string + Address string + BlockNumber uint64 } -func (obj *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Address = arg0 - return +func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs } - return errDecodeArgs + + if len(obj) < 1 { + return errArguments + } + args.Address = obj[0].(string) + + if len(obj) > 1 { + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) + } + + return nil } -func (a *GetBalanceArgs) requirements() error { - if a.Address == "" { - return NewErrorWithMessage(errArguments, "GetBalanceAt requires an 'address' value as argument") +func (args *GetBalanceArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } return nil } -type GetCodeAtArgs struct { - Address string +type GetDataArgs struct { + Address string + BlockNumber uint64 } -func (obj *GetCodeAtArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Address = arg0 - return +func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs } - return errDecodeArgs + + if len(obj) < 1 { + return errArguments + } + args.Address = obj[0].(string) + + if len(obj) > 1 { + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) + } + + return nil } -func (a *GetCodeAtArgs) requirements() error { - if a.Address == "" { - return NewErrorWithMessage(errArguments, "GetCodeAt requires an 'address' value as argument") +func (args *GetDataArgs) requirements() error { + if len(args.Address) == 0 { + return NewErrorWithMessage(errArguments, "Address cannot be blank") } return nil } @@ -190,11 +259,55 @@ type Sha3Args struct { Data string } -func (obj *Sha3Args) UnmarshalJSON(b []byte) (err error) { - if err = json.Unmarshal(b, &obj.Data); err != nil { +func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return NewErrorWithMessage(errDecodeArgs, err.Error()) + } + + if len(obj) < 1 { + return errArguments + } + args.Data = obj[0].(string) + + return nil +} + +type FilterArgs struct { + FromBlock uint64 + ToBlock uint64 + Limit uint64 + Offset uint64 + Address string + Topics []string +} + +func (args *FilterArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + FromBlock string `json:"fromBlock"` + ToBlock string `json:"toBlock"` + Limit string `json:"limit"` + Offset string `json:"offset"` + Address string `json:"address"` + Topics []string `json:"topics"` + } + + if err = json.Unmarshal(b, &obj); err != nil { return errDecodeArgs } - return + + if len(obj) < 1 { + return errArguments + } + args.FromBlock = ethutil.BytesToNumber(fromHex(obj[0].FromBlock)) + args.ToBlock = ethutil.BytesToNumber(fromHex(obj[0].ToBlock)) + args.Limit = ethutil.BytesToNumber(fromHex(obj[0].Limit)) + args.Offset = ethutil.BytesToNumber(fromHex(obj[0].Offset)) + args.Address = obj[0].Address + args.Topics = obj[0].Topics + + return nil } type FilterOptions struct { @@ -206,6 +319,33 @@ type FilterOptions struct { Max int } +func (args *FilterOptions) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + FromBlock string `json:"fromBlock"` + ToBlock string `json:"toBlock"` + Limit string `json:"limit"` + Offset string `json:"offset"` + Address string `json:"address"` + Topics []string `json:"topics"` + } + + if err = json.Unmarshal(b, &obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errArguments + } + args.Earliest = int64(ethutil.BytesToNumber(fromHex(obj[0].FromBlock))) + args.Latest = int64(ethutil.BytesToNumber(fromHex(obj[0].ToBlock))) + args.Max = int(ethutil.BytesToNumber(fromHex(obj[0].Limit))) + args.Skip = int(ethutil.BytesToNumber(fromHex(obj[0].Offset))) + args.Address = obj[0].Address + args.Topic = obj[0].Topics + + return nil +} + func toFilterOptions(options *FilterOptions) core.FilterOptions { var opts core.FilterOptions @@ -232,9 +372,9 @@ func toFilterOptions(options *FilterOptions) core.FilterOptions { return opts } -type FilterChangedArgs struct { - n int -} +// type FilterChangedArgs struct { +// n int +// } type DbArgs struct { Database string @@ -242,12 +382,32 @@ type DbArgs struct { Value string } +func (args *DbArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 2 { + return errArguments + } + args.Database = obj[0].(string) + args.Key = obj[1].(string) + + if len(obj) > 2 { + args.Value = obj[2].(string) + } + + return nil +} + func (a *DbArgs) requirements() error { if len(a.Database) == 0 { - return NewErrorWithMessage(errArguments, "DbPutArgs requires an 'Database' value as argument") + return NewErrorWithMessage(errArguments, "Database cannot be blank") } if len(a.Key) == 0 { - return NewErrorWithMessage(errArguments, "DbPutArgs requires an 'Key' value as argument") + return NewErrorWithMessage(errArguments, "Key cannot be blank") } return nil } @@ -260,3 +420,133 @@ type WhisperMessageArgs struct { Priority uint32 Ttl uint32 } + +func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { + var obj []struct { + Payload string + To string + From string + Topic []string + Priority string + Ttl string + } + + if err = json.Unmarshal(b, &obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errArguments + } + args.Payload = obj[0].Payload + args.To = obj[0].To + args.From = obj[0].From + args.Topic = obj[0].Topic + args.Priority = uint32(ethutil.BytesToNumber(fromHex(obj[0].Priority))) + args.Ttl = uint32(ethutil.BytesToNumber(fromHex(obj[0].Ttl))) + + return nil +} + +type CompileArgs struct { + Source string +} + +func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) > 0 { + args.Source = obj[0].(string) + } + + return nil +} + +type FilterStringArgs struct { + Word string +} + +func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errDecodeArgs + } + + args.Word = obj[0] + + return nil +} + +type FilterIdArgs struct { + Id int +} + +func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errDecodeArgs + } + + args.Id = int(ethutil.BytesToNumber(fromHex(obj[0]))) + + return nil +} + +type WhisperIdentityArgs struct { + Identity string +} + +func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { + var obj []string + r := bytes.NewReader(b) + if err := json.NewDecoder(r).Decode(&obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errDecodeArgs + } + + args.Identity = obj[0] + + return nil +} + +type WhisperFilterArgs struct { + To string + From string + Topics []string + Fn func() +} + +func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { + var obj []WhisperFilterArgs + + if err = json.Unmarshal(b, &obj); err != nil { + return errDecodeArgs + } + + if len(obj) < 1 { + return errArguments + } + + args.To = obj[0].To + args.From = obj[0].From + args.Topics = obj[0].Topics + + return nil +} diff --git a/rpc/messages.go b/rpc/messages.go index 3ab99d2c51..4f5b009905 100644 --- a/rpc/messages.go +++ b/rpc/messages.go @@ -17,7 +17,6 @@ package rpc import ( - "bytes" "encoding/json" "errors" "fmt" @@ -33,10 +32,10 @@ var ( ) type RpcRequest struct { - ID interface{} `json:"id"` - JsonRpc string `json:"jsonrpc"` - Method string `json:"method"` - Params []json.RawMessage `json:"params"` + ID interface{} `json:"id"` + JsonRpc string `json:"jsonrpc"` + Method string `json:"method"` + Params json.RawMessage `json:"params"` } type RpcSuccessResponse struct { @@ -61,367 +60,30 @@ 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, errArguments - } - - args := new(Sha3Args) - r := bytes.NewReader(req.Params[0]) - if err := json.NewDecoder(r).Decode(args); err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetBlockArgs() (*GetBlockArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetBlockArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToNewTxArgs() (*NewTxArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(NewTxArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - return args, nil -} - -func (req *RpcRequest) ToPushTxArgs() (*PushTxArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(PushTxArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetStorageAtArgs() (*GetStorageAtArgs, error) { - if len(req.Params) < 2 { - return nil, errArguments - } - - args := new(GetStorageAtArgs) - var arg0, arg1 string - - r0 := bytes.NewReader(req.Params[0]) - if err := json.NewDecoder(r0).Decode(arg0); err != nil { - return nil, errDecodeArgs - } - - r1 := bytes.NewReader(req.Params[1]) - if err := json.NewDecoder(r1).Decode(arg1); err != nil { - return nil, errDecodeArgs - } - - args.Address = arg0 - args.Key = arg1 - - return args, nil -} - -func (req *RpcRequest) ToStorageArgs() (*GetStorageArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetStorageArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetTxCountArgs() (*GetTxCountArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetTxCountArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetBalanceArgs() (*GetBalanceArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetBalanceArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToGetCodeAtArgs() (*GetCodeAtArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(GetCodeAtArgs) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToBoolArgs() (bool, error) { - if len(req.Params) < 1 { - return false, errArguments - } - - var args bool - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return false, errDecodeArgs - } - - 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 - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToFilterArgs() (*FilterOptions, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - args := new(FilterOptions) - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(args) - if err != nil { - return nil, errDecodeArgs - } - return args, nil -} - -func (req *RpcRequest) ToFilterStringArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToUninstallFilterArgs() (int, error) { - if len(req.Params) < 1 { - return 0, errArguments - } - - var args int - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return 0, errDecodeArgs - } - - return args, nil -} - -func (req *RpcRequest) ToFilterChangedArgs() (int, error) { - if len(req.Params) < 1 { - return 0, errArguments - } - - var id int - r := bytes.NewReader(req.Params[0]) - err := json.NewDecoder(r).Decode(&id) - if err != nil { - return 0, errDecodeArgs - } - return id, nil -} - -func (req *RpcRequest) ToDbPutArgs() (*DbArgs, error) { - if len(req.Params) < 3 { - return nil, errArguments - } - - var args DbArgs - err := json.Unmarshal(req.Params[0], &args.Database) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - err = json.Unmarshal(req.Params[1], &args.Key) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - err = json.Unmarshal(req.Params[2], &args.Value) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - return &args, nil -} - -func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) { - if len(req.Params) < 2 { - return nil, errArguments - } - - var args DbArgs - err := json.Unmarshal(req.Params[0], &args.Database) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - err = json.Unmarshal(req.Params[1], &args.Key) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - return &args, nil -} - -func (req *RpcRequest) ToWhisperFilterArgs() (*xeth.Options, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - var args xeth.Options - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } - - return &args, nil -} - -func (req *RpcRequest) ToIdArgs() (int, error) { - if len(req.Params) < 1 { - return 0, errArguments - } - - var id int - err := json.Unmarshal(req.Params[0], &id) - if err != nil { - return 0, errDecodeArgs - } - - return id, nil -} - -func (req *RpcRequest) ToWhisperPostArgs() (*WhisperMessageArgs, error) { - if len(req.Params) < 1 { - return nil, errArguments - } - - var args WhisperMessageArgs - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return nil, err - } - - return &args, nil -} - -func (req *RpcRequest) ToWhisperHasIdentityArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", err - } - - return args, nil -} - -func (req *RpcRequest) ToRegisterArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", err - } - - return args, nil -} - -func (req *RpcRequest) ToWatchTxArgs() (string, error) { - if len(req.Params) < 1 { - return "", errArguments - } - - var args string - err := json.Unmarshal(req.Params[0], &args) - if err != nil { - return "", err - } - - return args, nil -} +// func (req *RpcRequest) ToRegisterArgs() (string, error) { +// if len(req.Params) < 1 { +// return "", errArguments +// } + +// var args string +// err := json.Unmarshal(req.Params, &args) +// if err != nil { +// return "", err +// } + +// return args, nil +// } + +// func (req *RpcRequest) ToWatchTxArgs() (string, error) { +// if len(req.Params) < 1 { +// return "", errArguments +// } + +// var args string +// err := json.Unmarshal(req.Params, &args) +// if err != nil { +// return "", err +// } + +// return args, nil +// } diff --git a/xeth/xeth.go b/xeth/xeth.go index 8dd905b86b..451d3ceea7 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -86,21 +86,17 @@ func (self *XEth) BlockByHash(strHash string) *Block { return NewBlock(block) } -func (self *XEth) BlockByNumber(num int32) *Block { - if num == -1 { - return NewBlock(self.chainManager.CurrentBlock()) - } - - return NewBlock(self.chainManager.GetBlockByNumber(uint64(num))) +func (self *XEth) BlockByNumber(num uint64) *Block { + return NewBlock(self.chainManager.GetBlockByNumber(num)) } func (self *XEth) Block(v interface{}) *Block { if n, ok := v.(int32); ok { - return self.BlockByNumber(n) + return self.BlockByNumber(uint64(n)) } else if str, ok := v.(string); ok { return self.BlockByHash(str) } else if f, ok := v.(float64); ok { // Don't ask ... - return self.BlockByNumber(int32(f)) + return self.BlockByNumber(uint64(f)) } return nil From 03dc760ca07682a61ed547c11a1c650a324a1b14 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 5 Mar 2015 21:48:03 -0600 Subject: [PATCH 14/82] Tidy --- rpc/api.go | 90 +++++++++++++++++++++++++++++++------------------ rpc/args.go | 28 --------------- rpc/messages.go | 2 -- 3 files changed, 58 insertions(+), 62 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 35c9c51d72..ded91def16 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -124,24 +124,24 @@ func (self *EthereumApi) stop() { close(self.quit) } -func (self *EthereumApi) Register(args string, reply *interface{}) error { - self.regmut.Lock() - defer self.regmut.Unlock() +// func (self *EthereumApi) Register(args string, reply *interface{}) error { +// self.regmut.Lock() +// defer self.regmut.Unlock() - if _, ok := self.register[args]; ok { - self.register[args] = nil // register with empty - } - return nil -} +// if _, ok := self.register[args]; ok { +// self.register[args] = nil // register with empty +// } +// return nil +// } -func (self *EthereumApi) Unregister(args string, reply *interface{}) error { - self.regmut.Lock() - defer self.regmut.Unlock() +// func (self *EthereumApi) Unregister(args string, reply *interface{}) error { +// self.regmut.Lock() +// defer self.regmut.Unlock() - delete(self.register, args) +// delete(self.register, args) - return nil -} +// return nil +// } // func (self *EthereumApi) WatchTx(args string, reply *interface{}) error { // self.regmut.Lock() @@ -574,24 +574,6 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error case "eth_getWork": case "eth_submitWork": return errNotImplemented - // case "eth_register": - // args, err := req.ToRegisterArgs() - // if err != nil { - // return err - // } - // return p.Register(args, reply) - // case "eth_unregister": - // args, err := req.ToRegisterArgs() - // if err != nil { - // return err - // } - // return p.Unregister(args, reply) - // case "eth_watchTx": - // args, err := req.ToWatchTxArgs() - // if err != nil { - // return err - // } - // return p.WatchTx(args, reply) case "db_put": args := new(DbArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -643,6 +625,24 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.WhisperMessages(args.Id, reply) case "client_version": *reply = p.eth.GetClientVersion() + // case "eth_register": + // args, err := req.ToRegisterArgs() + // if err != nil { + // return err + // } + // return p.Register(args, reply) + // case "eth_unregister": + // args, err := req.ToRegisterArgs() + // if err != nil { + // return err + // } + // return p.Unregister(args, reply) + // case "eth_watchTx": + // args, err := req.ToWatchTxArgs() + // if err != nil { + // return err + // } + // return p.WatchTx(args, reply) default: return NewErrorWithMessage(errNotImplemented, req.Method) } @@ -675,3 +675,29 @@ func t(f ui.Frontend) { t, _ := ret.Get(0) fmt.Println("return:", t) } + +func toFilterOptions(options *FilterOptions) core.FilterOptions { + var opts core.FilterOptions + + // Convert optional address slice/string to byte slice + if str, ok := options.Address.(string); ok { + opts.Address = [][]byte{fromHex(str)} + } else if slice, ok := options.Address.([]interface{}); ok { + bslice := make([][]byte, len(slice)) + for i, addr := range slice { + if saddr, ok := addr.(string); ok { + bslice[i] = fromHex(saddr) + } + } + opts.Address = bslice + } + + opts.Earliest = options.Earliest + opts.Latest = options.Latest + opts.Topics = make([][]byte, len(options.Topic)) + for i, topic := range options.Topic { + opts.Topics[i] = fromHex(topic) + } + + return opts +} diff --git a/rpc/args.go b/rpc/args.go index 3284a232ac..f560512422 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -5,7 +5,6 @@ import ( "encoding/json" "math/big" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/ethutil" ) @@ -346,32 +345,6 @@ func (args *FilterOptions) UnmarshalJSON(b []byte) (err error) { return nil } -func toFilterOptions(options *FilterOptions) core.FilterOptions { - var opts core.FilterOptions - - // Convert optional address slice/string to byte slice - if str, ok := options.Address.(string); ok { - opts.Address = [][]byte{fromHex(str)} - } else if slice, ok := options.Address.([]interface{}); ok { - bslice := make([][]byte, len(slice)) - for i, addr := range slice { - if saddr, ok := addr.(string); ok { - bslice[i] = fromHex(saddr) - } - } - opts.Address = bslice - } - - opts.Earliest = options.Earliest - opts.Latest = options.Latest - opts.Topics = make([][]byte, len(options.Topic)) - for i, topic := range options.Topic { - opts.Topics[i] = fromHex(topic) - } - - return opts -} - // type FilterChangedArgs struct { // n int // } @@ -530,7 +503,6 @@ type WhisperFilterArgs struct { To string From string Topics []string - Fn func() } func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { diff --git a/rpc/messages.go b/rpc/messages.go index 4f5b009905..a3ebbf330c 100644 --- a/rpc/messages.go +++ b/rpc/messages.go @@ -20,8 +20,6 @@ import ( "encoding/json" "errors" "fmt" - - "github.com/ethereum/go-ethereum/xeth" ) var ( From 18b076861e6a70109ab152c29fc1f11530c273e2 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Thu, 5 Mar 2015 22:41:55 -0600 Subject: [PATCH 15/82] Add web3_sha3 test --- rpc/api_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rpc/api_test.go b/rpc/api_test.go index a9fc16cd37..ec03822c5b 100644 --- a/rpc/api_test.go +++ b/rpc/api_test.go @@ -1,11 +1,29 @@ package rpc import ( + "encoding/json" "sync" "testing" "time" ) +func TestWeb3Sha3(t *testing.T) { + jsonstr := `{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}` + expected := "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" + + api := &EthereumApi{} + + var req RpcRequest + json.Unmarshal([]byte(jsonstr), &req) + + var response interface{} + _ = api.GetRequestReply(&req, &response) + + if response.(string) != expected { + t.Errorf("Expected %s got %s", expected, response) + } +} + func TestFilterClose(t *testing.T) { t.Skip() api := &EthereumApi{ From 5da3d911cf80e032ceb79a17609e41973923fc1a Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Fri, 6 Mar 2015 00:03:22 -0600 Subject: [PATCH 16/82] Initial args tests --- rpc/args.go | 26 +++++----- rpc/args_test.go | 125 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 rpc/args_test.go diff --git a/rpc/args.go b/rpc/args.go index f560512422..ba2ef731aa 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -47,7 +47,7 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { if len(obj) < 1 { return errArguments } - args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[0].(string))) + args.BlockNumber = uint64(ethutil.Big(obj[0].(string)).Int64()) if len(obj) > 1 { args.Transactions = obj[1].(bool) @@ -110,7 +110,7 @@ func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) + args.BlockNumber = uint64(ethutil.Big(obj[1].(string)).Int64()) } return nil @@ -179,7 +179,7 @@ func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) + args.BlockNumber = uint64(ethutil.Big(obj[1].(string)).Int64()) } return nil @@ -210,7 +210,7 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) + args.BlockNumber = uint64(ethutil.Big(obj[1].(string)).Int64()) } return nil @@ -241,7 +241,7 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) + args.BlockNumber = uint64(ethutil.Big(obj[1].(string)).Int64()) } return nil @@ -299,10 +299,10 @@ func (args *FilterArgs) UnmarshalJSON(b []byte) (err error) { if len(obj) < 1 { return errArguments } - args.FromBlock = ethutil.BytesToNumber(fromHex(obj[0].FromBlock)) - args.ToBlock = ethutil.BytesToNumber(fromHex(obj[0].ToBlock)) - args.Limit = ethutil.BytesToNumber(fromHex(obj[0].Limit)) - args.Offset = ethutil.BytesToNumber(fromHex(obj[0].Offset)) + args.FromBlock = uint64(ethutil.Big(obj[0].FromBlock).Int64()) + args.ToBlock = uint64(ethutil.Big(obj[0].ToBlock).Int64()) + args.Limit = uint64(ethutil.Big(obj[0].Limit).Int64()) + args.Offset = uint64(ethutil.Big(obj[0].Offset).Int64()) args.Address = obj[0].Address args.Topics = obj[0].Topics @@ -335,10 +335,10 @@ func (args *FilterOptions) UnmarshalJSON(b []byte) (err error) { if len(obj) < 1 { return errArguments } - args.Earliest = int64(ethutil.BytesToNumber(fromHex(obj[0].FromBlock))) - args.Latest = int64(ethutil.BytesToNumber(fromHex(obj[0].ToBlock))) - args.Max = int(ethutil.BytesToNumber(fromHex(obj[0].Limit))) - args.Skip = int(ethutil.BytesToNumber(fromHex(obj[0].Offset))) + args.Earliest = int64(ethutil.Big(obj[0].FromBlock).Int64()) + args.Latest = int64(ethutil.Big(obj[0].ToBlock).Int64()) + args.Max = int(ethutil.Big(obj[0].Limit).Int64()) + args.Skip = int(ethutil.Big(obj[0].Offset).Int64()) args.Address = obj[0].Address args.Topic = obj[0].Topics diff --git a/rpc/args_test.go b/rpc/args_test.go new file mode 100644 index 0000000000..380ed176b3 --- /dev/null +++ b/rpc/args_test.go @@ -0,0 +1,125 @@ +package rpc + +import ( + "bytes" + "encoding/json" + "math/big" + "testing" +) + +func TestSha3(t *testing.T) { + input := `["0x68656c6c6f20776f726c64"]` + expected := "0x68656c6c6f20776f726c64" + + args := new(Sha3Args) + json.Unmarshal([]byte(input), &args) + + if args.Data != expected { + t.Error("got %s expected %s", input, expected) + } +} + +func TestGetBalanceArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x1f"]` + expected := new(GetBalanceArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = 31 + + args := new(GetBalanceArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.Address != expected.Address { + t.Errorf("Address should be %v but is %v", expected.Address, args.Address) + } + + if args.BlockNumber != expected.BlockNumber { + t.Errorf("BlockNumber should be %v but is %v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetBlockByHashArgs(t *testing.T) { + input := `["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true]` + expected := new(GetBlockByHashArgs) + expected.BlockHash = "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" + expected.Transactions = true + + args := new(GetBlockByHashArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.BlockHash != expected.BlockHash { + t.Errorf("BlockHash should be %v but is %v", expected.BlockHash, args.BlockHash) + } + + if args.Transactions != expected.Transactions { + t.Errorf("Transactions should be %v but is %v", expected.Transactions, args.Transactions) + } +} + +func TestGetBlockByNumberArgs(t *testing.T) { + input := `["0x1b4", false]` + expected := new(GetBlockByNumberArgs) + expected.BlockNumber = 436 + expected.Transactions = false + + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if args.BlockNumber != expected.BlockNumber { + t.Errorf("BlockHash should be %v but is %v", expected.BlockNumber, args.BlockNumber) + } + + if args.Transactions != expected.Transactions { + t.Errorf("Transactions should be %v but is %v", expected.Transactions, args.Transactions) + } +} + +func TestNewTxArgs(t *testing.T) { + input := `[{"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f072445675", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a000", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}]` + expected := new(NewTxArgs) + expected.From = "0xb60e8dd61c5d32be8058bb8eb970870f07233155" + expected.To = "0xd46e8dd67c5d32be8058bb8eb970870f072445675" + expected.Gas = big.NewInt(30400) + expected.GasPrice = big.NewInt(10000000000000) + expected.Value = big.NewInt(10000000000000) + expected.Data = "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + + args := new(NewTxArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + if bytes.Compare(expected.Gas.Bytes(), args.Gas.Bytes()) != 0 { + t.Errorf("Gas shoud be %#v but is %#v", expected.Gas.Bytes(), args.Gas.Bytes()) + } + + if bytes.Compare(expected.GasPrice.Bytes(), args.GasPrice.Bytes()) != 0 { + t.Errorf("GasPrice shoud be %#v but is %#v", expected.GasPrice, args.GasPrice) + } + + if bytes.Compare(expected.Value.Bytes(), args.Value.Bytes()) != 0 { + t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) + } + + if expected.Data != args.Data { + t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data) + } +} From bc45e5c6de3052a4c853387dea0af5cd9207f1f7 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Thu, 26 Feb 2015 13:22:09 +0100 Subject: [PATCH 17/82] Integrate eth_accounts and eth_transact to use new account manager * Add from to eth_transact / xeth.Transact and add static pass in lieu of integrating with native Mist window for user passphrase entry * Make eth_accounts return AccountManager.Accounts() * Add a Generate Key menu item in Mist --- cmd/mist/assets/qml/main.qml | 5 +++++ cmd/mist/bindings.go | 4 ++-- cmd/mist/gui.go | 7 ++++++ cmd/mist/ui_lib.go | 1 + core/types/transaction.go | 8 +++++++ eth/backend.go | 41 +++++++++++++++++++++--------------- javascript/types.go | 4 ++-- rpc/api.go | 30 +++++--------------------- rpc/args.go | 1 + xeth/xeth.go | 40 ++++++++++++++++++++++++----------- 10 files changed, 83 insertions(+), 58 deletions(-) diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index f9ee6939d6..7f72d35f42 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -190,6 +190,11 @@ ApplicationWindow { } } + MenuItem { + text: "Generate key" + shortcut: "Ctrl+k" + onTriggered: gui.generateKey() + } } Menu { diff --git a/cmd/mist/bindings.go b/cmd/mist/bindings.go index f21aa31353..fd89eb7e2e 100644 --- a/cmd/mist/bindings.go +++ b/cmd/mist/bindings.go @@ -49,7 +49,7 @@ func (gui *Gui) LogPrint(level logger.LogLevel, msg string) { } */ } -func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (string, error) { +func (gui *Gui) Transact(from, recipient, value, gas, gasPrice, d string) (string, error) { var data string if len(recipient) == 0 { code, err := ethutil.Compile(d, false) @@ -61,7 +61,7 @@ func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (string, err data = ethutil.Bytes2Hex(utils.FormatTransactionData(d)) } - return gui.xeth.Transact(recipient, value, gas, gasPrice, data) + return gui.xeth.Transact(from, recipient, value, gas, gasPrice, data) } // functions that allow Gui to implement interface guilogger.LogSystem diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go index 869b689dd6..bc6e9ed538 100644 --- a/cmd/mist/gui.go +++ b/cmd/mist/gui.go @@ -175,6 +175,13 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { func (gui *Gui) ImportKey(filePath string) { } +func (gui *Gui) GenerateKey() { + _, err := gui.eth.AccountManager().NewAccount("hurr") + if err != nil { + // TODO: UI feedback? + } +} + func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) { context.SetVar("lib", gui) component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml")) diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go index 098e8fca5c..af78f0c107 100644 --- a/cmd/mist/ui_lib.go +++ b/cmd/mist/ui_lib.go @@ -171,6 +171,7 @@ func (self *UiLib) Transact(params map[string]interface{}) (string, error) { object := mapToTxParams(params) return self.XEth.Transact( + object["from"], object["to"], object["value"], object["gas"], diff --git a/core/types/transaction.go b/core/types/transaction.go index 7a1d6104e4..7d34c86f47 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -129,6 +129,7 @@ func (tx *Transaction) sender() []byte { return crypto.Sha3(pubkey[1:])[12:] } +// TODO: deprecate after new accounts & key stores are integrated func (tx *Transaction) Sign(privk []byte) error { sig := tx.Signature(privk) @@ -140,6 +141,13 @@ func (tx *Transaction) Sign(privk []byte) error { return nil } +func (tx *Transaction) SetSignatureValues(sig []byte) error { + tx.R = sig[:32] + tx.S = sig[32:64] + tx.V = uint64(sig[64] + 27) + return nil +} + func (tx *Transaction) SignECDSA(key *ecdsa.PrivateKey) error { return tx.Sign(crypto.FromECDSA(key)) } diff --git a/eth/backend.go b/eth/backend.go index 1c711a775a..02e7e2746d 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/blockpool" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" @@ -117,6 +118,7 @@ type Ethereum struct { txPool *core.TxPool chainManager *core.ChainManager blockPool *blockpool.BlockPool + accountManager *accounts.AccountManager whisper *whisper.Whisper net *p2p.Server @@ -176,9 +178,13 @@ func New(config *Config) (*Ethereum, error) { DataDir: config.DataDir, } + // TODO: add config flag and case on plain/protected key store + ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir()) + am := accounts.NewAccountManager(ks, 300000) // keys unlocked for 300s + eth.accountManager = &am + eth.chainManager = core.NewChainManager(db, eth.EventMux()) pow := ethash.New(eth.chainManager) - eth.txPool = core.NewTxPool(eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(db, pow, eth.txPool, eth.chainManager, eth.EventMux()) eth.chainManager.SetProcessor(eth.blockProcessor) @@ -215,22 +221,23 @@ func New(config *Config) (*Ethereum, error) { return eth, nil } -func (s *Ethereum) KeyManager() *crypto.KeyManager { return s.keyManager } -func (s *Ethereum) Logger() logger.LogSystem { return s.logger } -func (s *Ethereum) Name() string { return s.net.Name } -func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager } -func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } -func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } -func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool } -func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } -func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } -func (s *Ethereum) Db() ethutil.Database { return s.db } -func (s *Ethereum) Miner() *miner.Miner { return s.miner } -func (s *Ethereum) IsListening() bool { return true } // Always listening -func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } -func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } -func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } -func (s *Ethereum) Coinbase() []byte { return nil } // TODO +func (s *Ethereum) KeyManager() *crypto.KeyManager { return s.keyManager } +func (s *Ethereum) Logger() logger.LogSystem { return s.logger } +func (s *Ethereum) Name() string { return s.net.Name } +func (s *Ethereum) AccountManager() *accounts.AccountManager { return s.accountManager } +func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager } +func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } +func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } +func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool } +func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } +func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } +func (s *Ethereum) Db() ethutil.Database { return s.db } +func (s *Ethereum) Miner() *miner.Miner { return s.miner } +func (s *Ethereum) IsListening() bool { return true } // Always listening +func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } +func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } +func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } +func (s *Ethereum) Coinbase() []byte { return nil } // TODO // Start the ethereum func (s *Ethereum) Start() error { diff --git a/javascript/types.go b/javascript/types.go index 77e209d194..e07267c8f6 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -70,8 +70,8 @@ func (self *JSEthereum) GetStateObject(addr string) otto.Value { return self.toVal(&JSStateObject{self.XEth.State().SafeGet(addr), self}) } -func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { - r, err := self.XEth.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr) +func (self *JSEthereum) Transact(fromStr, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { + r, err := self.XEth.Transact(fromStr, recipient, valueStr, gasStr, gasPriceStr, dataStr) if err != nil { fmt.Println(err) diff --git a/rpc/api.go b/rpc/api.go index 28024c2062..b622945eb5 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -252,38 +252,18 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { } func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { - if len(args.Gas) == 0 { + // TODO: align default values to have the same type, e.g. not depend on + // ethutil.Value conversions later on + if ethutil.Big(args.Gas).Cmp(big.NewInt(0)) == 0 { args.Gas = defaultGas.String() } - if len(args.GasPrice) == 0 { + if ethutil.Big(args.GasPrice).Cmp(big.NewInt(0)) == 0 { args.GasPrice = defaultGasPrice.String() } - // TODO if no_private_key then - //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) + result, _ := p.xeth().Transact(args.From, args.To, args.Value, args.Gas, args.GasPrice, args.Data) *reply = result - //} return nil } diff --git a/rpc/args.go b/rpc/args.go index ea8489585e..ec3359a4a4 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -24,6 +24,7 @@ func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) { type NewTxArgs struct { From string `json:"from"` + Pass string `json:"pass"` To string `json:"to"` Value string `json:"value"` Gas string `json:"gas"` diff --git a/xeth/xeth.go b/xeth/xeth.go index 677d40fd5a..91bd35f8e7 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -7,8 +7,8 @@ package xeth import ( "bytes" "encoding/json" - "fmt" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -27,6 +27,7 @@ var pipelogger = logger.NewLogger("XETH") type Backend interface { BlockProcessor() *core.BlockProcessor ChainManager() *core.ChainManager + AccountManager() *accounts.AccountManager TxPool() *core.TxPool PeerCount() int IsListening() bool @@ -42,6 +43,7 @@ type XEth struct { eth Backend blockProcessor *core.BlockProcessor chainManager *core.ChainManager + accountManager *accounts.AccountManager state *State whisper *Whisper miner *miner.Miner @@ -52,6 +54,7 @@ func New(eth Backend) *XEth { eth: eth, blockProcessor: eth.BlockProcessor(), chainManager: eth.ChainManager(), + accountManager: eth.AccountManager(), whisper: NewWhisper(eth.Whisper()), miner: eth.Miner(), } @@ -106,7 +109,13 @@ func (self *XEth) Block(v interface{}) *Block { } func (self *XEth) Accounts() []string { - return []string{toHex(self.eth.KeyManager().Address())} + // TODO: check err? + accounts, _ := self.eth.AccountManager().Accounts() + accountAddresses := make([]string, len(accounts)) + for i, ac := range accounts { + accountAddresses[i] = toHex(ac.Address) + } + return accountAddresses } func (self *XEth) PeerCount() int { @@ -266,17 +275,19 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st return toHex(res), nil } -func (self *XEth) Transact(toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { +func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { + var ( + from []byte to []byte value = ethutil.NewValue(valueStr) gas = ethutil.NewValue(gasStr) price = ethutil.NewValue(gasPriceStr) data []byte - key = self.eth.KeyManager().KeyPair() contractCreation bool ) + from = fromHex(fromStr) data = fromHex(codeStr) to = fromHex(toStr) if len(to) == 0 { @@ -290,21 +301,26 @@ func (self *XEth) Transact(toStr, valueStr, gasStr, gasPriceStr, codeStr string) tx = types.NewTransactionMessage(to, value.BigInt(), gas.BigInt(), price.BigInt(), data) } - var err error - state := self.eth.ChainManager().TxState() - if balance := state.GetBalance(key.Address()); balance.Cmp(tx.Value()) < 0 { - return "", fmt.Errorf("insufficient balance. balance=%v tx=%v", balance, tx.Value()) - } - nonce := state.GetNonce(key.Address()) + state := self.chainManager.TransState() + nonce := state.GetNonce(from) tx.SetNonce(nonce) - tx.Sign(key.PrivateKey) + sig, err := self.accountManager.Sign(&accounts.Account{Address: from}, tx.Hash()) + if err != nil { + return "", err + } + tx.SetSignatureValues(sig) err = self.eth.TxPool().Add(tx) if err != nil { return "", err } - state.SetNonce(key.Address(), nonce+1) + state.SetNonce(from, nonce+1) + + if contractCreation { + addr := core.AddressFromMessage(tx) + pipelogger.Infof("Contract addr %x\n", addr) + } if types.IsContractAddr(to) { return toHex(core.AddressFromMessage(tx)), nil From dc7022cd613113b90b491cffe3eab30c102e6345 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Fri, 6 Mar 2015 09:54:08 -0600 Subject: [PATCH 18/82] Increased args test coverage --- javascript/types.go | 2 +- rpc/args.go | 120 ++++++++++------- rpc/args_test.go | 309 ++++++++++++++++++++++++++++++++++++++++++++ xeth/xeth.go | 12 +- 4 files changed, 390 insertions(+), 53 deletions(-) diff --git a/javascript/types.go b/javascript/types.go index d580d14f5f..001b50a70e 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -62,7 +62,7 @@ type JSEthereum struct { func (self *JSEthereum) Block(v interface{}) otto.Value { if number, ok := v.(int64); ok { - return self.toVal(&JSBlock{self.XEth.BlockByNumber(uint64(number)), self}) + return self.toVal(&JSBlock{self.XEth.BlockByNumber(number), self}) } else if hash, ok := v.(string); ok { return self.toVal(&JSBlock{self.XEth.BlockByHash(hash), self}) } diff --git a/rpc/args.go b/rpc/args.go index ba2ef731aa..43cad5fde4 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -33,7 +33,7 @@ func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { } type GetBlockByNumberArgs struct { - BlockNumber uint64 + BlockNumber int64 Transactions bool } @@ -47,7 +47,7 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { if len(obj) < 1 { return errArguments } - args.BlockNumber = uint64(ethutil.Big(obj[0].(string)).Int64()) + args.BlockNumber = ethutil.Big(obj[0].(string)).Int64() if len(obj) > 1 { args.Transactions = obj[1].(bool) @@ -94,7 +94,7 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { type GetStorageArgs struct { Address string - BlockNumber uint64 + BlockNumber int64 } func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { @@ -110,7 +110,11 @@ func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - args.BlockNumber = uint64(ethutil.Big(obj[1].(string)).Int64()) + if obj[1].(string) == "latest" { + args.BlockNumber = -1 + } else { + args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() + } } return nil @@ -126,7 +130,7 @@ func (args *GetStorageArgs) requirements() error { type GetStorageAtArgs struct { Address string Key string - BlockNumber uint64 + BlockNumber int64 } func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { @@ -143,7 +147,11 @@ func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { args.Key = obj[1].(string) if len(obj) > 2 { - args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[2].(string))) + if obj[2].(string) == "latest" { + args.BlockNumber = -1 + } else { + args.BlockNumber = ethutil.Big(obj[2].(string)).Int64() + } } return nil @@ -162,7 +170,7 @@ func (args *GetStorageAtArgs) requirements() error { type GetTxCountArgs struct { Address string - BlockNumber uint64 + BlockNumber int64 } func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { @@ -179,7 +187,11 @@ func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - args.BlockNumber = uint64(ethutil.Big(obj[1].(string)).Int64()) + if obj[1].(string) == "latest" { + args.BlockNumber = -1 + } else { + args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() + } } return nil @@ -194,7 +206,7 @@ func (args *GetTxCountArgs) requirements() error { type GetBalanceArgs struct { Address string - BlockNumber uint64 + BlockNumber int64 } func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { @@ -210,7 +222,11 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - args.BlockNumber = uint64(ethutil.Big(obj[1].(string)).Int64()) + if obj[1].(string) == "latest" { + args.BlockNumber = -1 + } else { + args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() + } } return nil @@ -225,7 +241,7 @@ func (args *GetBalanceArgs) requirements() error { type GetDataArgs struct { Address string - BlockNumber uint64 + BlockNumber int64 } func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { @@ -241,7 +257,11 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - args.BlockNumber = uint64(ethutil.Big(obj[1].(string)).Int64()) + if obj[1].(string) == "latest" { + args.BlockNumber = -1 + } else { + args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() + } } return nil @@ -273,41 +293,41 @@ func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { return nil } -type FilterArgs struct { - FromBlock uint64 - ToBlock uint64 - Limit uint64 - Offset uint64 - Address string - Topics []string -} +// type FilterArgs struct { +// FromBlock uint64 +// ToBlock uint64 +// Limit uint64 +// Offset uint64 +// Address string +// Topics []string +// } -func (args *FilterArgs) UnmarshalJSON(b []byte) (err error) { - var obj []struct { - FromBlock string `json:"fromBlock"` - ToBlock string `json:"toBlock"` - Limit string `json:"limit"` - Offset string `json:"offset"` - Address string `json:"address"` - Topics []string `json:"topics"` - } +// func (args *FilterArgs) UnmarshalJSON(b []byte) (err error) { +// var obj []struct { +// FromBlock string `json:"fromBlock"` +// ToBlock string `json:"toBlock"` +// Limit string `json:"limit"` +// Offset string `json:"offset"` +// Address string `json:"address"` +// Topics []string `json:"topics"` +// } - if err = json.Unmarshal(b, &obj); err != nil { - return errDecodeArgs - } +// if err = json.Unmarshal(b, &obj); err != nil { +// return errDecodeArgs +// } - if len(obj) < 1 { - return errArguments - } - args.FromBlock = uint64(ethutil.Big(obj[0].FromBlock).Int64()) - args.ToBlock = uint64(ethutil.Big(obj[0].ToBlock).Int64()) - args.Limit = uint64(ethutil.Big(obj[0].Limit).Int64()) - args.Offset = uint64(ethutil.Big(obj[0].Offset).Int64()) - args.Address = obj[0].Address - args.Topics = obj[0].Topics +// if len(obj) < 1 { +// return errArguments +// } +// args.FromBlock = uint64(ethutil.Big(obj[0].FromBlock).Int64()) +// args.ToBlock = uint64(ethutil.Big(obj[0].ToBlock).Int64()) +// args.Limit = uint64(ethutil.Big(obj[0].Limit).Int64()) +// args.Offset = uint64(ethutil.Big(obj[0].Offset).Int64()) +// args.Address = obj[0].Address +// args.Topics = obj[0].Topics - return nil -} +// return nil +// } type FilterOptions struct { Earliest int64 @@ -415,8 +435,8 @@ func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { args.To = obj[0].To args.From = obj[0].From args.Topic = obj[0].Topic - args.Priority = uint32(ethutil.BytesToNumber(fromHex(obj[0].Priority))) - args.Ttl = uint32(ethutil.BytesToNumber(fromHex(obj[0].Ttl))) + args.Priority = uint32(ethutil.Big(obj[0].Priority).Int64()) + args.Ttl = uint32(ethutil.Big(obj[0].Ttl).Int64()) return nil } @@ -474,7 +494,7 @@ func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { return errDecodeArgs } - args.Id = int(ethutil.BytesToNumber(fromHex(obj[0]))) + args.Id = int(ethutil.Big(obj[0]).Int64()) return nil } @@ -500,13 +520,17 @@ func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { } type WhisperFilterArgs struct { - To string + To string `json:"to"` From string Topics []string } func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { - var obj []WhisperFilterArgs + var obj []struct { + To string + From string + Topics []string + } if err = json.Unmarshal(b, &obj); err != nil { return errDecodeArgs diff --git a/rpc/args_test.go b/rpc/args_test.go index 380ed176b3..1e6609b79e 100644 --- a/rpc/args_test.go +++ b/rpc/args_test.go @@ -30,6 +30,10 @@ func TestGetBalanceArgs(t *testing.T) { t.Error(err) } + if err := args.requirements(); err != nil { + t.Error(err) + } + if args.Address != expected.Address { t.Errorf("Address should be %v but is %v", expected.Address, args.Address) } @@ -123,3 +127,308 @@ func TestNewTxArgs(t *testing.T) { t.Errorf("Data shoud be %#v but is %#v", expected.Data, args.Data) } } + +func TestGetStorageArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` + expected := new(GetStorageArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = -1 + + args := new(GetStorageArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetStorageAtArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "0x0", "0x2"]` + expected := new(GetStorageAtArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.Key = "0x0" + expected.BlockNumber = 2 + + args := new(GetStorageAtArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.Key != args.Key { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetTxCountArgs(t *testing.T) { + input := `["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"]` + expected := new(GetTxCountArgs) + expected.Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + expected.BlockNumber = -1 + + args := new(GetTxCountArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestGetDataArgs(t *testing.T) { + input := `["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", "latest"]` + expected := new(GetDataArgs) + expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" + expected.BlockNumber = -1 + + args := new(GetDataArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.BlockNumber != args.BlockNumber { + t.Errorf("BlockNumber shoud be %#v but is %#v", expected.BlockNumber, args.BlockNumber) + } +} + +func TestFilterOptions(t *testing.T) { + input := `[{ + "fromBlock": "0x1", + "toBlock": "0x2", + "limit": "0x3", + "offset": "0x0", + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": ["0x12341234"]}]` + expected := new(FilterOptions) + expected.Earliest = 1 + expected.Latest = 2 + expected.Max = 3 + expected.Skip = 0 + expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" + expected.Topic = []string{"0x12341234"} + + args := new(FilterOptions) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Earliest != args.Earliest { + t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) + } + + if expected.Latest != args.Latest { + t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest) + } + + if expected.Max != args.Max { + t.Errorf("Max shoud be %#v but is %#v", expected.Max, args.Max) + } + + if expected.Skip != args.Skip { + t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip) + } + + if expected.Address != args.Address { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + // if expected.Topic != args.Topic { + // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) + // } +} + +func TestDbArgs(t *testing.T) { + input := `["0x74657374","0x6b6579","0x6d79537472696e67"]` + expected := new(DbArgs) + expected.Database = "0x74657374" + expected.Key = "0x6b6579" + expected.Value = "0x6d79537472696e67" + + args := new(DbArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if err := args.requirements(); err != nil { + t.Error(err) + } + + if expected.Database != args.Database { + t.Errorf("Database shoud be %#v but is %#v", expected.Database, args.Database) + } + + if expected.Key != args.Key { + t.Errorf("Key shoud be %#v but is %#v", expected.Key, args.Key) + } + + if expected.Value != args.Value { + t.Errorf("Value shoud be %#v but is %#v", expected.Value, args.Value) + } +} + +func TestWhisperMessageArgs(t *testing.T) { + input := `[{"from":"0xc931d93e97ab07fe42d923478ba2465f2", + "topics": ["0x68656c6c6f20776f726c64"], + "payload":"0x68656c6c6f20776f726c64", + "ttl": "0x64", + "priority": "0x64"}]` + expected := new(WhisperMessageArgs) + expected.From = "0xc931d93e97ab07fe42d923478ba2465f2" + expected.To = "" + expected.Payload = "0x68656c6c6f20776f726c64" + expected.Priority = 100 + expected.Ttl = 100 + expected.Topic = []string{"0x68656c6c6f20776f726c64"} + + args := new(WhisperMessageArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + if expected.Payload != args.Payload { + t.Errorf("Value shoud be %#v but is %#v", expected.Payload, args.Payload) + } + + if expected.Ttl != args.Ttl { + t.Errorf("Ttl shoud be %#v but is %#v", expected.Ttl, args.Ttl) + } + + if expected.Priority != args.Priority { + t.Errorf("Priority shoud be %#v but is %#v", expected.Priority, args.Priority) + } + + // if expected.Topic != args.Topic { + // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) + // } +} + +func TestFilterIdArgs(t *testing.T) { + input := `["0x7"]` + expected := new(FilterIdArgs) + expected.Id = 7 + + args := new(FilterIdArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Id != args.Id { + t.Errorf("Id shoud be %#v but is %#v", expected.Id, args.Id) + } +} + +func TestWhsiperFilterArgs(t *testing.T) { + input := `[{"topics": ["0x68656c6c6f20776f726c64"], "to": "0x34ag445g3455b34"}]` + expected := new(WhisperFilterArgs) + expected.From = "" + expected.To = "0x34ag445g3455b34" + expected.Topics = []string{"0x68656c6c6f20776f726c64"} + + args := new(WhisperFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.From != args.From { + t.Errorf("From shoud be %#v but is %#v", expected.From, args.From) + } + + if expected.To != args.To { + t.Errorf("To shoud be %#v but is %#v", expected.To, args.To) + } + + // if expected.Topics != args.Topics { + // t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + // } +} + +func TestCompileArgs(t *testing.T) { + input := `["contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"]` + expected := new(CompileArgs) + expected.Source = `contract test { function multiply(uint a) returns(uint d) { return a * 7; } }` + + args := new(CompileArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Source != args.Source { + t.Errorf("Source shoud be %#v but is %#v", expected.Source, args.Source) + } +} + +func TestFilterStringArgs(t *testing.T) { + input := `["pending"]` + expected := new(FilterStringArgs) + expected.Word = "pending" + + args := new(FilterStringArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Word != args.Word { + t.Errorf("Word shoud be %#v but is %#v", expected.Word, args.Word) + } +} + +func TestWhisperIdentityArgs(t *testing.T) { + input := `["0xc931d93e97ab07fe42d923478ba2465f283"]` + expected := new(WhisperIdentityArgs) + expected.Identity = "0xc931d93e97ab07fe42d923478ba2465f283" + + args := new(WhisperIdentityArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Identity != args.Identity { + t.Errorf("Identity shoud be %#v but is %#v", expected.Identity, args.Identity) + } +} diff --git a/xeth/xeth.go b/xeth/xeth.go index 451d3ceea7..b78a45f881 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -86,17 +86,21 @@ func (self *XEth) BlockByHash(strHash string) *Block { return NewBlock(block) } -func (self *XEth) BlockByNumber(num uint64) *Block { - return NewBlock(self.chainManager.GetBlockByNumber(num)) +func (self *XEth) BlockByNumber(num int64) *Block { + if num == -1 { + return NewBlock(self.chainManager.CurrentBlock()) + } + + return NewBlock(self.chainManager.GetBlockByNumber(uint64(num))) } func (self *XEth) Block(v interface{}) *Block { if n, ok := v.(int32); ok { - return self.BlockByNumber(uint64(n)) + return self.BlockByNumber(int64(n)) } else if str, ok := v.(string); ok { return self.BlockByHash(str) } else if f, ok := v.(float64); ok { // Don't ask ... - return self.BlockByNumber(uint64(f)) + return self.BlockByNumber(int64(f)) } return nil From d66f93cecdbae6a88bfb710e0d95d62340bf2460 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sat, 7 Mar 2015 12:38:33 +0100 Subject: [PATCH 19/82] accounts, core, eth, xeth: use account manager for everything The account manager is now responsible for picking the default account and the coinbase. --- accounts/account_manager.go | 39 ++++++++++++++++++++++++------ core/manager.go | 2 -- eth/backend.go | 47 +++++++++++-------------------------- xeth/xeth.go | 18 +++++++++----- 4 files changed, 58 insertions(+), 48 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 3e9fa77991..3b77852318 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -42,7 +42,10 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) -var ErrLocked = errors.New("account is locked; please request passphrase") +var ( + ErrLocked = errors.New("account is locked") + ErrNoKeys = errors.New("no keys in store") +) // TODO: better name for this struct? type Account struct { @@ -56,17 +59,39 @@ type AccountManager struct { mutex sync.RWMutex } -func NewAccountManager(keyStore crypto.KeyStore2, unlockMilliseconds time.Duration) AccountManager { - keysMap := make(map[string]crypto.Key) - am := &AccountManager{ +func NewAccountManager(keyStore crypto.KeyStore2, unlockMilliseconds time.Duration) *AccountManager { + return &AccountManager{ keyStore: keyStore, - unlockedKeys: keysMap, + unlockedKeys: make(map[string]crypto.Key), unlockMilliseconds: unlockMilliseconds, } - return *am } -func (am AccountManager) DeleteAccount(address []byte, auth string) error { +// Coinbase returns the account address that mining rewards are sent to. +func (am *AccountManager) Coinbase() (addr []byte, err error) { + // TODO: persist coinbase address on disk + return am.firstAddr() +} + +// MainAccount returns the primary account used for transactions. +func (am *AccountManager) Default() (*Account, error) { + // TODO: persist main account address on disk + addr, err := am.firstAddr() + return &Account{Address: addr}, err +} + +func (am *AccountManager) firstAddr() ([]byte, error) { + addrs, err := am.keyStore.GetKeyAddresses() + if err != nil { + return nil, err + } + if len(addrs) == 0 { + return nil, ErrNoKeys + } + return addrs[0], nil +} + +func (am *AccountManager) DeleteAccount(address []byte, auth string) error { return am.keyStore.DeleteKey(address, auth) } diff --git a/core/manager.go b/core/manager.go index bb039d063b..29f786653c 100644 --- a/core/manager.go +++ b/core/manager.go @@ -1,7 +1,6 @@ package core import ( - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" @@ -14,7 +13,6 @@ type Backend interface { PeerCount() int IsListening() bool Peers() []*p2p.Peer - KeyManager() *crypto.KeyManager Db() ethutil.Database EventMux() *event.TypeMux } diff --git a/eth/backend.go b/eth/backend.go index 02e7e2746d..cc5284dfa3 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -38,11 +38,9 @@ var ( type Config struct { Name string - KeyStore string DataDir string LogFile string LogLevel int - KeyRing string LogFormat string MaxPeers int @@ -60,9 +58,8 @@ type Config struct { Shh bool Dial bool - MinerThreads int - - KeyManager *crypto.KeyManager + MinerThreads int + AccountManager *accounts.AccountManager } func (cfg *Config) parseBootNodes() []*discover.Node { @@ -127,8 +124,7 @@ type Ethereum struct { blockSub event.Subscription miner *miner.Miner - RpcServer rpc.RpcServer - keyManager *crypto.KeyManager + RpcServer rpc.RpcServer logger logger.LogSystem @@ -153,35 +149,22 @@ func New(config *Config) (*Ethereum, error) { return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, ProtocolVersion, path) } - // Create new keymanager - var keyManager *crypto.KeyManager - switch config.KeyStore { - case "db": - keyManager = crypto.NewDBKeyManager(db) - case "file": - keyManager = crypto.NewFileKeyManager(config.DataDir) - default: - return nil, fmt.Errorf("unknown keystore type: %s", config.KeyStore) - } - // Initialise the keyring - keyManager.Init(config.KeyRing, 0, false) - saveProtocolVersion(db) //ethutil.Config.Db = db eth := &Ethereum{ - shutdownChan: make(chan bool), - db: db, - keyManager: keyManager, - eventMux: &event.TypeMux{}, - logger: ethlogger, - DataDir: config.DataDir, + shutdownChan: make(chan bool), + db: db, + eventMux: &event.TypeMux{}, + logger: ethlogger, + accountManager: config.AccountManager, + DataDir: config.DataDir, } - // TODO: add config flag and case on plain/protected key store - ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir()) - am := accounts.NewAccountManager(ks, 300000) // keys unlocked for 300s - eth.accountManager = &am + cb, err := eth.accountManager.Coinbase() + if err != nil { + return nil, fmt.Errorf("no coinbase: %v", err) + } eth.chainManager = core.NewChainManager(db, eth.EventMux()) pow := ethash.New(eth.chainManager) @@ -189,7 +172,7 @@ func New(config *Config) (*Ethereum, error) { eth.blockProcessor = core.NewBlockProcessor(db, pow, eth.txPool, eth.chainManager, eth.EventMux()) eth.chainManager.SetProcessor(eth.blockProcessor) eth.whisper = whisper.New() - eth.miner = miner.New(keyManager.Address(), eth, pow, config.MinerThreads) + eth.miner = miner.New(cb, eth, pow, config.MinerThreads) hasBlock := eth.chainManager.HasBlock insertChain := eth.chainManager.InsertChain @@ -221,7 +204,6 @@ func New(config *Config) (*Ethereum, error) { return eth, nil } -func (s *Ethereum) KeyManager() *crypto.KeyManager { return s.keyManager } func (s *Ethereum) Logger() logger.LogSystem { return s.logger } func (s *Ethereum) Name() string { return s.net.Name } func (s *Ethereum) AccountManager() *accounts.AccountManager { return s.accountManager } @@ -237,7 +219,6 @@ func (s *Ethereum) IsListening() bool { return true } // func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } -func (s *Ethereum) Coinbase() []byte { return nil } // TODO // Start the ethereum func (s *Ethereum) Start() error { diff --git a/xeth/xeth.go b/xeth/xeth.go index 91bd35f8e7..1ad62a7bf6 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -32,7 +32,6 @@ type Backend interface { PeerCount() int IsListening() bool Peers() []*p2p.Peer - KeyManager() *crypto.KeyManager Db() ethutil.Database EventMux() *event.TypeMux Whisper() *whisper.Whisper @@ -142,7 +141,8 @@ func (self *XEth) IsListening() bool { } func (self *XEth) Coinbase() string { - return toHex(self.eth.KeyManager().Address()) + cb, _ := self.eth.AccountManager().Coinbase() + return toHex(cb) } func (self *XEth) NumberToHuman(balance string) string { @@ -251,10 +251,13 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st gasPriceStr = "1" } + acct, err := self.accountManager.Default() + if err != nil { + return "", err + } var ( statedb = self.State().State() //self.chainManager.TransState() - key = self.eth.KeyManager().KeyPair() - from = statedb.GetOrNewStateObject(key.Address()) + from = statedb.GetOrNewStateObject(acct.Address) block = self.chainManager.CurrentBlock() to = statedb.GetOrNewStateObject(fromHex(toStr)) data = fromHex(dataStr) @@ -264,9 +267,12 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st ) msg := types.NewTransactionMessage(fromHex(toStr), value, gas, price, data) - msg.Sign(key.PrivateKey) + sig, err := self.accountManager.Sign(acct, msg.Hash()) + if err != nil { + return "", err + } + msg.SetSignatureValues(sig) vmenv := core.NewEnv(statedb, self.chainManager, msg, block) - res, err := vmenv.Call(from, to.Address(), data, gas, price, value) if err != nil { return "", err From a2810c06d7cfc64e1636fe4ecfd5e35cc52b0d2b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sat, 7 Mar 2015 12:39:52 +0100 Subject: [PATCH 20/82] cmd/ethereum: add account commands --- cmd/ethereum/main.go | 79 +++++++++++++++++++++++++++++++++++++++----- cmd/utils/flags.go | 48 ++++++++++++--------------- 2 files changed, 93 insertions(+), 34 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 1133bd6f72..f12616e179 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -21,6 +21,7 @@ package main import ( + "bufio" "fmt" "os" "runtime" @@ -34,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/state" + "github.com/peterh/liner" ) const ( @@ -60,6 +62,23 @@ func init() { The output of this command is supposed to be machine-readable. `, }, + { + Action: accountList, + Name: "account", + Usage: "manage accounts", + Subcommands: []cli.Command{ + { + Action: accountList, + Name: "list", + Usage: "print account addresses", + }, + { + Action: accountCreate, + Name: "new", + Usage: "create a new account", + }, + }, + }, { Action: dump, Name: "dump", @@ -93,8 +112,6 @@ runtime will execute the file and exit. app.Flags = []cli.Flag{ utils.BootnodesFlag, utils.DataDirFlag, - utils.KeyRingFlag, - utils.KeyStoreFlag, utils.ListenPortFlag, utils.LogFileFlag, utils.LogFormatFlag, @@ -166,6 +183,37 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) { } } +func accountList(ctx *cli.Context) { + am := utils.GetAccountManager(ctx) + accts, err := am.Accounts() + if err != nil { + utils.Fatalf("Could not list accounts: %v", err) + } + for _, acct := range accts { + fmt.Printf("Address: %#x\n", acct) + } +} + +func accountCreate(ctx *cli.Context) { + am := utils.GetAccountManager(ctx) + auth, err := readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } + confirm, err := readPassword("Repeat Passphrase: ", false) + if err != nil { + utils.Fatalf("%v", err) + } + if auth != confirm { + utils.Fatalf("Passphrases did not match.") + } + acct, err := am.NewAccount(auth) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: %#x\n", acct.Address) +} + func importchain(ctx *cli.Context) { if len(ctx.Args()) != 1 { utils.Fatalf("This command requires an argument.") @@ -201,12 +249,6 @@ func dump(ctx *cli.Context) { } } -// hashish returns true for strings that look like hashes. -func hashish(x string) bool { - _, err := strconv.Atoi(x) - return err != nil -} - func version(c *cli.Context) { fmt.Printf(`%v %v PV=%d @@ -216,3 +258,24 @@ GOPATH=%s GOROOT=%s `, ClientIdentifier, Version, eth.ProtocolVersion, runtime.GOOS, runtime.Version(), os.Getenv("GOPATH"), runtime.GOROOT()) } + +// hashish returns true for strings that look like hashes. +func hashish(x string) bool { + _, err := strconv.Atoi(x) + return err != nil +} + +func readPassword(prompt string, warnTerm bool) (string, error) { + if liner.TerminalSupported() { + lr := liner.NewLiner() + defer lr.Close() + return lr.PasswordPrompt(prompt) + } + if warnTerm { + fmt.Println("!! Unsupported terminal, password will be echoed.") + } + fmt.Print(prompt) + input, err := bufio.NewReader(os.Stdin).ReadString('\n') + fmt.Println() + return input, err +} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index fb80ac7084..4f3ecd2b28 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -4,8 +4,10 @@ import ( "crypto/ecdsa" "path" "runtime" + "time" "github.com/codegangsta/cli" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" @@ -30,16 +32,6 @@ var ( Name: "vm", Usage: "Virtual Machine type: 0 is standard VM, 1 is debug VM", } - KeyRingFlag = cli.StringFlag{ - Name: "keyring", - Usage: "Name of keyring to be used", - Value: "", - } - KeyStoreFlag = cli.StringFlag{ - Name: "keystore", - Usage: `Where to store keyrings: "db" or "file"`, - Value: "db", - } DataDirFlag = cli.StringFlag{ Name: "datadir", Usage: "Data directory to be used", @@ -145,22 +137,20 @@ func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) { func GetEthereum(clientID, version string, ctx *cli.Context) *eth.Ethereum { ethereum, err := eth.New(ð.Config{ - Name: p2p.MakeName(clientID, version), - KeyStore: ctx.GlobalString(KeyStoreFlag.Name), - DataDir: ctx.GlobalString(DataDirFlag.Name), - LogFile: ctx.GlobalString(LogFileFlag.Name), - LogLevel: ctx.GlobalInt(LogLevelFlag.Name), - LogFormat: ctx.GlobalString(LogFormatFlag.Name), - MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), - - MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), - Port: ctx.GlobalString(ListenPortFlag.Name), - NAT: GetNAT(ctx), - NodeKey: GetNodeKey(ctx), - KeyRing: ctx.GlobalString(KeyRingFlag.Name), - Shh: true, - Dial: true, - BootNodes: ctx.GlobalString(BootnodesFlag.Name), + Name: p2p.MakeName(clientID, version), + DataDir: ctx.GlobalString(DataDirFlag.Name), + LogFile: ctx.GlobalString(LogFileFlag.Name), + LogLevel: ctx.GlobalInt(LogLevelFlag.Name), + LogFormat: ctx.GlobalString(LogFormatFlag.Name), + MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), + AccountManager: GetAccountManager(ctx), + MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), + Port: ctx.GlobalString(ListenPortFlag.Name), + NAT: GetNAT(ctx), + NodeKey: GetNodeKey(ctx), + Shh: true, + Dial: true, + BootNodes: ctx.GlobalString(BootnodesFlag.Name), }) if err != nil { exit(err) @@ -176,3 +166,9 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database) { } return core.NewChainManager(db, new(event.TypeMux)), db } + +func GetAccountManager(ctx *cli.Context) *accounts.AccountManager { + dataDir := ctx.GlobalString(DataDirFlag.Name) + ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) + return accounts.NewAccountManager(ks, 300*time.Second) +} From fda7b4c79d070f1cb4f5d7ef5b4d077d9dcf2774 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 8 Mar 2015 00:18:13 +0100 Subject: [PATCH 21/82] accounts: use pointers consistently Account is now always a non-pointer. This will be important once the manager starts remembering accounts. AccountManager is now always a pointer because it contains locks and locks cannot be copied. --- accounts/account_manager.go | 26 +++++++++++--------------- accounts/accounts_test.go | 3 ++- xeth/xeth.go | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 3b77852318..86f9c59161 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -47,7 +47,6 @@ var ( ErrNoKeys = errors.New("no keys in store") ) -// TODO: better name for this struct? type Account struct { Address []byte } @@ -74,10 +73,10 @@ func (am *AccountManager) Coinbase() (addr []byte, err error) { } // MainAccount returns the primary account used for transactions. -func (am *AccountManager) Default() (*Account, error) { +func (am *AccountManager) Default() (Account, error) { // TODO: persist main account address on disk addr, err := am.firstAddr() - return &Account{Address: addr}, err + return Account{Address: addr}, err } func (am *AccountManager) firstAddr() ([]byte, error) { @@ -95,9 +94,9 @@ func (am *AccountManager) DeleteAccount(address []byte, auth string) error { return am.keyStore.DeleteKey(address, auth) } -func (am *AccountManager) Sign(fromAccount *Account, toSign []byte) (signature []byte, err error) { +func (am *AccountManager) Sign(a Account, toSign []byte) (signature []byte, err error) { am.mutex.RLock() - unlockedKey := am.unlockedKeys[string(fromAccount.Address)] + unlockedKey := am.unlockedKeys[string(a.Address)] am.mutex.RUnlock() if unlockedKey.Address == nil { return nil, ErrLocked @@ -106,28 +105,25 @@ func (am *AccountManager) Sign(fromAccount *Account, toSign []byte) (signature [ 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) +func (am *AccountManager) SignLocked(a Account, keyAuth string, toSign []byte) (signature []byte, err error) { + key, err := am.keyStore.GetKey(a.Address, keyAuth) if err != nil { return nil, err } am.mutex.RLock() - am.unlockedKeys[string(fromAccount.Address)] = *key + am.unlockedKeys[string(a.Address)] = *key am.mutex.RUnlock() - go unlockLater(am, fromAccount.Address) + go unlockLater(am, a.Address) signature, err = crypto.Sign(toSign, key.PrivateKey) return signature, err } -func (am AccountManager) NewAccount(auth string) (*Account, error) { +func (am *AccountManager) NewAccount(auth string) (Account, error) { key, err := am.keyStore.GenerateNewKey(crand.Reader, auth) if err != nil { - return nil, err + return Account{}, err } - ua := &Account{ - Address: key.Address, - } - return ua, err + return Account{Address: key.Address}, nil } func (am *AccountManager) Accounts() ([]Account, error) { diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index 44d1d72f17..d8187220a4 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -3,10 +3,11 @@ package accounts import ( "testing" + "time" + "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) { diff --git a/xeth/xeth.go b/xeth/xeth.go index 1ad62a7bf6..187aa8c0fb 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -311,7 +311,7 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt nonce := state.GetNonce(from) tx.SetNonce(nonce) - sig, err := self.accountManager.Sign(&accounts.Account{Address: from}, tx.Hash()) + sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash()) if err != nil { return "", err } From afc530ea411e18223b0323d7e11aa0fab9289d65 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 8 Mar 2015 00:35:23 +0100 Subject: [PATCH 22/82] accounts: use time.Duration correctly There is no point to using time.Duration if the value is interpreted as milliseconds. Callers should use the standard multiplication idiom to choose the unit. In fact, the only caller outside of the tests already does so. --- accounts/account_manager.go | 19 ++++++++++--------- accounts/accounts_test.go | 8 ++++---- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 86f9c59161..f87cce65fc 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -33,6 +33,7 @@ and accounts persistence is derived from stored keys' addresses package accounts import ( + "crypto/ecdsa" crand "crypto/rand" "errors" @@ -52,17 +53,17 @@ type Account struct { } type AccountManager struct { - keyStore crypto.KeyStore2 - unlockedKeys map[string]crypto.Key - unlockMilliseconds time.Duration - mutex sync.RWMutex + keyStore crypto.KeyStore2 + unlockedKeys map[string]crypto.Key + unlockTime time.Duration + mutex sync.RWMutex } -func NewAccountManager(keyStore crypto.KeyStore2, unlockMilliseconds time.Duration) *AccountManager { +func NewAccountManager(keyStore crypto.KeyStore2, unlockTime time.Duration) *AccountManager { return &AccountManager{ - keyStore: keyStore, - unlockedKeys: make(map[string]crypto.Key), - unlockMilliseconds: unlockMilliseconds, + keyStore: keyStore, + unlockedKeys: make(map[string]crypto.Key), + unlockTime: unlockTime, } } @@ -144,7 +145,7 @@ func (am *AccountManager) Accounts() ([]Account, error) { func unlockLater(am *AccountManager, addr []byte) { select { - case <-time.After(time.Millisecond * am.unlockMilliseconds): + case <-time.After(am.unlockTime): } am.mutex.RLock() // TODO: how do we know the key is actually gone from memory? diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index d8187220a4..30e0b011ad 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -12,7 +12,7 @@ import ( func TestAccountManager(t *testing.T) { ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir() + "/testaccounts") - am := NewAccountManager(ks, 100) + am := NewAccountManager(ks, 100*time.Millisecond) pass := "" // not used but required by API a1, err := am.NewAccount(pass) toSign := randentropy.GetEntropyCSPRNG(32) @@ -22,7 +22,7 @@ func TestAccountManager(t *testing.T) { } // Cleanup - time.Sleep(time.Millisecond * 150) // wait for locking + time.Sleep(150 * time.Millisecond) // wait for locking accounts, err := am.Accounts() if err != nil { @@ -38,7 +38,7 @@ func TestAccountManager(t *testing.T) { func TestAccountManagerLocking(t *testing.T) { ks := crypto.NewKeyStorePassphrase(ethutil.DefaultDataDir() + "/testaccounts") - am := NewAccountManager(ks, 200) + am := NewAccountManager(ks, 200*time.Millisecond) pass := "foo" a1, err := am.NewAccount(pass) toSign := randentropy.GetEntropyCSPRNG(32) @@ -62,7 +62,7 @@ func TestAccountManagerLocking(t *testing.T) { } // Signing without passphrase fails after automatic locking - time.Sleep(time.Millisecond * time.Duration(250)) + time.Sleep(250 * time.Millisecond) _, err = am.Sign(a1, toSign) if err != ErrLocked { From d6a7332993cf32960ef94947341cedd3061559a7 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 8 Mar 2015 00:36:06 +0100 Subject: [PATCH 23/82] accounts: fix uses of sync.RWMutex RWMutexes must be write-locked when writing in order to actually protect the writes. --- accounts/account_manager.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index f87cce65fc..c0f2953bd6 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -111,9 +111,9 @@ func (am *AccountManager) SignLocked(a Account, keyAuth string, toSign []byte) ( if err != nil { return nil, err } - am.mutex.RLock() + am.mutex.Lock() am.unlockedKeys[string(a.Address)] = *key - am.mutex.RUnlock() + am.mutex.Unlock() go unlockLater(am, a.Address) signature, err = crypto.Sign(toSign, key.PrivateKey) return signature, err @@ -147,8 +147,10 @@ func unlockLater(am *AccountManager, addr []byte) { select { case <-time.After(am.unlockTime): } - am.mutex.RLock() + am.mutex.Lock() // TODO: how do we know the key is actually gone from memory? delete(am.unlockedKeys, string(addr)) - am.mutex.RUnlock() + am.mutex.Unlock() +} + } From 3750ec7b7de04d8482b798e73f04637ea9e1ca89 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 8 Mar 2015 01:27:30 +0100 Subject: [PATCH 24/82] accounts: prevent early drops and zero keys in memory when dropping Private keys would be locked early if SignLocked was called more than once because the unlockLater was still running. Terminate it properly. --- accounts/account_manager.go | 69 +++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index c0f2953bd6..bb6d970b27 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -53,17 +53,24 @@ type Account struct { } type AccountManager struct { - keyStore crypto.KeyStore2 - unlockedKeys map[string]crypto.Key - unlockTime time.Duration - mutex sync.RWMutex + keyStore crypto.KeyStore2 + unlocked map[string]*unlocked + unlockTime time.Duration + mutex sync.RWMutex +} + +type unlocked struct { + addr []byte + abort chan struct{} + + *crypto.Key } func NewAccountManager(keyStore crypto.KeyStore2, unlockTime time.Duration) *AccountManager { return &AccountManager{ - keyStore: keyStore, - unlockedKeys: make(map[string]crypto.Key), - unlockTime: unlockTime, + keyStore: keyStore, + unlocked: make(map[string]*unlocked), + unlockTime: unlockTime, } } @@ -97,9 +104,9 @@ func (am *AccountManager) DeleteAccount(address []byte, auth string) error { func (am *AccountManager) Sign(a Account, toSign []byte) (signature []byte, err error) { am.mutex.RLock() - unlockedKey := am.unlockedKeys[string(a.Address)] + unlockedKey, found := am.unlocked[string(a.Address)] am.mutex.RUnlock() - if unlockedKey.Address == nil { + if !found { return nil, ErrLocked } signature, err = crypto.Sign(toSign, unlockedKey.PrivateKey) @@ -111,10 +118,8 @@ func (am *AccountManager) SignLocked(a Account, keyAuth string, toSign []byte) ( if err != nil { return nil, err } - am.mutex.Lock() - am.unlockedKeys[string(a.Address)] = *key - am.mutex.Unlock() - go unlockLater(am, a.Address) + u := am.addUnlocked(a.Address, key) + go am.dropLater(u) signature, err = crypto.Sign(toSign, key.PrivateKey) return signature, err } @@ -143,14 +148,40 @@ func (am *AccountManager) Accounts() ([]Account, error) { return accounts, err } -func unlockLater(am *AccountManager, addr []byte) { - select { - case <-time.After(am.unlockTime): - } +func (am *AccountManager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { + u := &unlocked{addr: addr, abort: make(chan struct{}), Key: key} am.mutex.Lock() - // TODO: how do we know the key is actually gone from memory? - delete(am.unlockedKeys, string(addr)) + prev, found := am.unlocked[string(addr)] + if found { + // terminate dropLater for this key to avoid unexpected drops. + close(prev.abort) + zeroKey(prev.PrivateKey) + } + am.unlocked[string(addr)] = u am.mutex.Unlock() + return u } +func (am *AccountManager) dropLater(u *unlocked) { + t := time.NewTimer(am.unlockTime) + defer t.Stop() + select { + case <-u.abort: + // just quit + case <-t.C: + am.mutex.Lock() + if am.unlocked[string(u.addr)] == u { + zeroKey(u.PrivateKey) + delete(am.unlocked, string(u.addr)) + } + am.mutex.Unlock() + } +} + +// zeroKey zeroes a private key in memory. +func zeroKey(k *ecdsa.PrivateKey) { + b := k.D.Bits() + for i := range b { + b[i] = 0 + } } From fb53a9362e1238d8edb466d77427dc3cbb13eb20 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 8 Mar 2015 01:52:49 +0100 Subject: [PATCH 25/82] accounts: AccountManager -> Manager --- accounts/account_manager.go | 26 ++++++++++++------------ accounts/accounts_test.go | 4 ++-- cmd/utils/flags.go | 4 ++-- eth/backend.go | 34 ++++++++++++++++---------------- javascript/javascript_runtime.go | 1 + xeth/xeth.go | 4 ++-- 6 files changed, 37 insertions(+), 36 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index bb6d970b27..97cf7c878b 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -52,7 +52,7 @@ type Account struct { Address []byte } -type AccountManager struct { +type Manager struct { keyStore crypto.KeyStore2 unlocked map[string]*unlocked unlockTime time.Duration @@ -66,8 +66,8 @@ type unlocked struct { *crypto.Key } -func NewAccountManager(keyStore crypto.KeyStore2, unlockTime time.Duration) *AccountManager { - return &AccountManager{ +func NewManager(keyStore crypto.KeyStore2, unlockTime time.Duration) *Manager { + return &Manager{ keyStore: keyStore, unlocked: make(map[string]*unlocked), unlockTime: unlockTime, @@ -75,19 +75,19 @@ func NewAccountManager(keyStore crypto.KeyStore2, unlockTime time.Duration) *Acc } // Coinbase returns the account address that mining rewards are sent to. -func (am *AccountManager) Coinbase() (addr []byte, err error) { +func (am *Manager) Coinbase() (addr []byte, err error) { // TODO: persist coinbase address on disk return am.firstAddr() } // MainAccount returns the primary account used for transactions. -func (am *AccountManager) Default() (Account, error) { +func (am *Manager) Default() (Account, error) { // TODO: persist main account address on disk addr, err := am.firstAddr() return Account{Address: addr}, err } -func (am *AccountManager) firstAddr() ([]byte, error) { +func (am *Manager) firstAddr() ([]byte, error) { addrs, err := am.keyStore.GetKeyAddresses() if err != nil { return nil, err @@ -98,11 +98,11 @@ func (am *AccountManager) firstAddr() ([]byte, error) { return addrs[0], nil } -func (am *AccountManager) DeleteAccount(address []byte, auth string) error { +func (am *Manager) DeleteAccount(address []byte, auth string) error { return am.keyStore.DeleteKey(address, auth) } -func (am *AccountManager) Sign(a Account, toSign []byte) (signature []byte, err error) { +func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) { am.mutex.RLock() unlockedKey, found := am.unlocked[string(a.Address)] am.mutex.RUnlock() @@ -113,7 +113,7 @@ func (am *AccountManager) Sign(a Account, toSign []byte) (signature []byte, err return signature, err } -func (am *AccountManager) SignLocked(a Account, keyAuth string, toSign []byte) (signature []byte, err error) { +func (am *Manager) SignLocked(a Account, keyAuth string, toSign []byte) (signature []byte, err error) { key, err := am.keyStore.GetKey(a.Address, keyAuth) if err != nil { return nil, err @@ -124,7 +124,7 @@ func (am *AccountManager) SignLocked(a Account, keyAuth string, toSign []byte) ( return signature, err } -func (am *AccountManager) NewAccount(auth string) (Account, error) { +func (am *Manager) NewAccount(auth string) (Account, error) { key, err := am.keyStore.GenerateNewKey(crand.Reader, auth) if err != nil { return Account{}, err @@ -132,7 +132,7 @@ func (am *AccountManager) NewAccount(auth string) (Account, error) { return Account{Address: key.Address}, nil } -func (am *AccountManager) Accounts() ([]Account, error) { +func (am *Manager) Accounts() ([]Account, error) { addresses, err := am.keyStore.GetKeyAddresses() if err != nil { return nil, err @@ -148,7 +148,7 @@ func (am *AccountManager) Accounts() ([]Account, error) { return accounts, err } -func (am *AccountManager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { +func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { u := &unlocked{addr: addr, abort: make(chan struct{}), Key: key} am.mutex.Lock() prev, found := am.unlocked[string(addr)] @@ -162,7 +162,7 @@ func (am *AccountManager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { return u } -func (am *AccountManager) dropLater(u *unlocked) { +func (am *Manager) dropLater(u *unlocked) { t := time.NewTimer(am.unlockTime) defer t.Stop() select { diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index 30e0b011ad..b90da2892e 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -12,7 +12,7 @@ import ( func TestAccountManager(t *testing.T) { ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir() + "/testaccounts") - am := NewAccountManager(ks, 100*time.Millisecond) + am := NewManager(ks, 100*time.Millisecond) pass := "" // not used but required by API a1, err := am.NewAccount(pass) toSign := randentropy.GetEntropyCSPRNG(32) @@ -38,7 +38,7 @@ func TestAccountManager(t *testing.T) { func TestAccountManagerLocking(t *testing.T) { ks := crypto.NewKeyStorePassphrase(ethutil.DefaultDataDir() + "/testaccounts") - am := NewAccountManager(ks, 200*time.Millisecond) + am := NewManager(ks, 200*time.Millisecond) pass := "foo" a1, err := am.NewAccount(pass) toSign := randentropy.GetEntropyCSPRNG(32) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 4f3ecd2b28..e6eee20a7d 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -167,8 +167,8 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database) { return core.NewChainManager(db, new(event.TypeMux)), db } -func GetAccountManager(ctx *cli.Context) *accounts.AccountManager { +func GetAccountManager(ctx *cli.Context) *accounts.Manager { dataDir := ctx.GlobalString(DataDirFlag.Name) ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) - return accounts.NewAccountManager(ks, 300*time.Second) + return accounts.NewManager(ks, 300*time.Second) } diff --git a/eth/backend.go b/eth/backend.go index cc5284dfa3..680cc175a3 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -59,7 +59,7 @@ type Config struct { Dial bool MinerThreads int - AccountManager *accounts.AccountManager + AccountManager *accounts.Manager } func (cfg *Config) parseBootNodes() []*discover.Node { @@ -115,7 +115,7 @@ type Ethereum struct { txPool *core.TxPool chainManager *core.ChainManager blockPool *blockpool.BlockPool - accountManager *accounts.AccountManager + accountManager *accounts.Manager whisper *whisper.Whisper net *p2p.Server @@ -204,21 +204,21 @@ func New(config *Config) (*Ethereum, error) { return eth, nil } -func (s *Ethereum) Logger() logger.LogSystem { return s.logger } -func (s *Ethereum) Name() string { return s.net.Name } -func (s *Ethereum) AccountManager() *accounts.AccountManager { return s.accountManager } -func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager } -func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } -func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } -func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool } -func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } -func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } -func (s *Ethereum) Db() ethutil.Database { return s.db } -func (s *Ethereum) Miner() *miner.Miner { return s.miner } -func (s *Ethereum) IsListening() bool { return true } // Always listening -func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } -func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } -func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } +func (s *Ethereum) Logger() logger.LogSystem { return s.logger } +func (s *Ethereum) Name() string { return s.net.Name } +func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } +func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager } +func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } +func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } +func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool } +func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } +func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } +func (s *Ethereum) Db() ethutil.Database { return s.db } +func (s *Ethereum) Miner() *miner.Miner { return s.miner } +func (s *Ethereum) IsListening() bool { return true } // Always listening +func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } +func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } +func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } // Start the ethereum func (s *Ethereum) Start() error { diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go index 36b14a057d..0a137f72a0 100644 --- a/javascript/javascript_runtime.go +++ b/javascript/javascript_runtime.go @@ -6,6 +6,7 @@ import ( "os" "path" "path/filepath" + "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/xeth" "github.com/obscuren/otto" diff --git a/xeth/xeth.go b/xeth/xeth.go index 187aa8c0fb..afe680f348 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -27,7 +27,7 @@ var pipelogger = logger.NewLogger("XETH") type Backend interface { BlockProcessor() *core.BlockProcessor ChainManager() *core.ChainManager - AccountManager() *accounts.AccountManager + AccountManager() *accounts.Manager TxPool() *core.TxPool PeerCount() int IsListening() bool @@ -42,7 +42,7 @@ type XEth struct { eth Backend blockProcessor *core.BlockProcessor chainManager *core.ChainManager - accountManager *accounts.AccountManager + accountManager *accounts.Manager state *State whisper *Whisper miner *miner.Miner From 6684ef201a1a133aafdec6b24564533756de3cd4 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 8 Mar 2015 02:45:02 +0100 Subject: [PATCH 26/82] accounts: don't store address in unlocked and add commentary This was suggested during review. --- accounts/account_manager.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 97cf7c878b..bb664878ac 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -60,10 +60,8 @@ type Manager struct { } type unlocked struct { - addr []byte - abort chan struct{} - *crypto.Key + abort chan struct{} } func NewManager(keyStore crypto.KeyStore2, unlockTime time.Duration) *Manager { @@ -119,7 +117,7 @@ func (am *Manager) SignLocked(a Account, keyAuth string, toSign []byte) (signatu return nil, err } u := am.addUnlocked(a.Address, key) - go am.dropLater(u) + go am.dropLater(a.Address, u) signature, err = crypto.Sign(toSign, key.PrivateKey) return signature, err } @@ -149,7 +147,7 @@ func (am *Manager) Accounts() ([]Account, error) { } func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { - u := &unlocked{addr: addr, abort: make(chan struct{}), Key: key} + u := &unlocked{Key: key, abort: make(chan struct{})} am.mutex.Lock() prev, found := am.unlocked[string(addr)] if found { @@ -162,7 +160,7 @@ func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { return u } -func (am *Manager) dropLater(u *unlocked) { +func (am *Manager) dropLater(addr []byte, u *unlocked) { t := time.NewTimer(am.unlockTime) defer t.Stop() select { @@ -170,9 +168,13 @@ func (am *Manager) dropLater(u *unlocked) { // just quit case <-t.C: am.mutex.Lock() - if am.unlocked[string(u.addr)] == u { + // only drop if it's still the same key instance that dropLater + // was launched with. we can check that using pointer equality + // because the map stores a new pointer every time the key is + // unlocked. + if am.unlocked[string(addr)] == u { zeroKey(u.PrivateKey) - delete(am.unlocked, string(u.addr)) + delete(am.unlocked, string(addr)) } am.mutex.Unlock() } From fb23a0cfe06b52919107879e646e7d9b066acb0e Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 9 Mar 2015 10:55:18 -0500 Subject: [PATCH 27/82] Remove support for compiling serpent --- rpc/api.go | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index ded91def16..64e9ed2850 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -339,20 +339,11 @@ func (p *EthereumApi) GetData(args *GetDataArgs, reply *interface{}) error { } func (p *EthereumApi) GetCompilers(reply *interface{}) error { - c := []string{"serpent"} + c := []string{""} *reply = c return nil } -func (p *EthereumApi) CompileSerpent(args *CompileArgs, reply *interface{}) error { - res, err := ethutil.Compile(args.Source, false) - if err != nil { - return err - } - *reply = res - return nil -} - func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error { if err := args.requirements(); err != nil { return err @@ -528,13 +519,8 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.GetCompilers(reply) case "eth_compileSolidity": case "eth_compileLLL": - return errNotImplemented case "eth_compileSerpent": - args := new(CompileArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - return p.CompileSerpent(args, reply) + return errNotImplemented case "eth_newFilter": args := new(FilterOptions) if err := json.Unmarshal(req.Params, &args); err != nil { From 9723191b19f6ddc12f0c3376ede7529b2d72e6a2 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 9 Mar 2015 11:04:38 -0500 Subject: [PATCH 28/82] Undo client_version changes --- cmd/ethereum/main.go | 3 ++- cmd/mist/main.go | 4 ++-- eth/backend.go | 5 +---- ethutil/common.go | 4 ---- p2p/server.go | 10 +++++++++- rpc/api.go | 2 -- xeth/xeth.go | 5 ----- 7 files changed, 14 insertions(+), 19 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index afb90d72a3..f79f948d1e 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/state" ) @@ -61,7 +62,7 @@ func main() { utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") ethereum, err := eth.New(ð.Config{ - Name: ethutil.MakeVersion(ClientIdentifier, Version), + Name: p2p.MakeName(ClientIdentifier, Version), KeyStore: KeyStore, DataDir: Datadir, LogFile: LogFile, diff --git a/cmd/mist/main.go b/cmd/mist/main.go index 0ece20d2cc..c9a07bfde7 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -28,8 +28,8 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/ui/qt/webengine" "github.com/obscuren/qml" ) @@ -52,7 +52,7 @@ func run() error { config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") ethereum, err := eth.New(ð.Config{ - Name: ethutil.MakeVersion(ClientIdentifier, Version), + Name: p2p.MakeName(ClientIdentifier, Version), KeyStore: KeyStore, DataDir: Datadir, LogFile: LogFile, diff --git a/eth/backend.go b/eth/backend.go index 27d3f3b3f9..f67f9c78b0 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -132,8 +132,7 @@ type Ethereum struct { logger ethlogger.LogSystem - Mining bool - version string + Mining bool } func New(config *Config) (*Ethereum, error) { @@ -176,7 +175,6 @@ func New(config *Config) (*Ethereum, error) { blacklist: p2p.NewBlacklist(), eventMux: &event.TypeMux{}, logger: logger, - version: config.Name, } eth.chainManager = core.NewChainManager(db, eth.EventMux()) @@ -233,7 +231,6 @@ func (s *Ethereum) PeerCount() int { return s.net.PeerCoun func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } func (s *Ethereum) Coinbase() []byte { return nil } // TODO -func (s *Ethereum) ClientVersion() string { return s.version } // Start the ethereum func (s *Ethereum) Start() error { diff --git a/ethutil/common.go b/ethutil/common.go index 88f4cd1899..9b66763b81 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -13,10 +13,6 @@ import ( "github.com/kardianos/osext" ) -func MakeVersion(name, version string) string { - return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) -} - func DefaultAssetPath() string { var assetPath string pwd, _ := os.Getwd() diff --git a/p2p/server.go b/p2p/server.go index 25da04cd96..3ea2538d1c 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net" + "runtime" "sync" "time" @@ -23,6 +24,13 @@ const ( var srvlog = logger.NewLogger("P2P Server") var srvjslog = logger.NewJsonLogger() +// MakeName creates a node name that follows the ethereum convention +// for such names. It adds the operation system name and Go runtime version +// the name. +func MakeName(name, version string) string { + return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) +} + // Server manages all peer connections. // // The fields of Server are used as configuration parameters. @@ -37,7 +45,7 @@ type Server struct { MaxPeers int // Name sets the node name of this server. - // Use ethutil.MakeVersion to create a name that follows existing conventions. + // Use MakeName to create a name that follows existing conventions. Name string // Bootstrap nodes are used to establish connectivity diff --git a/rpc/api.go b/rpc/api.go index 64e9ed2850..ddba422f84 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -609,8 +609,6 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.WhisperMessages(args.Id, reply) - case "client_version": - *reply = p.eth.GetClientVersion() // case "eth_register": // args, err := req.ToRegisterArgs() // if err != nil { diff --git a/xeth/xeth.go b/xeth/xeth.go index b78a45f881..091743a957 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -36,7 +36,6 @@ type Backend interface { EventMux() *event.TypeMux Whisper() *whisper.Whisper Miner() *miner.Miner - ClientVersion() string } type XEth struct { @@ -165,10 +164,6 @@ func (self *XEth) IsContract(address string) bool { return len(self.State().SafeGet(address).Code()) > 0 } -func (self *XEth) GetClientVersion() string { - return self.eth.ClientVersion() -} - func (self *XEth) SecretToAddress(key string) string { pair, err := crypto.NewKeyPairFromSec(fromHex(key)) if err != nil { From 27c42ea93488790d1c509c3ee6f187a1edab7e0a Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Fri, 6 Mar 2015 03:25:57 +0100 Subject: [PATCH 29/82] Add empty total difficulty to test blocks and clean up stopping the node --- cmd/blocktest/main.go | 14 +++++++++++--- cmd/utils/cmd.go | 9 +++++++++ eth/backend.go | 11 +++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/cmd/blocktest/main.go b/cmd/blocktest/main.go index 4a05b8bee8..579aa850ad 100644 --- a/cmd/blocktest/main.go +++ b/cmd/blocktest/main.go @@ -168,7 +168,7 @@ func main() { logger.Flush() }() - utils.HandleInterrupt() + //utils.HandleInterrupt() utils.InitConfig(VmType, ConfigFile, Datadir, "ethblocktest") @@ -190,12 +190,17 @@ func main() { MinerThreads: MinerThreads, }) + utils.StartEthereumForTest(ethereum) utils.StartRpc(ethereum, RpcListenAddress, RpcPort) - utils.StartEthereum(ethereum) ethereum.ChainManager().ResetWithGenesisBlock(blocks[0]) + // bph := ethereum.ChainManager().GetBlock(blocks[1].Header().ParentHash) + // fmt.Println("bph: ", bph) - // fmt.Println("HURR: ", hex.EncodeToString(ethutil.Encode(blocks[0].RlpData()))) + //fmt.Println("b0: ", hex.EncodeToString(ethutil.Encode(blocks[0].RlpData()))) + //fmt.Println("b0: ", hex.EncodeToString(blocks[0].Hash())) + //fmt.Println("b1: ", hex.EncodeToString(ethutil.Encode(blocks[1].RlpData()))) + //fmt.Println("b1: ", hex.EncodeToString(blocks[1].Hash())) go ethereum.ChainManager().InsertChain(types.Blocks{blocks[1]}) fmt.Println("OK! ") @@ -254,6 +259,9 @@ func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) { } gb := types.NewBlockWithHeader(gbh) + //gb.uncles = *new([]*types.Header) + //gb.transactions = *new(types.Transactions) + gb.Td = new(big.Int) gb.Reward = new(big.Int) testBlock := new(types.Block) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index a77c6ad4db..3823ec75af 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -134,6 +134,15 @@ func StartEthereum(ethereum *eth.Ethereum) { }) } +func StartEthereumForTest(ethereum *eth.Ethereum) { + clilogger.Infoln("Starting ", ethereum.Name()) + ethereum.StartForTest() + RegisterInterrupt(func(sig os.Signal) { + ethereum.Stop() + logger.Flush() + }) +} + func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) { var err error switch { diff --git a/eth/backend.go b/eth/backend.go index 584d60c7e8..f42ceda693 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -275,6 +275,17 @@ func (s *Ethereum) Start() error { return nil } +func (s *Ethereum) StartForTest() { + jsonlogger.LogJson(&logger.LogStarting{ + ClientString: s.net.Name, + ProtocolVersion: ProtocolVersion, + }) + + // Start services + s.txPool.Start() + s.blockPool.Start() +} + func (self *Ethereum) SuggestPeer(nodeURL string) error { n, err := discover.ParseNode(nodeURL) if err != nil { From a7538d0020d3a51ab3b25997b3c4f01db87d4c7a Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 9 Mar 2015 18:19:35 +0100 Subject: [PATCH 30/82] merge --- rpc/api.go | 16 +++++++++++++--- rpc/args.go | 17 ++--------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 617e4998d2..dc0945d198 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -665,10 +665,20 @@ func toFilterOptions(options *FilterOptions) core.FilterOptions { opts.Earliest = options.Earliest opts.Latest = options.Latest - opts.Topics = make([][][]byte, len(options.Topic)) - for i, topic := range options.Topic { - opts.Topics[i] = fromHex(topic) + + topics := make([][][]byte, len(options.Topics)) + for i, topicDat := range options.Topics { + if slice, ok := topicDat.([]interface{}); ok { + topics[i] = make([][]byte, len(slice)) + for j, topic := range slice { + topics[i][j] = fromHex(topic.(string)) + } + } else if str, ok := topicDat.(string); ok { + topics[i] = make([][]byte, 1) + topics[i][0] = fromHex(str) + } } + opts.Topics = topics return opts } diff --git a/rpc/args.go b/rpc/args.go index d34cfb2fa8..63969e598b 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -333,7 +333,7 @@ type FilterOptions struct { Earliest int64 Latest int64 Address interface{} - Topic []interface{} + Topics []interface{} Skip int Max int } @@ -360,20 +360,7 @@ func (args *FilterOptions) UnmarshalJSON(b []byte) (err error) { args.Max = int(ethutil.Big(obj[0].Limit).Int64()) args.Skip = int(ethutil.Big(obj[0].Offset).Int64()) args.Address = obj[0].Address - - topics := make([][][]byte, len(obj[0].Topics)) - for i, topicDat := range obj[0].Topics { - if slice, ok := topicDat.([]interface{}); ok { - topics[i] = make([][]byte, len(slice)) - for j, topic := range slice { - topics[i][j] = fromHex(topic.(string)) - } - } else if str, ok := topicDat.(string); ok { - topics[i] = make([][]byte, 1) - topics[i][0] = fromHex(str) - } - } - args.Topics = topics + args.Topics = obj[0].Topics return nil } From 2dacb51fb02816c7591bfdbaaf7a160e122e5590 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 16:58:06 +0100 Subject: [PATCH 31/82] cmd/blocktest: simplify to fix build block tests only need the chain, not all of ethereum. --- cmd/blocktest/flags.go | 41 --------- cmd/blocktest/main.go | 184 ++++++++--------------------------------- 2 files changed, 35 insertions(+), 190 deletions(-) delete mode 100644 cmd/blocktest/flags.go diff --git a/cmd/blocktest/flags.go b/cmd/blocktest/flags.go deleted file mode 100644 index c811e5b857..0000000000 --- a/cmd/blocktest/flags.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - This file is part of go-ethereum - - go-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - go-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with go-ethereum. If not, see . -*/ -/** - * @authors - * Gustav Simonsson - */ -package main - -import ( - "flag" - "fmt" - "os" -) - -var ( - TestFile string -) - -func Init() { - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "%s \n", os.Args[0]) - flag.PrintDefaults() - } - flag.Parse() - - TestFile = flag.Arg(0) -} diff --git a/cmd/blocktest/main.go b/cmd/blocktest/main.go index 4a05b8bee8..b96f427101 100644 --- a/cmd/blocktest/main.go +++ b/cmd/blocktest/main.go @@ -25,34 +25,26 @@ package main import ( "bytes" - "crypto/ecdsa" "encoding/hex" "encoding/json" + "flag" "fmt" "io/ioutil" "log" "math/big" - "path" + "os" "runtime" - "strconv" "strings" - "time" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/core" types "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/rlp" ) -const ( - ClientIdentifier = "Ethereum(G)" - Version = "0.8.6" -) - type Account struct { Balance string Code string @@ -78,6 +70,7 @@ type BlockHeader struct { TransactionsTrie string UncleHash string } + type Tx struct { Data string GasLimit string @@ -103,103 +96,40 @@ type Test struct { Pre map[string]Account } -var ( - 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 - MinerThreads int -) - -// flags specific to cli client -var ( - StartMining bool - StartJsConsole bool - InputFile string -) - func main() { - init_vars() - - Init() - - if len(TestFile) < 1 { - log.Fatal("Please specify test file") - } - blocks, err := loadBlocksFromTestFile(TestFile) - if err != nil { - panic(err) + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "%s \n", os.Args[0]) + flag.PrintDefaults() } + flag.Parse() runtime.GOMAXPROCS(runtime.NumCPU()) + logger.AddLogSystem(logger.NewStdLogSystem(os.Stderr, log.LstdFlags, logger.DebugDetailLevel)) + defer func() { logger.Flush() }() - defer func() { - logger.Flush() - }() + if len(os.Args) < 2 { + utils.Fatalf("Please specify a test file as the first argument.") + } + blocks, err := loadBlocksFromTestFile(os.Args[1]) + if err != nil { + utils.Fatalf("Could not load blocks: %v", err) + } - utils.HandleInterrupt() + chain := memchain() + chain.ResetWithGenesisBlock(blocks[0]) + if err = chain.InsertChain(types.Blocks{blocks[1]}); err != nil { + utils.Fatalf("Error: %v", err) + } else { + fmt.Println("PASS") + } +} - utils.InitConfig(VmType, ConfigFile, Datadir, "ethblocktest") - - 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, - MinerThreads: MinerThreads, - }) - - utils.StartRpc(ethereum, RpcListenAddress, RpcPort) - utils.StartEthereum(ethereum) - - ethereum.ChainManager().ResetWithGenesisBlock(blocks[0]) - - // fmt.Println("HURR: ", hex.EncodeToString(ethutil.Encode(blocks[0].RlpData()))) - - go ethereum.ChainManager().InsertChain(types.Blocks{blocks[1]}) - fmt.Println("OK! ") - ethereum.WaitForShutdown() +func memchain() *core.ChainManager { + db, err := ethdb.NewMemDatabase() + if err != nil { + utils.Fatalf("Could not create in-memory database: %v", err) + } + return core.NewChainManager(db, new(event.TypeMux)) } func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) { @@ -207,9 +137,8 @@ func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) { if err != nil { return } - bt := *new(map[string]Test) - err = json.Unmarshal(fileContent, &bt) - if err != nil { + bt := make(map[string]Test) + if err = json.Unmarshal(fileContent, &bt); err != nil { return } @@ -272,49 +201,6 @@ func loadBlocksFromTestFile(filePath string) (blocks types.Blocks, err error) { return } -func init_vars() { - VmType = 0 - Identifier = "" - KeyRing = "" - KeyStore = "db" - RpcListenAddress = "127.0.0.1" - RpcPort = 8545 - WsPort = 40404 - StartRpc = true - StartWebSockets = false - NonInteractive = false - GenAddr = false - SecretFile = "" - ExportDir = "" - LogFile = "" - - timeStr := strconv.FormatInt(time.Now().UnixNano(), 10) - - Datadir = path.Join(ethutil.DefaultDataDir(), timeStr) - ConfigFile = path.Join(ethutil.DefaultDataDir(), timeStr, "conf.ini") - - DebugFile = "" - LogLevel = 5 - LogFormat = "std" - DiffTool = false - DiffType = "all" - ShowGenesis = false - ImportChain = "" - Dump = false - DumpHash = "" - DumpNumber = -1 - StartMining = false - StartJsConsole = false - PrintVersion = false - MinerThreads = runtime.NumCPU() - - Dial = false - OutboundPort = "30303" - BootNodes = "" - MaxPeer = 1 - -} - func hex_decode(s string) (res []byte, err error) { return hex.DecodeString(strings.TrimPrefix(s, "0x")) } From 2ee88a220ac22a814def1d568f80d7f29ede7095 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 18:05:56 +0100 Subject: [PATCH 32/82] core: remove Hash method from Message interface This will simplify the next commmit. Hash was only used for logging. --- core/state_transition.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 00e383f3f1..c487f8514d 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -3,6 +3,7 @@ package core import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/state" @@ -44,8 +45,6 @@ type StateTransition struct { } type Message interface { - Hash() []byte - From() []byte To() []byte @@ -152,7 +151,7 @@ func (self *StateTransition) preCheck() (err error) { } func (self *StateTransition) TransitionState() (ret []byte, err error) { - statelogger.Debugf("(~) %x\n", self.msg.Hash()) + // statelogger.Debugf("(~) %x\n", self.msg.Hash()) // XXX Transactions after this point are considered valid. if err = self.preCheck(); err != nil { From b4fa94c4b1459e71d4f11a3178cf56edf2b4aed3 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 18:04:40 +0100 Subject: [PATCH 33/82] xeth: don't sign transactions for tx call This should make calls faster and removes interaction with account manager. --- rpc/api.go | 2 +- xeth/xeth.go | 57 ++++++++++++++++++++++++++++------------------------ 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index b622945eb5..9c792dd61c 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -269,7 +269,7 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { } 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(args.From, args.To, args.Value, args.Gas, args.GasPrice, args.Data) if err != nil { return err } diff --git a/xeth/xeth.go b/xeth/xeth.go index afe680f348..956b8cd01d 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -7,6 +7,7 @@ package xeth import ( "bytes" "encoding/json" + "math/big" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core" @@ -243,7 +244,7 @@ func (self *XEth) PushTx(encodedTx string) (string, error) { return toHex(tx.Hash()), nil } -func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { +func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { if len(gasStr) == 0 { gasStr = "100000" } @@ -251,34 +252,20 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st gasPriceStr = "1" } - acct, err := self.accountManager.Default() - if err != nil { - return "", err + statedb := self.State().State() //self.chainManager.TransState() + msg := callmsg{ + from: statedb.GetOrNewStateObject(fromHex(fromStr)), + to: fromHex(toStr), + gas: ethutil.Big(gasStr), + gasPrice: ethutil.Big(gasPriceStr), + value: ethutil.Big(valueStr), + data: fromHex(dataStr), } - var ( - statedb = self.State().State() //self.chainManager.TransState() - from = statedb.GetOrNewStateObject(acct.Address) - block = self.chainManager.CurrentBlock() - to = statedb.GetOrNewStateObject(fromHex(toStr)) - data = fromHex(dataStr) - gas = ethutil.Big(gasStr) - price = ethutil.Big(gasPriceStr) - value = ethutil.Big(valueStr) - ) - - msg := types.NewTransactionMessage(fromHex(toStr), value, gas, price, data) - sig, err := self.accountManager.Sign(acct, msg.Hash()) - if err != nil { - return "", err - } - msg.SetSignatureValues(sig) + block := self.chainManager.CurrentBlock() vmenv := core.NewEnv(statedb, self.chainManager, msg, block) - res, err := vmenv.Call(from, to.Address(), data, gas, price, value) - if err != nil { - return "", err - } - return toHex(res), nil + res, err := vmenv.Call(msg.from, msg.to, msg.data, msg.gas, msg.gasPrice, msg.value) + return toHex(res), err } func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { @@ -334,3 +321,21 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt return toHex(tx.Hash()), nil } + +// callmsg is the message type used for call transations. +type callmsg struct { + from *state.StateObject + to []byte + gas, gasPrice *big.Int + value *big.Int + data []byte +} + +// accessor boilerplate to implement core.Message +func (m callmsg) From() []byte { return m.from.Address() } +func (m callmsg) Nonce() uint64 { return m.from.Nonce() } +func (m callmsg) To() []byte { return m.to } +func (m callmsg) GasPrice() *big.Int { return m.gasPrice } +func (m callmsg) Gas() *big.Int { return m.gas } +func (m callmsg) Value() *big.Int { return m.value } +func (m callmsg) Data() []byte { return m.data } From 73d1ebe244644c2d74a1c0c38a3b339e72140886 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 22:51:50 +0100 Subject: [PATCH 34/82] cmd/utils: add NewApp --- cmd/ethereum/main.go | 6 +----- cmd/utils/flags.go | 11 +++++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index f12616e179..f5215c587d 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -45,12 +45,10 @@ const ( var ( clilogger = logger.NewLogger("CLI") - app = cli.NewApp() + app = utils.NewApp(Version, "the go-ethereum command line interface") ) func init() { - app.Version = Version - app.Usage = "the go-ethereum command-line client" app.Action = run app.HideVersion = true // we have a command to print the version app.Commands = []cli.Command{ @@ -107,8 +105,6 @@ runtime will execute the file and exit. Usage: `import a blockchain file`, }, } - app.Author = "" - app.Email = "" app.Flags = []cli.Flag{ utils.BootnodesFlag, utils.DataDirFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e6eee20a7d..2156963c4a 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -19,6 +19,17 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" ) +// NewApp creates an app with sane defaults. +func NewApp(version, usage string) *cli.App { + app := cli.NewApp() + app.Name = path.Base(os.Args[0]) + app.Author = "" + app.Email = "" + app.Version = version + app.Usage = usage + return app +} + // These are all the command line flags we support. // If you add to this list, please remember to include the // flag in the appropriate command definition. From a11f1d6a7ec2eaa1a348776072c49019368a5ef3 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 23:00:27 +0100 Subject: [PATCH 35/82] rpc: add dataDir parameter and JSON-RPC handler --- cmd/ethereum/main.go | 4 +--- cmd/utils/cmd.go | 12 ------------ cmd/utils/flags.go | 18 ++++++++++++++++++ rpc/api.go | 5 +++-- rpc/http.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 rpc/http.go diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index f5215c587d..4855a3e4af 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -170,9 +170,7 @@ func runjs(ctx *cli.Context) { func startEth(ctx *cli.Context, eth *eth.Ethereum) { utils.StartEthereum(eth) if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { - addr := ctx.GlobalString(utils.RPCListenAddrFlag.Name) - port := ctx.GlobalInt(utils.RPCPortFlag.Name) - utils.StartRpc(eth, addr, port) + utils.StartRPC(eth, ctx) } if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { eth.Miner().Start() diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index 3c3d3955dd..79ae7888ae 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -34,9 +34,7 @@ import ( "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/rlp" - rpchttp "github.com/ethereum/go-ethereum/rpc/http" "github.com/ethereum/go-ethereum/state" - "github.com/ethereum/go-ethereum/xeth" ) var clilogger = logger.NewLogger("CLI") @@ -165,16 +163,6 @@ func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, Secre clilogger.Infof("Main address %x\n", keyManager.Address()) } -func StartRpc(ethereum *eth.Ethereum, RpcListenAddress string, RpcPort int) { - var err error - 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 { - go ethereum.RpcServer.Start() - } -} - func FormatTransactionData(data string) []byte { d := ethutil.StringToByteFunc(data, func(s string) (ret []byte) { slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2156963c4a..2995ebad83 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2,6 +2,10 @@ package utils import ( "crypto/ecdsa" + "fmt" + "net" + "net/http" + "os" "path" "runtime" "time" @@ -17,6 +21,8 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/xeth" ) // NewApp creates an app with sane defaults. @@ -183,3 +189,15 @@ func GetAccountManager(ctx *cli.Context) *accounts.Manager { ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) return accounts.NewManager(ks, 300*time.Second) } + +func StartRPC(eth *eth.Ethereum, ctx *cli.Context) { + addr := ctx.GlobalString(RPCListenAddrFlag.Name) + port := ctx.GlobalInt(RPCPortFlag.Name) + dataDir := ctx.GlobalString(DataDirFlag.Name) + + l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port)) + if err != nil { + Fatalf("Can't listen on %s:%d: %v", addr, port, err) + } + go http.Serve(l, rpc.JSONRPC(xeth.New(eth), dataDir)) +} diff --git a/rpc/api.go b/rpc/api.go index 9c792dd61c..c3aa7186b7 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -11,6 +11,7 @@ package rpc import ( "fmt" "math/big" + "path" "strings" "sync" "time" @@ -55,8 +56,8 @@ type EthereumApi struct { defaultBlockAge int64 } -func NewEthereumApi(eth *xeth.XEth) *EthereumApi { - db, _ := ethdb.NewLDBDatabase("dapps") +func NewEthereumApi(eth *xeth.XEth, dataDir string) *EthereumApi { + db, _ := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps")) api := &EthereumApi{ eth: eth, mux: eth.Backend().EventMux(), diff --git a/rpc/http.go b/rpc/http.go new file mode 100644 index 0000000000..44e2ad6ab3 --- /dev/null +++ b/rpc/http.go @@ -0,0 +1,42 @@ +package rpc + +import ( + "net/http" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/xeth" +) + +var rpchttplogger = logger.NewLogger("RPC-HTTP") + +// JSONRPC returns a handler that implements the Ethereum JSON-RPC API. +func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler { + var json JsonWrapper + const jsonrpcver = "2.0" + api := NewEthereumApi(pipe, dataDir) + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + + rpchttplogger.DebugDetailln("Handling request") + + reqParsed, reqerr := json.ParseRequestBody(req) + if reqerr != nil { + jsonerr := &RpcErrorObject{-32700, "Error: Could not parse request"} + json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) + return + } + + var response interface{} + reserr := api.GetRequestReply(&reqParsed, &response) + if reserr != nil { + rpchttplogger.Warnln(reserr) + jsonerr := &RpcErrorObject{-32603, reserr.Error()} + json.Send(w, &RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) + return + } + + rpchttplogger.DebugDetailf("Generated response: %T %s", response, response) + json.Send(w, &RpcSuccessResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Result: response}) + }) +} From 6a7e02fc9f7c7a92181ecdf3ce70dbdad3cb000e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 23:01:26 +0100 Subject: [PATCH 36/82] rpc/http: delete package --- rpc/http/server.go | 116 --------------------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 rpc/http/server.go diff --git a/rpc/http/server.go b/rpc/http/server.go deleted file mode 100644 index 452b7c9af0..0000000000 --- a/rpc/http/server.go +++ /dev/null @@ -1,116 +0,0 @@ -/* - This file is part of go-ethereum - - go-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - go-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with go-ethereum. If not, see . -*/ -package rpchttp - -import ( - "fmt" - "net" - "net/http" - - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/xeth" -) - -var rpchttplogger = logger.NewLogger("RPC-HTTP") -var JSON rpc.JsonWrapper - -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 - } - - return &RpcHttpServer{ - listener: l, - quit: make(chan bool), - pipe: pipe, - port: port, - addr: address, - }, nil -} - -type RpcHttpServer struct { - quit chan bool - listener net.Listener - pipe *xeth.XEth - port int - addr string -} - -func (s *RpcHttpServer) exitHandler() { -out: - for { - select { - case <-s.quit: - s.listener.Close() - break out - } - } - - rpchttplogger.Infoln("Shutdown RPC-HTTP server") -} - -func (s *RpcHttpServer) Stop() { - close(s.quit) -} - -func (s *RpcHttpServer) Start() { - rpchttplogger.Infof("Starting RPC-HTTP server on %s:%d", s.addr, s.port) - go s.exitHandler() - - api := rpc.NewEthereumApi(s.pipe) - h := s.apiHandler(api) - http.Handle("/", h) - - err := http.Serve(s.listener, nil) - // FIX Complains on shutdown due to listner already being closed - if err != nil { - rpchttplogger.Errorln("Error on RPC-HTTP interface:", err) - } -} - -func (s *RpcHttpServer) apiHandler(api *rpc.EthereumApi) http.Handler { - var jsonrpcver string = "2.0" - fn := func(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - - rpchttplogger.DebugDetailln("Handling request") - - reqParsed, reqerr := JSON.ParseRequestBody(req) - if reqerr != nil { - jsonerr := &rpc.RpcErrorObject{-32700, "Error: Could not parse request"} - JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) - return - } - - var response interface{} - reserr := api.GetRequestReply(&reqParsed, &response) - if reserr != nil { - rpchttplogger.Warnln(reserr) - jsonerr := &rpc.RpcErrorObject{-32603, reserr.Error()} - JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Error: jsonerr}) - return - } - - rpchttplogger.DebugDetailf("Generated response: %T %s", response, response) - JSON.Send(w, &rpc.RpcSuccessResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Result: response}) - } - - return http.HandlerFunc(fn) -} From 63758db37977bf4a2bd13cd360432c520dd77dd8 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 23:01:36 +0100 Subject: [PATCH 37/82] eth: delete unused RpcServer field --- eth/backend.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/eth/backend.go b/eth/backend.go index 680cc175a3..28ed0bbe80 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -20,7 +20,6 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" - "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/whisper" ) @@ -124,8 +123,6 @@ type Ethereum struct { blockSub event.Subscription miner *miner.Miner - RpcServer rpc.RpcServer - logger logger.LogSystem Mining bool @@ -268,10 +265,6 @@ func (s *Ethereum) Stop() { s.txSub.Unsubscribe() // quits txBroadcastLoop s.blockSub.Unsubscribe() // quits blockBroadcastLoop - if s.RpcServer != nil { - s.RpcServer.Stop() - } - s.txPool.Stop() s.eventMux.Stop() s.blockPool.Stop() From c2e5dacf555d93cb96b8fbffa700a2a6fece8bf0 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 23:02:43 +0100 Subject: [PATCH 38/82] accounts: add Manager.HasAccount, delete Manager.Default --- accounts/account_manager.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index bb664878ac..4575334bff 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -33,6 +33,7 @@ and accounts persistence is derived from stored keys' addresses package accounts import ( + "bytes" "crypto/ecdsa" crand "crypto/rand" @@ -72,19 +73,22 @@ func NewManager(keyStore crypto.KeyStore2, unlockTime time.Duration) *Manager { } } +func (am *Manager) HasAccount(addr []byte) bool { + accounts, _ := am.Accounts() + for _, acct := range accounts { + if bytes.Compare(acct.Address, addr) == 0 { + return true + } + } + return false +} + // Coinbase returns the account address that mining rewards are sent to. func (am *Manager) Coinbase() (addr []byte, err error) { // TODO: persist coinbase address on disk return am.firstAddr() } -// MainAccount returns the primary account used for transactions. -func (am *Manager) Default() (Account, error) { - // TODO: persist main account address on disk - addr, err := am.firstAddr() - return Account{Address: addr}, err -} - func (am *Manager) firstAddr() ([]byte, error) { addrs, err := am.keyStore.GetKeyAddresses() if err != nil { @@ -135,9 +139,7 @@ func (am *Manager) Accounts() ([]Account, error) { if err != nil { return nil, err } - accounts := make([]Account, len(addresses)) - for i, addr := range addresses { accounts[i] = Account{ Address: addr, From 697f6748b8df6b50157f81eb7959d856d3eb688e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 23:03:20 +0100 Subject: [PATCH 39/82] cmd/mist: use cli library and package accounts --- cmd/mist/assets/qml/views/info.qml | 41 --------- cmd/mist/bindings.go | 29 ------- cmd/mist/debugger.go | 8 +- cmd/mist/flags.go | 130 ----------------------------- cmd/mist/gui.go | 53 +----------- cmd/mist/main.go | 101 +++++++++++----------- 6 files changed, 58 insertions(+), 304 deletions(-) delete mode 100644 cmd/mist/flags.go diff --git a/cmd/mist/assets/qml/views/info.qml b/cmd/mist/assets/qml/views/info.qml index b2d2f521c9..0187bba6d6 100644 --- a/cmd/mist/assets/qml/views/info.qml +++ b/cmd/mist/assets/qml/views/info.qml @@ -54,7 +54,6 @@ Rectangle { height: 200 anchors { left: parent.left - right: logLevelSlider.left bottom: parent.bottom top: parent.top } @@ -107,46 +106,6 @@ Rectangle { } } } - - /* - TableView { - id: logView - headerVisible: false - anchors { - right: logLevelSlider.left - left: parent.left - bottom: parent.bottom - top: parent.top - } - - TableViewColumn{ role: "description" ; title: "log" } - - model: logModel - } - */ - - Slider { - id: logLevelSlider - value: gui.getLogLevelInt() - anchors { - right: parent.right - top: parent.top - bottom: parent.bottom - - rightMargin: 5 - leftMargin: 5 - topMargin: 5 - bottomMargin: 5 - } - - orientation: Qt.Vertical - maximumValue: 5 - stepSize: 1 - - onValueChanged: { - gui.setLogLevel(value) - } - } } property var logModel: ListModel { diff --git a/cmd/mist/bindings.go b/cmd/mist/bindings.go index fd89eb7e2e..b473cc985c 100644 --- a/cmd/mist/bindings.go +++ b/cmd/mist/bindings.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/state" ) @@ -37,18 +36,6 @@ type plugin struct { Path string `json:"path"` } -// LogPrint writes to the GUI log. -func (gui *Gui) LogPrint(level logger.LogLevel, msg string) { - /* - str := strings.TrimRight(s, "\n") - lines := strings.Split(str, "\n") - - view := gui.getObjectByName("infoView") - for _, line := range lines { - view.Call("addLog", line) - } - */ -} func (gui *Gui) Transact(from, recipient, value, gas, gasPrice, d string) (string, error) { var data string if len(recipient) == 0 { @@ -64,17 +51,6 @@ func (gui *Gui) Transact(from, recipient, value, gas, gasPrice, d string) (strin return gui.xeth.Transact(from, recipient, value, gas, gasPrice, data) } -// functions that allow Gui to implement interface guilogger.LogSystem -func (gui *Gui) SetLogLevel(level logger.LogLevel) { - gui.logLevel = level - gui.eth.Logger().SetLogLevel(level) - gui.config.Save("loglevel", level) -} - -func (gui *Gui) GetLogLevel() logger.LogLevel { - return gui.logLevel -} - func (self *Gui) AddPlugin(pluginPath string) { self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath} @@ -89,11 +65,6 @@ func (self *Gui) RemovePlugin(pluginPath string) { ethutil.WriteFile(self.eth.DataDir+"/plugins.json", json) } -// this extra function needed to give int typecast value to gui widget -// that sets initial loglevel to default -func (gui *Gui) GetLogLevelInt() int { - return int(gui.logLevel) -} func (self *Gui) DumpState(hash, path string) { var stateDump []byte diff --git a/cmd/mist/debugger.go b/cmd/mist/debugger.go index c1ab2f3f18..bd8ddde375 100644 --- a/cmd/mist/debugger.go +++ b/cmd/mist/debugger.go @@ -137,16 +137,18 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data return } + // TODO: improve this + allAccounts, _ := self.lib.eth.AccountManager().Accounts() + var ( gas = ethutil.Big(gasStr) gasPrice = ethutil.Big(gasPriceStr) value = ethutil.Big(valueStr) - // Contract addr as test address - keyPair = self.lib.eth.KeyManager().KeyPair() + acc = allAccounts[0] ) statedb := self.lib.eth.ChainManager().TransState() - account := self.lib.eth.ChainManager().TransState().GetAccount(keyPair.Address()) + account := self.lib.eth.ChainManager().TransState().GetAccount(acc.Address) contract := statedb.NewStateObject([]byte{0}) contract.SetCode(script) contract.SetBalance(value) diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go deleted file mode 100644 index 139af5923b..0000000000 --- a/cmd/mist/flags.go +++ /dev/null @@ -1,130 +0,0 @@ -/* - This file is part of go-ethereum - - go-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - go-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with go-ethereum. If not, see . -*/ -/** - * @authors - * Jeffrey Wilcke - */ -package main - -import ( - "crypto/ecdsa" - "flag" - "fmt" - "log" - "os" - "path" - "runtime" - - "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" -) - -var ( - Identifier string - KeyRing string - KeyStore string - StartRpc bool - RpcListenAddress string - RpcPort 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 -var AssetPath string -var defaultConfigFile = path.Join(ethutil.DefaultDataDir(), "conf.ini") - -func Init() { - // TODO: move common flag processing to cmd/utils - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "%s [options] [filename]:\noptions precedence: default < config file < environment variables < command line\n", os.Args[0]) - flag.PrintDefaults() - } - - flag.IntVar(&VmType, "vm", 0, "Virtual Machine type: 0-1: standard, debug") - 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") - 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.BoolVar(&StartRpc, "rpc", true, "start rpc server") - flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)") - flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") - 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", 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)") - - flag.StringVar(&AssetPath, "asset_path", ethutil.DefaultAssetPath(), "absolute path to GUI assets directory") - - // Network stuff - var ( - nodeKeyFile = flag.String("nodekey", "", "network private key file") - nodeKeyHex = flag.String("nodekeyhex", "", "network private key (for testing)") - natstr = flag.String("nat", "any", "port mapping mechanism (any|none|upnp|pmp|extip:)") - ) - flag.StringVar(&OutboundPort, "port", "30303", "listening port") - flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap") - flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers") - - flag.IntVar(&MinerThreads, "minerthreads", runtime.NumCPU(), "number of miner threads") - - flag.Parse() - - var err error - if NAT, err = nat.Parse(*natstr); err != nil { - log.Fatalf("-nat: %v", err) - } - switch { - case *nodeKeyFile != "" && *nodeKeyHex != "": - log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive") - case *nodeKeyFile != "": - if NodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil { - log.Fatalf("-nodekey: %v", err) - } - case *nodeKeyHex != "": - if NodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil { - log.Fatalf("-nodekeyhex: %v", err) - } - } - - if VmType >= int(vm.MaxVmTy) { - log.Fatal("Invalid VM type ", VmType) - } -} diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go index bc6e9ed538..e21d6fcea4 100644 --- a/cmd/mist/gui.go +++ b/cmd/mist/gui.go @@ -23,7 +23,6 @@ package main import "C" import ( - "bytes" "encoding/json" "fmt" "io/ioutil" @@ -70,20 +69,18 @@ type Gui struct { txDb *ethdb.LDBDatabase - logLevel logger.LogLevel - open bool + open bool xeth *xeth.XEth Session string - config *ethutil.ConfigManager plugins map[string]plugin } // Create GUI, but doesn't start it -func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, session string, logLevel int) *Gui { - db, err := ethdb.NewLDBDatabase("tx_database") +func NewWindow(ethereum *eth.Ethereum) *Gui { + db, err := ethdb.NewLDBDatabase(path.Join(ethereum.DataDir, "tx_database")) if err != nil { panic(err) } @@ -92,10 +89,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, session st gui := &Gui{eth: ethereum, txDb: db, xeth: xeth, - logLevel: logger.LogLevel(logLevel), - Session: session, open: false, - config: config, plugins: make(map[string]plugin), serviceEvents: make(chan ServEv, 1), } @@ -142,18 +136,12 @@ func (gui *Gui) Start(assetPath string) { gui.open = true win.Show() - // only add the gui guilogger after window is shown otherwise slider wont be shown - logger.AddLogSystem(gui) win.Wait() - - // need to silence gui guilogger after window closed otherwise logsystem hangs (but do not save loglevel) - gui.logLevel = logger.Silence gui.open = false } func (gui *Gui) Stop() { if gui.open { - gui.logLevel = logger.Silence gui.open = false gui.win.Hide() } @@ -172,9 +160,6 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { return gui.win, nil } -func (gui *Gui) ImportKey(filePath string) { -} - func (gui *Gui) GenerateKey() { _, err := gui.eth.AccountManager().NewAccount("hurr") if err != nil { @@ -198,31 +183,11 @@ func (gui *Gui) createWindow(comp qml.Object) *qml.Window { return gui.win } -func (gui *Gui) ImportAndSetPrivKey(secret string) bool { - err := gui.eth.KeyManager().InitFromString(gui.Session, 0, secret) - if err != nil { - guilogger.Errorln("unable to import: ", err) - return false - } - guilogger.Errorln("successfully imported: ", err) - return true -} - -func (gui *Gui) CreateAndSetPrivKey() (string, string, string, string) { - err := gui.eth.KeyManager().Init(gui.Session, 0, true) - if err != nil { - guilogger.Errorln("unable to create key: ", err) - return "", "", "", "" - } - return gui.eth.KeyManager().KeyPair().AsStrings() -} - func (gui *Gui) setInitialChain(ancientBlocks bool) { sBlk := gui.eth.ChainManager().LastBlockHash() blk := gui.eth.ChainManager().GetBlock(sBlk) for ; blk != nil; blk = gui.eth.ChainManager().GetBlock(sBlk) { sBlk = blk.ParentHash() - gui.processBlock(blk, true) } } @@ -266,10 +231,8 @@ func (self *Gui) loadMergedMiningOptions() { } func (gui *Gui) insertTransaction(window string, tx *types.Transaction) { - addr := gui.address() - var inout string - if bytes.Compare(tx.From(), addr) == 0 { + if gui.eth.AccountManager().HasAccount(tx.From()) { inout = "send" } else { inout = "recv" @@ -487,14 +450,6 @@ func (gui *Gui) setPeerInfo() { } } -func (gui *Gui) privateKey() string { - return ethutil.Bytes2Hex(gui.eth.KeyManager().PrivateKey()) -} - -func (gui *Gui) address() []byte { - return gui.eth.KeyManager().Address() -} - /* func LoadExtension(path string) (uintptr, error) { lib, err := ffi.NewLibrary(path) diff --git a/cmd/mist/main.go b/cmd/mist/main.go index 1d44038480..425630ece2 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -26,10 +26,10 @@ import ( "runtime" "time" + "github.com/codegangsta/cli" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/ui/qt/webengine" "github.com/obscuren/qml" ) @@ -39,56 +39,32 @@ const ( Version = "0.8.6" ) -var ethereum *eth.Ethereum -var mainlogger = logger.NewLogger("MAIN") - -func run() error { - webengine.Initialize() - - // precedence: code-internal flag default < config file < environment variables < command line - Init() // parsing command line - - tstart := time.Now() - config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") - - ethereum, err := eth.New(ð.Config{ - Name: p2p.MakeName(ClientIdentifier, Version), - KeyStore: KeyStore, - DataDir: Datadir, - LogFile: LogFile, - LogLevel: LogLevel, - MaxPeers: MaxPeer, - Port: OutboundPort, - NAT: NAT, - Shh: true, - BootNodes: BootNodes, - NodeKey: NodeKey, - KeyRing: KeyRing, - Dial: true, - MinerThreads: MinerThreads, - }) - if err != nil { - mainlogger.Fatalln(err) +var ( + app = utils.NewApp(Version, "the ether browser") + assetPathFlag = cli.StringFlag{ + Name: "asset_path", + Usage: "absolute path to GUI assets directory", + Value: ethutil.DefaultAssetPath(), } - utils.KeyTasks(ethereum.KeyManager(), KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive) +) - if StartRpc { - utils.StartRpc(ethereum, RpcListenAddress, RpcPort) +func init() { + app.Action = run + app.Flags = []cli.Flag{ + assetPathFlag, + + utils.BootnodesFlag, + utils.DataDirFlag, + utils.ListenPortFlag, + utils.LogFileFlag, + utils.LogLevelFlag, + utils.MaxPeersFlag, + utils.MinerThreadsFlag, + utils.NATFlag, + utils.NodeKeyFileFlag, + utils.RPCListenAddrFlag, + utils.RPCPortFlag, } - - gui := NewWindow(ethereum, config, KeyRing, LogLevel) - - utils.RegisterInterrupt(func(os.Signal) { - gui.Stop() - }) - go utils.StartEthereum(ethereum) - - fmt.Println("ETH stack took", time.Since(tstart)) - - // gui blocks the main thread - gui.Start(AssetPath) - - return nil } func main() { @@ -97,15 +73,16 @@ func main() { // This is a bit of a cheat, but ey! os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999") - qml.Run(run) - var interrupted = false utils.RegisterInterrupt(func(os.Signal) { interrupted = true }) - utils.HandleInterrupt() + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, "Error: ", err) + } + // we need to run the interrupt callbacks in case gui is closed // this skips if we got here by actual interrupt stopping the GUI if !interrupted { @@ -113,3 +90,23 @@ func main() { } logger.Flush() } + +func run(ctx *cli.Context) { + tstart := time.Now() + + // TODO: show qml popup instead of exiting if initialization fails. + ethereum := utils.GetEthereum(ClientIdentifier, Version, ctx) + utils.StartRPC(ethereum, ctx) + go utils.StartEthereum(ethereum) + fmt.Println("initializing eth stack took", time.Since(tstart)) + + // Open the window + qml.Run(func() error { + webengine.Initialize() + gui := NewWindow(ethereum) + utils.RegisterInterrupt(func(os.Signal) { gui.Stop() }) + // gui blocks the main thread + gui.Start(ctx.GlobalString(assetPathFlag.Name)) + return nil + }) +} From cd51860bf024d949a09b3863c88a278a386227a7 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 23:06:04 +0100 Subject: [PATCH 40/82] cmd/utils: delete InitConfig, KeyTasks --- cmd/utils/cmd.go | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index 79ae7888ae..271a879c7e 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/logger" @@ -96,14 +95,6 @@ func initDataDir(Datadir string) { } } -func InitConfig(vmType int, ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager { - initDataDir(Datadir) - cfg := ethutil.ReadConfig(ConfigFile, Datadir, EnvPrefix) - cfg.VmType = vmType - - return cfg -} - func exit(err error) { status := 0 if err != nil { @@ -132,37 +123,6 @@ func StartEthereum(ethereum *eth.Ethereum) { }) } -func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) { - var err error - switch { - case GenAddr: - if NonInteractive || confirm("This action overwrites your old private key.") { - err = keyManager.Init(KeyRing, 0, true) - } - exit(err) - case len(SecretFile) > 0: - SecretFile = ethutil.ExpandHomePath(SecretFile) - - if NonInteractive || confirm("This action overwrites your old private key.") { - err = keyManager.InitFromSecretsFile(KeyRing, 0, SecretFile) - } - exit(err) - case len(ExportDir) > 0: - err = keyManager.Init(KeyRing, 0, false) - if err == nil { - err = keyManager.Export(ExportDir) - } - exit(err) - default: - // Creates a keypair if none exists - err = keyManager.Init(KeyRing, 0, false) - if err != nil { - exit(err) - } - } - clilogger.Infof("Main address %x\n", keyManager.Address()) -} - func FormatTransactionData(data string) []byte { d := ethutil.StringToByteFunc(data, func(s string) (ret []byte) { slice := regexp.MustCompile("\\n|\\s").Split(s, 1000000000) From 487f68ec4892794cb994cffd95d5bc2bf3052d3e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 00:09:39 +0100 Subject: [PATCH 41/82] accounts: add {Timed,}Unlock, remove SignLocked --- accounts/account_manager.go | 48 ++++++++++++++++++---------- accounts/accounts_test.go | 64 +++++++++++++++---------------------- cmd/utils/flags.go | 3 +- 3 files changed, 57 insertions(+), 58 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 4575334bff..fdd7d83e9c 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -54,10 +54,9 @@ type Account struct { } type Manager struct { - keyStore crypto.KeyStore2 - unlocked map[string]*unlocked - unlockTime time.Duration - mutex sync.RWMutex + keyStore crypto.KeyStore2 + unlocked map[string]*unlocked + mutex sync.RWMutex } type unlocked struct { @@ -65,11 +64,10 @@ type unlocked struct { abort chan struct{} } -func NewManager(keyStore crypto.KeyStore2, unlockTime time.Duration) *Manager { +func NewManager(keyStore crypto.KeyStore2) *Manager { return &Manager{ - keyStore: keyStore, - unlocked: make(map[string]*unlocked), - unlockTime: unlockTime, + keyStore: keyStore, + unlocked: make(map[string]*unlocked), } } @@ -115,15 +113,28 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) return signature, err } -func (am *Manager) SignLocked(a Account, keyAuth string, toSign []byte) (signature []byte, err error) { - key, err := am.keyStore.GetKey(a.Address, keyAuth) +// TimedUnlock unlocks the account with the given address. +// When timeout has passed, the account will be locked again. +func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duration) error { + key, err := am.keyStore.GetKey(addr, keyAuth) if err != nil { - return nil, err + return err } - u := am.addUnlocked(a.Address, key) - go am.dropLater(a.Address, u) - signature, err = crypto.Sign(toSign, key.PrivateKey) - return signature, err + u := am.addUnlocked(addr, key) + go am.dropLater(addr, u, timeout) + return nil +} + +// Unlock unlocks the account with the given address. The account +// stays unlocked until the program exits or until a TimedUnlock +// timeout (started after the call to Unlock) expires. +func (am *Manager) Unlock(addr []byte, keyAuth string) error { + key, err := am.keyStore.GetKey(addr, keyAuth) + if err != nil { + return err + } + am.addUnlocked(addr, key) + return nil } func (am *Manager) NewAccount(auth string) (Account, error) { @@ -155,6 +166,9 @@ func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { if found { // terminate dropLater for this key to avoid unexpected drops. close(prev.abort) + // the key is zeroed here instead of in dropLater because + // there might not actually be a dropLater running for this + // key, i.e. when Unlock was used. zeroKey(prev.PrivateKey) } am.unlocked[string(addr)] = u @@ -162,8 +176,8 @@ func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { return u } -func (am *Manager) dropLater(addr []byte, u *unlocked) { - t := time.NewTimer(am.unlockTime) +func (am *Manager) dropLater(addr []byte, u *unlocked, timeout time.Duration) { + t := time.NewTimer(timeout) defer t.Stop() select { case <-u.abort: diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go index b90da2892e..427114cbde 100644 --- a/accounts/accounts_test.go +++ b/accounts/accounts_test.go @@ -1,44 +1,36 @@ package accounts import ( + "io/ioutil" + "os" "testing" - "time" "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(ethutil.DefaultDataDir() + "/testaccounts") - am := NewManager(ks, 100*time.Millisecond) +func TestSign(t *testing.T) { + dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain) + defer os.RemoveAll(dir) + + am := NewManager(ks) pass := "" // not used but required by API a1, err := am.NewAccount(pass) toSign := randentropy.GetEntropyCSPRNG(32) - _, err = am.SignLocked(a1, pass, toSign) + am.Unlock(a1.Address, "") + + _, err = am.Sign(a1, toSign) if err != nil { t.Fatal(err) } - - // Cleanup - time.Sleep(150 * time.Millisecond) // 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 := NewManager(ks, 200*time.Millisecond) +func TestTimedUnlock(t *testing.T) { + dir, ks := tmpKeyStore(t, crypto.NewKeyStorePassphrase) + defer os.RemoveAll(dir) + + am := NewManager(ks) pass := "foo" a1, err := am.NewAccount(pass) toSign := randentropy.GetEntropyCSPRNG(32) @@ -46,38 +38,32 @@ func TestAccountManagerLocking(t *testing.T) { // Signing without passphrase fails because account is locked _, err = am.Sign(a1, toSign) if err != ErrLocked { - t.Fatal(err) + t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err) } // Signing with passphrase works - _, err = am.SignLocked(a1, pass, toSign) - if err != nil { + if err = am.TimedUnlock(a1.Address, pass, 100*time.Millisecond); 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) + t.Fatal("Signing shouldn't return an error after unlocking, got ", err) } - // Signing without passphrase fails after automatic locking - time.Sleep(250 * time.Millisecond) - + // Signing fails again after automatic locking + time.Sleep(150 * time.Millisecond) _, err = am.Sign(a1, toSign) if err != ErrLocked { - t.Fatal(err) + t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) } +} - // Cleanup - accounts, err := am.Accounts() +func tmpKeyStore(t *testing.T, new func(string) crypto.KeyStore2) (string, crypto.KeyStore2) { + d, err := ioutil.TempDir("", "eth-keystore-test") if err != nil { t.Fatal(err) } - for _, account := range accounts { - err := am.DeleteAccount(account.Address, pass) - if err != nil { - t.Fatal(err) - } - } + return d, new(d) } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index ee7ea4c798..cde5fa0248 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -8,7 +8,6 @@ import ( "os" "path" "runtime" - "time" "github.com/codegangsta/cli" "github.com/ethereum/go-ethereum/accounts" @@ -199,7 +198,7 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database, ethutil.D func GetAccountManager(ctx *cli.Context) *accounts.Manager { dataDir := ctx.GlobalString(DataDirFlag.Name) ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) - return accounts.NewManager(ks, 300*time.Second) + return accounts.NewManager(ks) } func StartRPC(eth *eth.Ethereum, ctx *cli.Context) { From d1e04f7388ec29b009d646e20570393a5851f54d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 00:24:11 +0100 Subject: [PATCH 42/82] cmd/ethereum: allow multiple js files --- cmd/ethereum/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 1ae8e46a27..d0edef81da 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -159,13 +159,13 @@ func runjs(ctx *cli.Context) { startEth(ctx, eth) if len(ctx.Args()) == 0 { runREPL(eth) - eth.Stop() - eth.WaitForShutdown() - } else if len(ctx.Args()) == 1 { - execJsFile(eth, ctx.Args()[0]) } else { - utils.Fatalf("This command can handle at most one argument.") + for _, file := range ctx.Args() { + execJsFile(eth, file) + } } + eth.Stop() + eth.WaitForShutdown() } func startEth(ctx *cli.Context, eth *eth.Ethereum) { From 395da0e7c1accfaeda6527a473e9a31b11fe88a8 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:00:06 +0100 Subject: [PATCH 43/82] xeth: use Frontend interface to unlock accounts The interface has moved to package xeth because that's where it is actually used. --- ui/frontend.go | 8 ------ xeth/xeth.go | 78 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 31 deletions(-) delete mode 100644 ui/frontend.go diff --git a/ui/frontend.go b/ui/frontend.go deleted file mode 100644 index 413a242597..0000000000 --- a/ui/frontend.go +++ /dev/null @@ -1,8 +0,0 @@ -package ui - -import "github.com/ethereum/go-ethereum/core/types" - -type Interface interface { - UnlockAccount(address []byte) bool - ConfirmTransaction(tx *types.Transaction) bool -} diff --git a/xeth/xeth.go b/xeth/xeth.go index a0491506b9..f8b537321e 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -1,12 +1,10 @@ +// eXtended ETHereum package xeth -/* - * eXtended ETHereum - */ - import ( "bytes" "encoding/json" + "fmt" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -19,7 +17,6 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/state" - "github.com/ethereum/go-ethereum/ui" "github.com/ethereum/go-ethereum/whisper" ) @@ -41,6 +38,26 @@ type Backend interface { Miner() *miner.Miner } +// Frontend should be implemented by users of XEth. Its methods are +// called whenever XEth makes a decision that requires user input. +type Frontend interface { + // UnlockAccount is called when a transaction needs to be signed + // but the key corresponding to the transaction's sender is + // locked. + // + // It should unlock the account with the given address and return + // true if unlocking succeeded. + UnlockAccount(address []byte) bool + + // This is called for all transactions inititated through + // Transact. It should prompt the user to confirm the transaction + // and return true if the transaction was acknowledged. + // + // ConfirmTransaction is not used for Call transactions + // because they cannot change any state. + ConfirmTransaction(tx *types.Transaction) bool +} + type XEth struct { eth Backend blockProcessor *core.BlockProcessor @@ -50,15 +67,20 @@ type XEth struct { whisper *Whisper miner *miner.Miner - frontend ui.Interface + frontend Frontend } -type TmpFrontend struct{} +// dummyFrontend is a non-interactive frontend that allows all +// transactions but cannot not unlock any keys. +type dummyFrontend struct{} -func (TmpFrontend) UnlockAccount([]byte) bool { panic("UNLOCK ACCOUNT") } -func (TmpFrontend) ConfirmTransaction(*types.Transaction) bool { panic("CONFIRM TRANSACTION") } +func (dummyFrontend) UnlockAccount([]byte) bool { return false } +func (dummyFrontend) ConfirmTransaction(*types.Transaction) bool { return true } -func New(eth Backend, frontend ui.Interface) *XEth { +// New creates an XEth that uses the given frontend. +// If a nil Frontend is provided, a default frontend which +// confirms all transactions will be used. +func New(eth Backend, frontend Frontend) *XEth { xeth := &XEth{ eth: eth, blockProcessor: eth.BlockProcessor(), @@ -66,14 +88,12 @@ func New(eth Backend, frontend ui.Interface) *XEth { accountManager: eth.AccountManager(), whisper: NewWhisper(eth.Whisper()), miner: eth.Miner(), + frontend: frontend, } - if frontend == nil { - xeth.frontend = TmpFrontend{} + xeth.frontend = dummyFrontend{} } - xeth.state = NewState(xeth, xeth.chainManager.TransState()) - return xeth } @@ -283,7 +303,6 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st } func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { - var ( from []byte to []byte @@ -310,16 +329,12 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt state := self.chainManager.TransState() nonce := state.GetNonce(from) - tx.SetNonce(nonce) - sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash()) - if err != nil { + + if err := self.sign(tx, from, false); err != nil { return "", err } - tx.SetSignatureValues(sig) - - err = self.eth.TxPool().Add(tx) - if err != nil { + if err := self.eth.TxPool().Add(tx); err != nil { return "", err } state.SetNonce(from, nonce+1) @@ -332,10 +347,27 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt if types.IsContractAddr(to) { return toHex(core.AddressFromMessage(tx)), nil } - return toHex(tx.Hash()), nil } +func (self *XEth) sign(tx *types.Transaction, from []byte, didUnlock bool) error { + sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash()) + if err == accounts.ErrLocked { + if didUnlock { + return fmt.Errorf("sender account still locked after successful unlock") + } + if !self.frontend.UnlockAccount(from) { + return fmt.Errorf("could not unlock sender account") + } + // retry signing, the account should now be unlocked. + self.sign(tx, from, true) + } else if err != nil { + return err + } + tx.SetSignatureValues(sig) + return nil +} + // callmsg is the message type used for call transations. type callmsg struct { from *state.StateObject From 9f0e3bd286472f85ab2457fc19cd48cdf12df110 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:00:57 +0100 Subject: [PATCH 44/82] cmd/ethereum: unlock accounts on JS REPL --- cmd/ethereum/js.go | 212 ++++++++++++++++++++++--------------------- cmd/ethereum/main.go | 5 +- 2 files changed, 112 insertions(+), 105 deletions(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index e3165d3f5c..96e292733d 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -22,11 +22,9 @@ import ( "fmt" "io/ioutil" "os" - "os/signal" "path" "strings" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethutil" @@ -37,51 +35,117 @@ import ( "github.com/peterh/liner" ) -func execJsFile(ethereum *eth.Ethereum, filename string) { - file, err := os.Open(filename) - if err != nil { - utils.Fatalf("%v", err) - } - content, err := ioutil.ReadAll(file) - if err != nil { - utils.Fatalf("%v", err) - } - re := javascript.NewJSRE(xeth.New(ethereum, nil)) - if _, err := re.Run(string(content)); err != nil { - utils.Fatalf("Javascript Error: %v", err) - } +type prompter interface { + AppendHistory(string) + Prompt(p string) (string, error) + PasswordPrompt(p string) (string, error) } -type repl struct { +type dumbPrompter struct{ r *bufio.Reader } + +func (r dumbPrompter) Prompt(p string) (string, error) { + fmt.Print(p) + return r.r.ReadString('\n') +} + +func (r dumbPrompter) PasswordPrompt(p string) (string, error) { + fmt.Println("!! Unsupported terminal, password will echo.") + fmt.Print(p) + input, err := bufio.NewReader(os.Stdin).ReadString('\n') + fmt.Println() + return input, err +} + +func (r dumbPrompter) AppendHistory(string) {} + +type jsre struct { re *javascript.JSRE ethereum *eth.Ethereum xeth *xeth.XEth - prompt string - lr *liner.State + ps1 string + prompter } -func runREPL(ethereum *eth.Ethereum) { - xeth := xeth.New(ethereum, nil) - repl := &repl{ - re: javascript.NewJSRE(xeth), - xeth: xeth, - ethereum: ethereum, - prompt: "> ", - } - repl.initStdFuncs() +func newJSRE(ethereum *eth.Ethereum) *jsre { + js := &jsre{ethereum: ethereum, ps1: "> "} + js.xeth = xeth.New(ethereum, js) + js.re = javascript.NewJSRE(js.xeth) + js.initStdFuncs() + if !liner.TerminalSupported() { - repl.dumbRead() + js.prompter = dumbPrompter{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() - defer lr.Close() lr.SetCtrlCAborts(true) - repl.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) - repl.read(lr) - repl.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + defer lr.Close() + js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) + defer js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + js.prompter = lr + } + return js +} + +func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool { + p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx) + answer, _ := self.prompter.Prompt(p) + return strings.HasPrefix(strings.Trim(answer, " "), "y") +} + +func (self *jsre) UnlockAccount(addr []byte) bool { + fmt.Printf("Please unlock account %x.\n", addr) + pass, err := self.prompter.PasswordPrompt("Passphrase: ") + if err != nil { + return false + } + // TODO: allow retry + if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil { + fmt.Println("Unlocking failed: ", err) + return false + } else { + fmt.Println("Account is now unlocked for this session.") + return true } } -func (self *repl) withHistory(op func(*os.File)) { +func (self *jsre) exec(filename string) error { + file, err := os.Open(filename) + if err != nil { + return err + } + content, err := ioutil.ReadAll(file) + if err != nil { + return err + } + if _, err := self.re.Run(string(content)); err != nil { + return fmt.Errorf("Javascript Error: %v", err) + } + return nil +} + +func (self *jsre) interactive() { + for { + input, err := self.prompter.Prompt(self.ps1) + if err != nil { + return + } + if input == "" { + continue + } + str += input + "\n" + self.setIndent() + if indentCount <= 0 { + if input == "exit" { + return + } + hist := str[:len(str)-1] + self.prompter.AppendHistory(hist) + self.parseInput(str) + str = "" + } + } +} + +func (self *jsre) withHistory(op func(*os.File)) { hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) if err != nil { fmt.Printf("unable to open history file: %v\n", err) @@ -91,7 +155,7 @@ func (self *repl) withHistory(op func(*os.File)) { hist.Close() } -func (self *repl) parseInput(code string) { +func (self *jsre) parseInput(code string) { defer func() { if r := recover(); r != nil { fmt.Println("[native] error", r) @@ -108,79 +172,21 @@ func (self *repl) parseInput(code string) { var indentCount = 0 var str = "" -func (self *repl) setIndent() { +func (self *jsre) setIndent() { open := strings.Count(str, "{") open += strings.Count(str, "(") closed := strings.Count(str, "}") closed += strings.Count(str, ")") indentCount = open - closed if indentCount <= 0 { - self.prompt = "> " + self.ps1 = "> " } else { - self.prompt = strings.Join(make([]string, indentCount*2), "..") - self.prompt += " " + self.ps1 = strings.Join(make([]string, indentCount*2), "..") + self.ps1 += " " } } -func (self *repl) read(lr *liner.State) { - for { - input, err := lr.Prompt(self.prompt) - if err != nil { - return - } - if input == "" { - continue - } - str += input + "\n" - self.setIndent() - if indentCount <= 0 { - if input == "exit" { - return - } - hist := str[:len(str)-1] - lr.AppendHistory(hist) - self.parseInput(str) - str = "" - } - } -} - -func (self *repl) dumbRead() { - fmt.Println("Unsupported terminal, line editing will not work.") - - // process lines - readDone := make(chan struct{}) - go func() { - r := bufio.NewReader(os.Stdin) - loop: - for { - fmt.Print(self.prompt) - line, err := r.ReadString('\n') - switch { - case err != nil || line == "exit": - break loop - case line == "": - continue - default: - self.parseInput(line + "\n") - } - } - close(readDone) - }() - - // wait for Ctrl-C - sigc := make(chan os.Signal, 1) - signal.Notify(sigc, os.Interrupt, os.Kill) - defer signal.Stop(sigc) - - select { - case <-readDone: - case <-sigc: - os.Stdin.Close() // terminate read - } -} - -func (self *repl) printValue(v interface{}) { +func (self *jsre) printValue(v interface{}) { method, _ := self.re.Vm.Get("prettyPrint") v, err := self.re.Vm.ToValue(v) if err == nil { @@ -191,7 +197,7 @@ func (self *repl) printValue(v interface{}) { } } -func (self *repl) initStdFuncs() { +func (self *jsre) initStdFuncs() { t, _ := self.re.Vm.Get("eth") eth := t.Object() eth.Set("connect", self.connect) @@ -205,7 +211,7 @@ func (self *repl) initStdFuncs() { * The following methods are natively implemented javascript functions. */ -func (self *repl) dump(call otto.FunctionCall) otto.Value { +func (self *jsre) dump(call otto.FunctionCall) otto.Value { var block *types.Block if len(call.ArgumentList) > 0 { @@ -236,17 +242,17 @@ func (self *repl) dump(call otto.FunctionCall) otto.Value { return v } -func (self *repl) stopMining(call otto.FunctionCall) otto.Value { +func (self *jsre) stopMining(call otto.FunctionCall) otto.Value { self.xeth.Miner().Stop() return otto.TrueValue() } -func (self *repl) startMining(call otto.FunctionCall) otto.Value { +func (self *jsre) startMining(call otto.FunctionCall) otto.Value { self.xeth.Miner().Start() return otto.TrueValue() } -func (self *repl) connect(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() @@ -257,7 +263,7 @@ func (self *repl) connect(call otto.FunctionCall) otto.Value { return otto.TrueValue() } -func (self *repl) export(call otto.FunctionCall) otto.Value { +func (self *jsre) export(call otto.FunctionCall) otto.Value { if len(call.ArgumentList) == 0 { fmt.Println("err: require file name") return otto.FalseValue() diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index d0edef81da..1703c02bb9 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -157,11 +157,12 @@ func run(ctx *cli.Context) { func runjs(ctx *cli.Context) { eth := utils.GetEthereum(ClientIdentifier, Version, ctx) startEth(ctx, eth) + repl := newJSRE(eth) if len(ctx.Args()) == 0 { - runREPL(eth) + repl.interactive() } else { for _, file := range ctx.Args() { - execJsFile(eth, file) + repl.exec(file) } } eth.Stop() From 62ebce304edcdd9d0c1022b57d9e744013c0818f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:12:55 +0100 Subject: [PATCH 45/82] crypto: delete old key management stuff --- crypto/key_manager.go | 134 ------------------------------------------ crypto/key_store.go | 113 ----------------------------------- crypto/keyring.go | 123 -------------------------------------- crypto/keys_test.go | 122 -------------------------------------- 4 files changed, 492 deletions(-) delete mode 100644 crypto/key_manager.go delete mode 100644 crypto/key_store.go delete mode 100644 crypto/keyring.go delete mode 100644 crypto/keys_test.go diff --git a/crypto/key_manager.go b/crypto/key_manager.go deleted file mode 100644 index 326e559e07..0000000000 --- a/crypto/key_manager.go +++ /dev/null @@ -1,134 +0,0 @@ -package crypto - -import ( - "fmt" - "sync" - - "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/logger" -) - -var keylogger = logger.NewLogger("KEY") - -type KeyManager struct { - keyRing *KeyRing - session string - keyStore KeyStore // interface - keyRings map[string]*KeyRing // cache - keyPair *KeyPair -} - -func NewDBKeyManager(db ethutil.Database) *KeyManager { - return &KeyManager{keyStore: &DBKeyStore{db: db}, keyRings: make(map[string]*KeyRing)} -} - -func NewFileKeyManager(basedir string) *KeyManager { - return &KeyManager{keyStore: &FileKeyStore{basedir: basedir}, keyRings: make(map[string]*KeyRing)} -} - -func (k *KeyManager) KeyPair() *KeyPair { - return k.keyPair -} - -func (k *KeyManager) KeyRing() *KeyPair { - return k.keyPair -} - -func (k *KeyManager) PrivateKey() []byte { - return k.keyPair.PrivateKey -} - -func (k *KeyManager) PublicKey() []byte { - return k.keyPair.PublicKey -} - -func (k *KeyManager) Address() []byte { - return k.keyPair.Address() -} - -func (k *KeyManager) save(session string, keyRing *KeyRing) error { - err := k.keyStore.Save(session, keyRing) - if err != nil { - return err - } - k.keyRings[session] = keyRing - return nil -} - -func (k *KeyManager) load(session string) (*KeyRing, error) { - keyRing, found := k.keyRings[session] - if !found { - var err error - keyRing, err = k.keyStore.Load(session) - if err != nil { - return nil, err - } - } - return keyRing, nil -} - -func cursorError(cursor int, len int) error { - return fmt.Errorf("cursor %d out of range (0..%d)", cursor, len) -} - -func (k *KeyManager) reset(session string, cursor int, keyRing *KeyRing) error { - if cursor >= keyRing.Len() { - return cursorError(cursor, keyRing.Len()) - } - lock := &sync.Mutex{} - lock.Lock() - defer lock.Unlock() - err := k.save(session, keyRing) - if err != nil { - return err - } - k.session = session - k.keyRing = keyRing - k.keyPair = keyRing.GetKeyPair(cursor) - return nil -} - -func (k *KeyManager) SetCursor(cursor int) error { - if cursor >= k.keyRing.Len() { - return cursorError(cursor, k.keyRing.Len()) - } - k.keyPair = k.keyRing.GetKeyPair(cursor) - return nil -} - -func (k *KeyManager) Init(session string, cursor int, force bool) error { - var keyRing *KeyRing - if !force { - var err error - keyRing, err = k.load(session) - if err != nil { - return err - } - } - if keyRing == nil { - keyRing = NewGeneratedKeyRing(1) - keylogger.Infof("Created keypair. Private key: %x\n", keyRing.keys[0].PrivateKey) - } - return k.reset(session, cursor, keyRing) -} - -func (k *KeyManager) InitFromSecretsFile(session string, cursor int, secretsfile string) error { - keyRing, err := NewKeyRingFromFile(secretsfile) - if err != nil { - return err - } - return k.reset(session, cursor, keyRing) -} - -func (k *KeyManager) InitFromString(session string, cursor int, secrets string) error { - keyRing, err := NewKeyRingFromString(secrets) - if err != nil { - return err - } - return k.reset(session, cursor, keyRing) -} - -func (k *KeyManager) Export(dir string) error { - fileKeyStore := FileKeyStore{dir} - return fileKeyStore.Save(k.session, k.keyRing) -} diff --git a/crypto/key_store.go b/crypto/key_store.go deleted file mode 100644 index 04560a04ed..0000000000 --- a/crypto/key_store.go +++ /dev/null @@ -1,113 +0,0 @@ -package crypto - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "strings" - - "github.com/ethereum/go-ethereum/ethutil" -) - -type KeyStore interface { - Load(string) (*KeyRing, error) - Save(string, *KeyRing) error -} - -type DBKeyStore struct { - db ethutil.Database -} - -const dbKeyPrefix = "KeyRing" - -func (k *DBKeyStore) dbKey(session string) []byte { - return []byte(fmt.Sprintf("%s%s", dbKeyPrefix, session)) -} - -func (k *DBKeyStore) Save(session string, keyRing *KeyRing) error { - k.db.Put(k.dbKey(session), keyRing.RlpEncode()) - return nil -} - -func (k *DBKeyStore) Load(session string) (*KeyRing, error) { - data, err := k.db.Get(k.dbKey(session)) - if err != nil { - return nil, nil - } - var keyRing *KeyRing - keyRing, err = NewKeyRingFromBytes(data) - if err != nil { - return nil, err - } - // if empty keyRing is found we return nil, no error - if keyRing.Len() == 0 { - return nil, nil - } - return keyRing, nil -} - -type FileKeyStore struct { - basedir string -} - -func (k *FileKeyStore) Save(session string, keyRing *KeyRing) error { - var content []byte - var err error - var privateKeys []string - var publicKeys []string - var mnemonics []string - var addresses []string - keyRing.Each(func(keyPair *KeyPair) { - privateKeys = append(privateKeys, ethutil.Bytes2Hex(keyPair.PrivateKey)) - publicKeys = append(publicKeys, ethutil.Bytes2Hex(keyPair.PublicKey)) - addresses = append(addresses, ethutil.Bytes2Hex(keyPair.Address())) - mnemonics = append(mnemonics, keyPair.Mnemonic()) - }) - - basename := session - if session == "" { - basename = "default" - } - - path := path.Join(k.basedir, basename) - content = []byte(strings.Join(privateKeys, "\n")) - err = ioutil.WriteFile(path+".prv", content, 0600) - if err != nil { - return err - } - - content = []byte(strings.Join(publicKeys, "\n")) - err = ioutil.WriteFile(path+".pub", content, 0644) - if err != nil { - return err - } - - content = []byte(strings.Join(addresses, "\n")) - err = ioutil.WriteFile(path+".addr", content, 0644) - if err != nil { - return err - } - - content = []byte(strings.Join(mnemonics, "\n")) - err = ioutil.WriteFile(path+".mne", content, 0600) - if err != nil { - return err - } - - return nil -} - -func (k *FileKeyStore) Load(session string) (*KeyRing, error) { - basename := session - if session == "" { - basename = "default" - } - secfile := path.Join(k.basedir, basename+".prv") - _, err := os.Stat(secfile) - // if file is not found then we return nil, no error - if err != nil { - return nil, nil - } - return NewKeyRingFromFile(secfile) -} diff --git a/crypto/keyring.go b/crypto/keyring.go deleted file mode 100644 index eab13dbc4b..0000000000 --- a/crypto/keyring.go +++ /dev/null @@ -1,123 +0,0 @@ -package crypto - -import ( - "fmt" - "io/ioutil" - "strings" - - "github.com/ethereum/go-ethereum/ethutil" -) - -type KeyRing struct { - keys []*KeyPair -} - -func NewKeyRing() *KeyRing { - return &KeyRing{} -} - -func (k *KeyRing) AddKeyPair(keyPair *KeyPair) { - k.keys = append(k.keys, keyPair) -} - -func (k *KeyRing) GetKeyPair(i int) *KeyPair { - if len(k.keys) > i { - return k.keys[i] - } - - return nil -} - -func (k *KeyRing) Empty() bool { - return k.Len() == 0 -} - -func (k *KeyRing) Len() int { - return len(k.keys) -} - -func (k *KeyRing) Each(f func(*KeyPair)) { - for _, keyPair := range k.keys { - f(keyPair) - } -} - -func NewGeneratedKeyRing(len int) *KeyRing { - keyRing := NewKeyRing() - for i := 0; i < len; i++ { - keyRing.AddKeyPair(GenerateNewKeyPair()) - } - return keyRing -} - -func NewKeyRingFromFile(secfile string) (*KeyRing, error) { - var content []byte - var err error - content, err = ioutil.ReadFile(secfile) - if err != nil { - return nil, err - } - keyRing, err := NewKeyRingFromString(string(content)) - if err != nil { - return nil, err - } - return keyRing, nil -} - -func NewKeyRingFromString(content string) (*KeyRing, error) { - secretStrings := strings.Split(content, "\n") - var secrets [][]byte - for _, secretString := range secretStrings { - secret := secretString - words := strings.Split(secretString, " ") - if len(words) == 24 { - secret = MnemonicDecode(words) - } else if len(words) != 1 { - return nil, fmt.Errorf("Unrecognised key format") - } - - if len(secret) != 0 { - secrets = append(secrets, ethutil.Hex2Bytes(secret)) - } - } - - return NewKeyRingFromSecrets(secrets) -} - -func NewKeyRingFromSecrets(secs [][]byte) (*KeyRing, error) { - keyRing := NewKeyRing() - for _, sec := range secs { - keyPair, err := NewKeyPairFromSec(sec) - if err != nil { - return nil, err - } - keyRing.AddKeyPair(keyPair) - } - return keyRing, nil -} - -func NewKeyRingFromBytes(data []byte) (*KeyRing, error) { - var secrets [][]byte - it := ethutil.NewValueFromBytes(data).NewIterator() - for it.Next() { - secret := it.Value().Bytes() - secrets = append(secrets, secret) - } - keyRing, err := NewKeyRingFromSecrets(secrets) - if err != nil { - return nil, err - } - return keyRing, nil -} - -func (k *KeyRing) RlpEncode() []byte { - return k.RlpValue().Encode() -} - -func (k *KeyRing) RlpValue() *ethutil.Value { - v := ethutil.EmptyValue() - k.Each(func(keyPair *KeyPair) { - v.Append(keyPair.RlpValue()) - }) - return v -} diff --git a/crypto/keys_test.go b/crypto/keys_test.go deleted file mode 100644 index 56e8519696..0000000000 --- a/crypto/keys_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package crypto - -// import ( -// "github.com/ethereum/go-ethereum/ethdb" -// // "io/ioutil" -// "fmt" -// "os" -// "path" -// "testing" -// ) - -// // test if persistence layer works -// func TestDBKeyManager(t *testing.T) { -// memdb, _ := ethdb.NewMemDatabase() -// keyManager0 := NewDBKeyManager(memdb) -// err := keyManager0.Init("", 0, false) -// if err != nil { -// t.Error("Unexpected error: ", err) -// } -// keyManager1 := NewDBKeyManager(memdb) -// err = keyManager1.Init("", 0, false) -// if err != nil { -// t.Error("Unexpected error: ", err) -// } -// if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) { -// t.Error("Expected private keys %x, %x, to be identical via db persistence", keyManager0.PrivateKey(), keyManager1.PrivateKey()) -// } -// err = keyManager1.Init("", 0, true) -// if err != nil { -// t.Error("Unexpected error: ", err) -// } -// if string(keyManager0.PrivateKey()) == string(keyManager1.PrivateKey()) { -// t.Error("Expected private keys %x, %x, to be be different despite db persistence if force generate", keyManager0.PrivateKey(), keyManager1.PrivateKey()) -// } -// } - -// func TestFileKeyManager(t *testing.T) { -// basedir0 := "/tmp/ethtest0" -// os.RemoveAll(basedir0) -// os.Mkdir(basedir0, 0777) - -// keyManager0 := NewFileKeyManager(basedir0) -// err := keyManager0.Init("", 0, false) -// if err != nil { -// t.Error("Unexpected error: ", err) -// } - -// keyManager1 := NewFileKeyManager(basedir0) - -// err = keyManager1.Init("", 0, false) -// if err != nil { -// t.Error("Unexpected error: ", err) -// } -// if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) { -// t.Error("Expected private keys %x, %x, to be identical via db persistence", keyManager0.PrivateKey(), keyManager1.PrivateKey()) -// } - -// err = keyManager1.Init("", 0, true) -// if err != nil { -// t.Error("Unexpected error: ", err) -// } -// if string(keyManager0.PrivateKey()) == string(keyManager1.PrivateKey()) { -// t.Error("Expected private keys %x, %x, to be be different despite db persistence if force generate", keyManager0.PrivateKey(), keyManager1.PrivateKey()) -// } -// } - -// // cursor errors -// func TestCursorErrors(t *testing.T) { -// memdb, _ := ethdb.NewMemDatabase() -// keyManager0 := NewDBKeyManager(memdb) -// err := keyManager0.Init("", 0, false) -// err = keyManager0.Init("", 1, false) -// if err == nil { -// t.Error("Expected cursor error") -// } -// err = keyManager0.SetCursor(1) -// if err == nil { -// t.Error("Expected cursor error") -// } -// } - -// func TestExportImport(t *testing.T) { -// memdb, _ := ethdb.NewMemDatabase() -// keyManager0 := NewDBKeyManager(memdb) -// err := keyManager0.Init("", 0, false) -// basedir0 := "/tmp/ethtest0" -// os.RemoveAll(basedir0) -// os.Mkdir(basedir0, 0777) -// keyManager0.Export(basedir0) - -// keyManager1 := NewFileKeyManager(basedir0) -// err = keyManager1.Init("", 0, false) -// if err != nil { -// t.Error("Unexpected error: ", err) -// } -// fmt.Printf("keyRing: %v\n", keyManager0.KeyPair()) -// fmt.Printf("keyRing: %v\n", keyManager1.KeyPair()) -// if string(keyManager0.PrivateKey()) != string(keyManager1.PrivateKey()) { -// t.Error("Expected private keys %x, %x, to be identical via export to filestore basedir", keyManager0.PrivateKey(), keyManager1.PrivateKey()) -// } -// path.Join("") - -// // memdb, _ = ethdb.NewMemDatabase() -// // keyManager2 := NewDBKeyManager(memdb) -// // err = keyManager2.InitFromSecretsFile("", 0, path.Join(basedir0, "default.prv")) -// // if err != nil { -// // t.Error("Unexpected error: ", err) -// // } -// // if string(keyManager0.PrivateKey()) != string(keyManager2.PrivateKey()) { -// // t.Error("Expected private keys %s, %s, to be identical via export/import prv", keyManager0.PrivateKey(), keyManager1.PrivateKey()) -// // } - -// // memdb, _ = ethdb.NewMemDatabase() -// // keyManager3 := NewDBKeyManager(memdb) -// // err = keyManager3.InitFromSecretsFile("", 0, path.Join(basedir0, "default.mne")) -// // if err != nil { -// // t.Error("Unexpected error: ", err) -// // } -// // if string(keyManager0.PrivateKey()) != string(keyManager3.PrivateKey()) { -// // t.Error("Expected private keys %s, %s, to be identical via export/import mnemonic file", keyManager0.PrivateKey(), keyManager1.PrivateKey()) -// // } -// } From ae38871a54b1ccb51346ce5d99beb863b7febe66 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:33:59 +0100 Subject: [PATCH 46/82] cmd/ethereum: remove "prompter" in identifiers --- cmd/ethereum/js.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index 96e292733d..de73e83a2f 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -41,14 +41,14 @@ type prompter interface { PasswordPrompt(p string) (string, error) } -type dumbPrompter struct{ r *bufio.Reader } +type dumbterm struct{ r *bufio.Reader } -func (r dumbPrompter) Prompt(p string) (string, error) { +func (r dumbterm) Prompt(p string) (string, error) { fmt.Print(p) return r.r.ReadString('\n') } -func (r dumbPrompter) PasswordPrompt(p string) (string, error) { +func (r dumbterm) PasswordPrompt(p string) (string, error) { fmt.Println("!! Unsupported terminal, password will echo.") fmt.Print(p) input, err := bufio.NewReader(os.Stdin).ReadString('\n') @@ -56,13 +56,14 @@ func (r dumbPrompter) PasswordPrompt(p string) (string, error) { return input, err } -func (r dumbPrompter) AppendHistory(string) {} +func (r dumbterm) AppendHistory(string) {} type jsre struct { re *javascript.JSRE ethereum *eth.Ethereum xeth *xeth.XEth ps1 string + prompter } @@ -73,7 +74,7 @@ func newJSRE(ethereum *eth.Ethereum) *jsre { js.initStdFuncs() if !liner.TerminalSupported() { - js.prompter = dumbPrompter{bufio.NewReader(os.Stdin)} + js.prompter = dumbterm{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() lr.SetCtrlCAborts(true) @@ -87,13 +88,13 @@ func newJSRE(ethereum *eth.Ethereum) *jsre { func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool { p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx) - answer, _ := self.prompter.Prompt(p) + answer, _ := self.Prompt(p) return strings.HasPrefix(strings.Trim(answer, " "), "y") } func (self *jsre) UnlockAccount(addr []byte) bool { fmt.Printf("Please unlock account %x.\n", addr) - pass, err := self.prompter.PasswordPrompt("Passphrase: ") + pass, err := self.PasswordPrompt("Passphrase: ") if err != nil { return false } @@ -124,7 +125,7 @@ func (self *jsre) exec(filename string) error { func (self *jsre) interactive() { for { - input, err := self.prompter.Prompt(self.ps1) + input, err := self.Prompt(self.ps1) if err != nil { return } @@ -138,7 +139,7 @@ func (self *jsre) interactive() { return } hist := str[:len(str)-1] - self.prompter.AppendHistory(hist) + self.AppendHistory(hist) self.parseInput(str) str = "" } From 9b3ae1fdb7903bc390fcb5c34edddace2a0e1dd8 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:34:52 +0100 Subject: [PATCH 47/82] eth: fix tests --- eth/backend.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/eth/backend.go b/eth/backend.go index 8417b572b9..ac8a016fe5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -25,7 +25,7 @@ import ( ) var ( - ethlogger = logger.NewLogger("SERV") + servlogger = logger.NewLogger("SERV") jsonlogger = logger.NewJsonLogger() defaultBootNodes = []*discover.Node{ @@ -74,7 +74,7 @@ func (cfg *Config) parseBootNodes() []*discover.Node { } n, err := discover.ParseNode(url) if err != nil { - ethlogger.Errorf("Bootstrap URL %s: %v\n", url, err) + servlogger.Errorf("Bootstrap URL %s: %v\n", url, err) continue } ns = append(ns, n) @@ -98,7 +98,7 @@ func (cfg *Config) nodeKey() (*ecdsa.PrivateKey, error) { return nil, fmt.Errorf("could not generate server key: %v", err) } if err := ioutil.WriteFile(keyfile, crypto.FromECDSA(key), 0600); err != nil { - ethlogger.Errorln("could not persist nodekey: ", err) + servlogger.Errorln("could not persist nodekey: ", err) } return key, nil } @@ -134,7 +134,7 @@ type Ethereum struct { func New(config *Config) (*Ethereum, error) { // Boostrap database - ethlogger := logger.New(config.DataDir, config.LogFile, config.LogLevel, config.LogFormat) + servlogger := logger.New(config.DataDir, config.LogFile, config.LogLevel, config.LogFormat) blockDb, err := ethdb.NewLDBDatabase(path.Join(config.DataDir, "blockchain")) if err != nil { @@ -161,7 +161,7 @@ func New(config *Config) (*Ethereum, error) { blockDb: blockDb, stateDb: stateDb, eventMux: &event.TypeMux{}, - logger: ethlogger, + logger: servlogger, accountManager: config.AccountManager, DataDir: config.DataDir, } @@ -256,7 +256,7 @@ func (s *Ethereum) Start() error { s.blockSub = s.eventMux.Subscribe(core.NewMinedBlockEvent{}) go s.blockBroadcastLoop() - ethlogger.Infoln("Server started") + servlogger.Infoln("Server started") return nil } @@ -284,7 +284,7 @@ func (s *Ethereum) Stop() { s.whisper.Stop() } - ethlogger.Infoln("Server stopped") + servlogger.Infoln("Server stopped") close(s.shutdownChan) } From e6118f2d7914fdc52c6ad86e48561f3bc1adaf61 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:35:21 +0100 Subject: [PATCH 48/82] state: fix tests --- state/state_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/state/state_test.go b/state/state_test.go index ee1cf92866..6b6d52881c 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -33,16 +33,16 @@ func (s *StateSuite) TestDump(c *checker.C) { // check that dump contains the state objects that are in trie got := string(s.state.Dump()) want := `{ - "root": "4e3a59299745ba6752247c8b91d0f716dac9ec235861c91f5ac1894a361d87ba", + "root": "6e277ae8357d013e50f74eedb66a991f6922f93ae03714de58b3d0c5e9eee53f", "accounts": { - "0000000000000000000000000000000000000001": { + "1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d": { "balance": "22", "nonce": 0, "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "storage": {} }, - "0000000000000000000000000000000000000102": { + "a17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1": { "balance": "0", "nonce": 0, "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", From 3de51f76eea87f8e8a4b8a7277cd294529e28491 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 9 Mar 2015 20:38:37 -0500 Subject: [PATCH 49/82] Allow integers for BlockNumber If parsing fails, fall back to hex decoding --- rpc/args.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/rpc/args.go b/rpc/args.go index 63969e598b..9735feb624 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -47,7 +47,12 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { if len(obj) < 1 { return errArguments } - args.BlockNumber = ethutil.Big(obj[0].(string)).Int64() + + if v, ok := obj[0].(float64); ok { + args.BlockNumber = int64(v) + } else { + args.BlockNumber = ethutil.Big(obj[0].(string)).Int64() + } if len(obj) > 1 { args.Transactions = obj[1].(bool) @@ -110,7 +115,9 @@ func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - if obj[1].(string) == "latest" { + if v, ok := obj[1].(float64); ok { + args.BlockNumber = int64(v) + } else if obj[1].(string) == "latest" { args.BlockNumber = -1 } else { args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() @@ -147,7 +154,9 @@ func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { args.Key = obj[1].(string) if len(obj) > 2 { - if obj[2].(string) == "latest" { + if v, ok := obj[2].(float64); ok { + args.BlockNumber = int64(v) + } else if obj[2].(string) == "latest" { args.BlockNumber = -1 } else { args.BlockNumber = ethutil.Big(obj[2].(string)).Int64() @@ -187,7 +196,9 @@ func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - if obj[1].(string) == "latest" { + if v, ok := obj[1].(float64); ok { + args.BlockNumber = int64(v) + } else if obj[1].(string) == "latest" { args.BlockNumber = -1 } else { args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() @@ -222,7 +233,9 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - if obj[1].(string) == "latest" { + if v, ok := obj[1].(float64); ok { + args.BlockNumber = int64(v) + } else if obj[1].(string) == "latest" { args.BlockNumber = -1 } else { args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() @@ -257,7 +270,9 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { args.Address = obj[0].(string) if len(obj) > 1 { - if obj[1].(string) == "latest" { + if v, ok := obj[1].(float64); ok { + args.BlockNumber = int64(v) + } else if obj[1].(string) == "latest" { args.BlockNumber = -1 } else { args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() From 80985f97da8174576ee227909035a364af2fd6c9 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:35:46 +0100 Subject: [PATCH 50/82] cmd/evm, core, ethdb, state, tests/helper: remove ReadConfig calls --- cmd/evm/main.go | 2 -- core/chain_manager_test.go | 2 -- core/helper_test.go | 2 -- ethdb/database_test.go | 2 -- state/state_test.go | 1 - tests/helper/init.go | 3 --- 6 files changed, 12 deletions(-) diff --git a/cmd/evm/main.go b/cmd/evm/main.go index d6a93460e6..960558bb4f 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -59,8 +59,6 @@ func main() { logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel))) - ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "") - db, _ := ethdb.NewMemDatabase() statedb := state.New(nil, db) sender := statedb.NewStateObject([]byte("sender")) diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index e78c2e980e..91822f9e20 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -12,14 +12,12 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rlp" ) func init() { runtime.GOMAXPROCS(runtime.NumCPU()) - ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH") } // Test fork of length N starting from block i diff --git a/core/helper_test.go b/core/helper_test.go index 473576e3f1..36d9e70a83 100644 --- a/core/helper_test.go +++ b/core/helper_test.go @@ -62,8 +62,6 @@ func (tm *TestManager) Db() ethutil.Database { } func NewTestManager() *TestManager { - ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "ETH") - db, err := ethdb.NewMemDatabase() if err != nil { fmt.Println("Could not create mem-db, failing") diff --git a/ethdb/database_test.go b/ethdb/database_test.go index 2cbaf58e0d..7de30fd814 100644 --- a/ethdb/database_test.go +++ b/ethdb/database_test.go @@ -7,8 +7,6 @@ import ( ) func TestCompression(t *testing.T) { - ethutil.ReadConfig("", "/tmp", "") - db, err := NewLDBDatabase("testdb") if err != nil { t.Fatal(err) diff --git a/state/state_test.go b/state/state_test.go index 6b6d52881c..07e35f7e28 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -57,7 +57,6 @@ func (s *StateSuite) TestDump(c *checker.C) { } func (s *StateSuite) SetUpTest(c *checker.C) { - ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") db, _ := ethdb.NewMemDatabase() s.state = New(nil, db) } diff --git a/tests/helper/init.go b/tests/helper/init.go index df98b9e422..e0892d8f69 100644 --- a/tests/helper/init.go +++ b/tests/helper/init.go @@ -4,7 +4,6 @@ import ( "log" "os" - "github.com/ethereum/go-ethereum/ethutil" logpkg "github.com/ethereum/go-ethereum/logger" ) @@ -14,6 +13,4 @@ var Log = logpkg.NewLogger("TEST") func init() { Logger = logpkg.NewStdLogSystem(os.Stdout, log.LstdFlags, logpkg.InfoLevel) logpkg.AddLogSystem(Logger) - - ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") } From b87442a9f72f9f8dad9ea1442fc69529b2aaa631 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 10 Mar 2015 11:29:22 +0100 Subject: [PATCH 51/82] Fixed error message. Closes #448 --- core/block_processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/block_processor.go b/core/block_processor.go index 34c12729cd..ea9d06841a 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -261,7 +261,7 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error { } if block.Time <= parent.Time { - return ValidationError("Block timestamp not after or equal to prev block (%v - %v)", block.Time, parent.Time) + return ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time) } if int64(block.Time) > time.Now().Unix() { From 56524d13b5113751539fe0a86a0a88fc4c45d2af Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 10 Mar 2015 12:06:16 +0100 Subject: [PATCH 52/82] removed debugger --- cmd/mist/assets/qml/main.qml | 25 -- cmd/mist/assets/qml/views/chain.qml | 17 -- cmd/mist/debugger.go | 367 ---------------------------- cmd/mist/ui_lib.go | 27 +- vm/debugger.go | 1 - 5 files changed, 1 insertion(+), 436 deletions(-) delete mode 100644 cmd/mist/debugger.go delete mode 100644 vm/debugger.go diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index 6824d2ba92..e06ddbd71b 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -194,13 +194,6 @@ ApplicationWindow { Menu { title: "Developer" - MenuItem { - iconSource: "../icecream.png" - text: "Debugger" - shortcut: "Ctrl+d" - onTriggered: eth.startDebugger() - } - MenuItem { text: "Import Tx" onTriggered: { @@ -756,24 +749,6 @@ ApplicationWindow { } } - Rectangle { - height: 55 - color: "transparent" - visible: true - Text { - text: "DEBUG" - font.family: sourceSansPro.name - font.weight: Font.DemiBold - anchors { - left: parent.left - top: parent.verticalCenter - leftMargin: 16 - } - color: "#AAA0A0" - } - } - - ColumnLayout { id: menuLegacy visible: true diff --git a/cmd/mist/assets/qml/views/chain.qml b/cmd/mist/assets/qml/views/chain.qml index 4d1bc0e03d..9892beddf1 100644 --- a/cmd/mist/assets/qml/views/chain.qml +++ b/cmd/mist/assets/qml/views/chain.qml @@ -178,7 +178,6 @@ Rectangle { } function showContractData(tx) { - txDetailsDebugButton.tx = tx if(tx.createsContract) { contractData.text = tx.data contractLabel.text = "

Transaction created contract " + tx.address + "

" @@ -202,22 +201,6 @@ Rectangle { id: contractLabel anchors.leftMargin: 10 } - Button { - property var tx - id: txDetailsDebugButton - anchors.right: parent.right - anchors.rightMargin: 10 - anchors.top: parent.top - anchors.topMargin: 10 - text: "Debug contract" - onClicked: { - if(tx && tx.createsContract){ - eth.startDbWithCode(tx.rawData) - }else { - eth.startDbWithContractAndData(tx.address, tx.rawData) - } - } - } TextArea { id: contractData text: "Contract" diff --git a/cmd/mist/debugger.go b/cmd/mist/debugger.go deleted file mode 100644 index c1ab2f3f18..0000000000 --- a/cmd/mist/debugger.go +++ /dev/null @@ -1,367 +0,0 @@ -/* - This file is part of go-ethereum - - go-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - go-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with go-ethereum. If not, see . -*/ -/** - * @authors - * Jeffrey Wilcke - */ -package main - -import ( - "fmt" - "math/big" - "strconv" - "strings" - "unicode" - - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/state" - "github.com/ethereum/go-ethereum/vm" - "github.com/obscuren/qml" -) - -type DebuggerWindow struct { - win *qml.Window - engine *qml.Engine - lib *UiLib - - vm *vm.Vm - Db *Debugger - - state *state.StateDB -} - -func NewDebuggerWindow(lib *UiLib) *DebuggerWindow { - engine := qml.NewEngine() - component, err := engine.LoadFile(lib.AssetPath("debugger/debugger.qml")) - if err != nil { - fmt.Println(err) - - return nil - } - - win := component.CreateWindow(nil) - - w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.Vm{}} - w.Db = NewDebugger(w) - - return w -} - -func (self *DebuggerWindow) Show() { - context := self.engine.Context() - context.SetVar("dbg", self) - - go func() { - self.win.Show() - self.win.Wait() - }() -} - -func (self *DebuggerWindow) SetCode(code string) { - self.win.Set("codeText", code) -} - -func (self *DebuggerWindow) SetData(data string) { - self.win.Set("dataText", data) -} - -func (self *DebuggerWindow) SetAsm(data []byte) { - self.win.Root().Call("clearAsm") - - dis := core.Disassemble(data) - for _, str := range dis { - self.win.Root().Call("setAsm", str) - } -} - -func (self *DebuggerWindow) Compile(code string) { - var err error - script := ethutil.StringToByteFunc(code, func(s string) (ret []byte) { - ret, err = ethutil.Compile(s, true) - return - }) - - if err == nil { - self.SetAsm(script) - } -} - -// Used by QML -func (self *DebuggerWindow) AutoComp(code string) { - if self.Db.done { - self.Compile(code) - } -} - -func (self *DebuggerWindow) ClearLog() { - self.win.Root().Call("clearLog") -} - -func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) { - self.Stop() - - defer func() { - if r := recover(); r != nil { - self.Logf("compile FAULT: %v", r) - } - }() - - data := utils.FormatTransactionData(dataStr) - - var err error - script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { - ret, err = ethutil.Compile(s, false) - return - }) - - if err != nil { - self.Logln(err) - - return - } - - var ( - gas = ethutil.Big(gasStr) - gasPrice = ethutil.Big(gasPriceStr) - value = ethutil.Big(valueStr) - // Contract addr as test address - keyPair = self.lib.eth.KeyManager().KeyPair() - ) - - statedb := self.lib.eth.ChainManager().TransState() - account := self.lib.eth.ChainManager().TransState().GetAccount(keyPair.Address()) - contract := statedb.NewStateObject([]byte{0}) - contract.SetCode(script) - contract.SetBalance(value) - - self.SetAsm(script) - - block := self.lib.eth.ChainManager().CurrentBlock() - - msg := types.NewTransactionMessage(nil, value, gas, gasPrice, data) - env := core.NewEnv(statedb, self.lib.eth.ChainManager(), msg, block) - - self.Logf("callsize %d", len(script)) - go func() { - pgas := new(big.Int).Set(gas) - ret, err := env.Call(account, contract.Address(), data, gas, gasPrice, ethutil.Big0) - - rgas := new(big.Int).Sub(pgas, gas) - tot := new(big.Int).Mul(rgas, gasPrice) - self.Logf("gas usage %v total price = %v (%v)", rgas, tot, ethutil.CurrencyToString(tot)) - if err != nil { - self.Logln("exited with errors:", err) - } else { - if len(ret) > 0 { - self.Logf("exited: % x", ret) - } else { - self.Logf("exited: nil") - } - } - - statedb.Reset() - - if !self.Db.interrupt { - self.Db.done = true - } else { - self.Db.interrupt = false - } - }() -} - -func (self *DebuggerWindow) Logf(format string, v ...interface{}) { - self.win.Root().Call("setLog", fmt.Sprintf(format, v...)) -} - -func (self *DebuggerWindow) Logln(v ...interface{}) { - str := fmt.Sprintln(v...) - self.Logf("%s", str[:len(str)-1]) -} - -func (self *DebuggerWindow) Next() { - self.Db.Next() -} - -func (self *DebuggerWindow) Continue() { - self.vm.Stepping = false - self.Next() -} - -func (self *DebuggerWindow) Stop() { - if !self.Db.done { - self.Db.Q <- true - } -} - -func (self *DebuggerWindow) ExecCommand(command string) { - if len(command) > 0 { - cmd := strings.Split(command, " ") - switch cmd[0] { - case "help": - self.Logln("Debugger commands:") - self.Logln("break, bp Set breakpoint on instruction") - self.Logln("clear [log, break, bp] Clears previous set sub-command(s)") - case "break", "bp": - if len(cmd) > 1 { - lineNo, err := strconv.Atoi(cmd[1]) - if err != nil { - self.Logln(err) - break - } - self.Db.breakPoints = append(self.Db.breakPoints, int64(lineNo)) - self.Logf("break point set on instruction %d", lineNo) - } else { - self.Logf("'%s' requires line number", cmd[0]) - } - case "clear": - if len(cmd) > 1 { - switch cmd[1] { - case "break", "bp": - self.Db.breakPoints = nil - - self.Logln("Breakpoints cleared") - case "log": - self.ClearLog() - default: - self.Logf("clear '%s' is not valid", cmd[1]) - } - } else { - self.Logln("'clear' requires sub command") - } - - default: - self.Logf("Unknown command %s", cmd[0]) - } - } -} - -type Debugger struct { - N chan bool - Q chan bool - done, interrupt bool - breakPoints []int64 - main *DebuggerWindow - win *qml.Window -} - -func NewDebugger(main *DebuggerWindow) *Debugger { - db := &Debugger{make(chan bool), make(chan bool), true, false, nil, main, main.win} - - return db -} - -type storeVal struct { - Key, Value string -} - -func (self *Debugger) Step(evm vm.VirtualMachine, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, context *vm.Context) { -} - -func (self *Debugger) BreakHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool { - self.main.Logln("break on instr:", pc) - - return self.halting(pc, op, mem, stack, stateObject) -} - -func (self *Debugger) StepHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool { - return self.halting(pc, op, mem, stack, stateObject) -} - -func (self *Debugger) SetCode(byteCode []byte) { - self.main.SetAsm(byteCode) -} - -func (self *Debugger) BreakPoints() []int64 { - return self.breakPoints -} - -func (d *Debugger) halting(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool { - d.win.Root().Call("setInstruction", pc) - d.win.Root().Call("clearMem") - d.win.Root().Call("clearStack") - d.win.Root().Call("clearStorage") - - addr := 0 - for i := 0; i+16 <= mem.Len(); i += 16 { - dat := mem.Data()[i : i+16] - var str string - - for _, d := range dat { - if unicode.IsGraphic(rune(d)) { - str += string(d) - } else { - str += "?" - } - } - - d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("%s % x", str, dat)}) - addr += 16 - } - - for _, val := range stack.Data() { - d.win.Root().Call("setStack", val.String()) - } - - it := stateObject.Trie().Iterator() - for it.Next() { - d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", it.Key), fmt.Sprintf("% x", it.Value)}) - - } - - stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32)) - psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64()) - d.win.Root().ObjectByName("stackFrame").Set("text", fmt.Sprintf(`stack ptr: %v`, stackFrameAt)) - d.win.Root().ObjectByName("stackSize").Set("text", fmt.Sprintf(`stack size: %d`, psize)) - d.win.Root().ObjectByName("memSize").Set("text", fmt.Sprintf(`mem size: %v`, mem.Len())) - -out: - for { - select { - case <-d.N: - break out - case <-d.Q: - d.interrupt = true - d.clearBuffers() - - return false - } - } - - return true -} - -func (d *Debugger) clearBuffers() { -out: - // drain - for { - select { - case <-d.N: - case <-d.Q: - default: - break out - } - } -} - -func (d *Debugger) Next() { - if !d.done { - d.N <- true - } -} diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go index 187d5b2d60..b202432c41 100644 --- a/cmd/mist/ui_lib.go +++ b/cmd/mist/ui_lib.go @@ -47,9 +47,7 @@ type UiLib struct { connected bool assetPath string // The main application window - win *qml.Window - Db *Debugger - DbWindow *DebuggerWindow + win *qml.Window jsEngine *javascript.JSRE @@ -126,29 +124,6 @@ func (ui *UiLib) AssetPath(p string) string { return path.Join(ui.assetPath, p) } -func (self *UiLib) StartDbWithContractAndData(contractHash, data string) { - dbWindow := NewDebuggerWindow(self) - object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash)) - if len(object.Code()) > 0 { - dbWindow.SetCode(ethutil.Bytes2Hex(object.Code())) - } - dbWindow.SetData(data) - - dbWindow.Show() -} - -func (self *UiLib) StartDbWithCode(code string) { - dbWindow := NewDebuggerWindow(self) - dbWindow.SetCode(code) - dbWindow.Show() -} - -func (self *UiLib) StartDebugger() { - dbWindow := NewDebuggerWindow(self) - - dbWindow.Show() -} - func (self *UiLib) Transact(params map[string]interface{}) (string, error) { object := mapToTxParams(params) diff --git a/vm/debugger.go b/vm/debugger.go deleted file mode 100644 index 830a8f69d9..0000000000 --- a/vm/debugger.go +++ /dev/null @@ -1 +0,0 @@ -package vm From bbe8b186600992ada6da9e75e9976cd5a9dc0ae3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 10 Mar 2015 13:40:49 +0100 Subject: [PATCH 53/82] Added unmarshalState --- cmd/mist/assets/examples/coin.html | 2 +- .../assets/ext/ethereum.js/dist/ethereum.js | 2499 ++++++++++------- rpc/args.go | 50 +- 3 files changed, 1522 insertions(+), 1029 deletions(-) diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html index 509a9aeeb3..115145c4c4 100644 --- a/cmd/mist/assets/examples/coin.html +++ b/cmd/mist/assets/examples/coin.html @@ -35,7 +35,7 @@ var web3 = require('web3'); var eth = web3.eth; - web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8545')); + web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545')); var desc = [{ "name": "balance(address)", "type": "function", diff --git a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js index 5b7d872706..7b2531677a 100644 --- a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js +++ b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js @@ -22,34 +22,57 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof requ * @date 2014 */ -var utils = require('./utils'); +var utils = require('../utils/utils'); +var c = require('../utils/config'); var types = require('./types'); -var c = require('./const'); var f = require('./formatters'); -var displayTypeError = function (type) { - console.error('parser does not support type: ' + type); +/** + * throw incorrect type error + * + * @method throwTypeError + * @param {String} type + * @throws incorrect type error + */ +var throwTypeError = function (type) { + throw new Error('parser does not support type: ' + type); }; -/// This method should be called if we want to check if givent type is an array type -/// @returns true if it is, otherwise false -var arrayType = function (type) { +/** This method should be called if we want to check if givent type is an array type + * + * @method isArrayType + * @param {String} type name + * @returns {Boolean} true if it is, otherwise false + */ +var isArrayType = function (type) { return type.slice(-2) === '[]'; }; +/** + * This method should be called to return dynamic type length in hex + * + * @method dynamicTypeBytes + * @param {String} type + * @param {String|Array} dynamic type + * @return {String} length of dynamic type in hex or empty string if type is not dynamic + */ var dynamicTypeBytes = function (type, value) { // TODO: decide what to do with array of strings - if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + if (isArrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. return f.formatInputInt(value.length); return ""; }; var inputTypes = types.inputTypes(); -/// Formats input params to bytes -/// @param abi contract method inputs -/// @param array of params that will be formatted to bytes -/// @returns bytes representation of input params +/** + * Formats input params to bytes + * + * @method formatInput + * @param {Array} abi inputs of method + * @param {Array} params that will be formatted to bytes + * @returns bytes representation of input params + */ var formatInput = function (inputs, params) { var bytes = ""; var toAppendConstant = ""; @@ -67,12 +90,12 @@ var formatInput = function (inputs, params) { typeMatch = inputTypes[j].type(inputs[i].type, params[i]); } if (!typeMatch) { - displayTypeError(inputs[i].type); + throwTypeError(inputs[i].type); } var formatter = inputTypes[j - 1].format; - if (arrayType(inputs[i].type)) + if (isArrayType(inputs[i].type)) toAppendArrayContent += params[i].reduce(function (acc, curr) { return acc + formatter(curr); }, ""); @@ -87,18 +110,29 @@ var formatInput = function (inputs, params) { return bytes; }; +/** + * This method should be called to predict the length of dynamic type + * + * @method dynamicBytesLength + * @param {String} type + * @returns {Number} length of dynamic type, 0 or multiplication of ETH_PADDING (32) + */ var dynamicBytesLength = function (type) { - if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. + if (isArrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. return c.ETH_PADDING * 2; return 0; }; var outputTypes = types.outputTypes(); -/// Formats output bytes back to param list -/// @param contract abi method outputs -/// @param bytes representtion of output -/// @returns array of output params +/** + * Formats output bytes back to param list + * + * @method formatOutput + * @param {Array} abi outputs of method + * @param {String} bytes represention of output + * @returns {Array} output params + */ var formatOutput = function (outs, output) { output = output.slice(2); @@ -120,11 +154,11 @@ var formatOutput = function (outs, output) { } if (!typeMatch) { - displayTypeError(outs[i].type); + throwTypeError(outs[i].type); } var formatter = outputTypes[j - 1].format; - if (arrayType(outs[i].type)) { + if (isArrayType(outs[i].type)) { var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); dynamicPart = dynamicPart.slice(padding); var array = []; @@ -147,9 +181,14 @@ var formatOutput = function (outs, output) { return result; }; -/// @param json abi for contract -/// @returns input parser object for given json abi -/// TODO: refactor creating the parser, do not double logic from contract +/** + * Should be called to create input parser for contract with given abi + * + * @method inputParser + * @param {Array} contract abi + * @returns {Object} input parser object for given json abi + * TODO: refactor creating the parser, do not double logic from contract + */ var inputParser = function (json) { var parser = {}; json.forEach(function (method) { @@ -171,8 +210,13 @@ var inputParser = function (json) { return parser; }; -/// @param json abi for contract -/// @returns output parser for given json abi +/** + * Should be called to create output parser for contract with given abi + * + * @method outputParser + * @param {Array} contract abi + * @returns {Object} output parser for given json abi + */ var outputParser = function (json) { var parser = {}; json.forEach(function (method) { @@ -201,7 +245,7 @@ module.exports = { formatOutput: formatOutput }; -},{"./const":2,"./formatters":8,"./types":15,"./utils":16}],2:[function(require,module,exports){ +},{"../utils/config":4,"../utils/utils":5,"./formatters":2,"./types":3}],2:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -218,12 +262,322 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file const.js +/** @file formatters.js * @authors: * Marek Kotewicz * @date 2015 */ +if ("build" !== 'build') {/* + var BigNumber = require('bignumber.js'); // jshint ignore:line +*/} + +var utils = require('../utils/utils'); +var c = require('../utils/config'); + +/** + * Should be called to pad string to expected length + * + * @method padLeft + * @param {String} string to be padded + * @param {Number} characters that result string should have + * @param {String} sign, by default 0 + * @returns {String} right aligned string + */ +var padLeft = function (string, chars, sign) { + return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; +}; + +/** + * Formats input value to byte representation of int + * If value is negative, return it's two's complement + * If the value is floating point, round it down + * + * @method formatInputInt + * @param {String|Number|BigNumber} value that needs to be formatted + * @returns {String} right-aligned byte representation of int + */ +var formatInputInt = function (value) { + var padding = c.ETH_PADDING * 2; + BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); + return padLeft(utils.toTwosComplement(value).round().toString(16), padding); +}; + +/** + * Formats input value to byte representation of string + * + * @method formatInputString + * @param {String} + * @returns {String} left-algined byte representation of string + */ +var formatInputString = function (value) { + return utils.fromAscii(value, c.ETH_PADDING).substr(2); +}; + +/** + * Formats input value to byte representation of bool + * + * @method formatInputBool + * @param {Boolean} + * @returns {String} right-aligned byte representation bool + */ +var formatInputBool = function (value) { + return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); +}; + +/** + * Formats input value to byte representation of real + * Values are multiplied by 2^m and encoded as integers + * + * @method formatInputReal + * @param {String|Number|BigNumber} + * @returns {String} byte representation of real + */ +var formatInputReal = function (value) { + return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); +}; + +/** + * Check if input value is negative + * + * @method signedIsNegative + * @param {String} value is hex format + * @returns {Boolean} true if it is negative, otherwise false + */ +var signedIsNegative = function (value) { + return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; +}; + +/** + * Formats right-aligned output bytes to int + * + * @method formatOutputInt + * @param {String} bytes + * @returns {BigNumber} right-aligned output bytes formatted to big number + */ +var formatOutputInt = function (value) { + + value = value || "0"; + + // check if it's negative number + // it it is, return two's complement + if (signedIsNegative(value)) { + return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); + } + return new BigNumber(value, 16); +}; + +/** + * Formats right-aligned output bytes to uint + * + * @method formatOutputUInt + * @param {String} bytes + * @returns {BigNumeber} right-aligned output bytes formatted to uint + */ +var formatOutputUInt = function (value) { + value = value || "0"; + return new BigNumber(value, 16); +}; + +/** + * Formats right-aligned output bytes to real + * + * @method formatOutputReal + * @param {String} + * @returns {BigNumber} input bytes formatted to real + */ +var formatOutputReal = function (value) { + return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/** + * Formats right-aligned output bytes to ureal + * + * @method formatOutputUReal + * @param {String} + * @returns {BigNumber} input bytes formatted to ureal + */ +var formatOutputUReal = function (value) { + return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); +}; + +/** + * Should be used to format output hash + * + * @method formatOutputHash + * @param {String} + * @returns {String} right-aligned output bytes formatted to hex + */ +var formatOutputHash = function (value) { + return "0x" + value; +}; + +/** + * Should be used to format output bool + * + * @method formatOutputBool + * @param {String} + * @returns {Boolean} right-aligned input bytes formatted to bool + */ +var formatOutputBool = function (value) { + return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; +}; + +/** + * Should be used to format output string + * + * @method formatOutputString + * @param {Sttring} left-aligned hex representation of string + * @returns {String} ascii string + */ +var formatOutputString = function (value) { + return utils.toAscii(value); +}; + +/** + * Should be used to format output address + * + * @method formatOutputAddress + * @param {String} right-aligned input bytes + * @returns {String} address + */ +var formatOutputAddress = function (value) { + return "0x" + value.slice(value.length - 40, value.length); +}; + +module.exports = { + formatInputInt: formatInputInt, + formatInputString: formatInputString, + formatInputBool: formatInputBool, + formatInputReal: formatInputReal, + formatOutputInt: formatOutputInt, + formatOutputUInt: formatOutputUInt, + formatOutputReal: formatOutputReal, + formatOutputUReal: formatOutputUReal, + formatOutputHash: formatOutputHash, + formatOutputBool: formatOutputBool, + formatOutputString: formatOutputString, + formatOutputAddress: formatOutputAddress +}; + + +},{"../utils/config":4,"../utils/utils":5}],3:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file types.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var f = require('./formatters'); + +/// @param expected type prefix (string) +/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false +var prefixedType = function (prefix) { + return function (type) { + return type.indexOf(prefix) === 0; + }; +}; + +/// @param expected type name (string) +/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false +var namedType = function (name) { + return function (type) { + return name === type; + }; +}; + +/// Setups input formatters for solidity types +/// @returns an array of input formatters +var inputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatInputInt }, + { type: prefixedType('int'), format: f.formatInputInt }, + { type: prefixedType('hash'), format: f.formatInputInt }, + { type: prefixedType('string'), format: f.formatInputString }, + { type: prefixedType('real'), format: f.formatInputReal }, + { type: prefixedType('ureal'), format: f.formatInputReal }, + { type: namedType('address'), format: f.formatInputInt }, + { type: namedType('bool'), format: f.formatInputBool } + ]; +}; + +/// Setups output formaters for solidity types +/// @returns an array of output formatters +var outputTypes = function () { + + return [ + { type: prefixedType('uint'), format: f.formatOutputUInt }, + { type: prefixedType('int'), format: f.formatOutputInt }, + { type: prefixedType('hash'), format: f.formatOutputHash }, + { type: prefixedType('string'), format: f.formatOutputString }, + { type: prefixedType('real'), format: f.formatOutputReal }, + { type: prefixedType('ureal'), format: f.formatOutputUReal }, + { type: namedType('address'), format: f.formatOutputAddress }, + { type: namedType('bool'), format: f.formatOutputBool } + ]; +}; + +module.exports = { + prefixedType: prefixedType, + namedType: namedType, + inputTypes: inputTypes, + outputTypes: outputTypes +}; + + +},{"./formatters":2}],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 config.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/** + * Utils + * + * @module utils + */ + +/** + * Utility functions + * + * @class [utils] config + * @constructor + */ + /// required to define ETH_BIGNUMBER_ROUNDING_MODE if ("build" !== 'build') {/* var BigNumber = require('bignumber.js'); // jshint ignore:line @@ -256,11 +610,713 @@ module.exports = { ETH_SIGNATURE_LENGTH: 4, ETH_UNITS: ETH_UNITS, ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, - ETH_POLLING_TIMEOUT: 1000 + ETH_POLLING_TIMEOUT: 1000, + ETH_DEFAULTBLOCK: 'latest' }; -},{}],3:[function(require,module,exports){ +},{}],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 utils.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/** + * Utils + * + * @module utils + */ + +/** + * Utility functions + * + * @class [utils] utils + * @constructor + */ + +if ("build" !== 'build') {/* + var BigNumber = require('bignumber.js'); // jshint ignore:line +*/} + +var unitMap = { + 'wei': '1', + 'kwei': '1000', + 'ada': '1000', + 'mwei': '1000000', + 'babbage': '1000000', + 'gwei': '1000000000', + 'shannon': '1000000000', + 'szabo': '1000000000000', + 'finney': '1000000000000000', + 'ether': '1000000000000000000', + 'kether': '1000000000000000000000', + 'grand': '1000000000000000000000', + 'einstein': '1000000000000000000000', + 'mether': '1000000000000000000000000', + 'gether': '1000000000000000000000000000', + 'tether': '1000000000000000000000000000000' +}; + + +/** Finds first index of array element matching pattern + * + * @method findIndex + * @param {Array} + * @param {Function} pattern + * @returns {Number} index of element + */ +var findIndex = function (array, callback) { + var end = false; + var i = 0; + for (; i < array.length && !end; i++) { + end = callback(array[i]); + } + return end ? i - 1 : -1; +}; + +/** + * Should be called to get sting from it's hex representation + * + * @method toAscii + * @param {String} string in hex + * @returns {String} ascii string representation of hex value + */ +var toAscii = function(hex) { +// Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') { + i = 2; + } + for (; i < l; i+=2) { + var code = parseInt(hex.substr(i, 2), 16); + if (code === 0) { + break; + } + + str += String.fromCharCode(code); + } + + return str; +}; + +/** + * Shold be called to get hex representation (prefixed by 0x) of ascii string + * + * @method fromAscii + * @param {String} string + * @returns {String} hex representation of input string + */ +var toHexNative = function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var n = str.charCodeAt(i).toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return hex; +}; + +/** + * Shold be called to get hex representation (prefixed by 0x) of ascii string + * + * @method fromAscii + * @param {String} string + * @param {Number} optional padding + * @returns {String} hex representation of input string + */ +var fromAscii = function(str, pad) { + pad = pad === undefined ? 0 : pad; + var hex = toHexNative(str); + while (hex.length < pad*2) + hex += "00"; + return "0x" + hex; +}; + +/** + * Should be called to get display name of contract function + * + * @method extractDisplayName + * @param {String} name of function/event + * @returns {String} display name for function/event eg. multiply(uint256) -> multiply + */ +var extractDisplayName = function (name) { + var length = name.indexOf('('); + return length !== -1 ? name.substr(0, length) : name; +}; + +/// @returns overloaded part of function/event name +var extractTypeName = function (name) { + /// TODO: make it invulnerable + var length = name.indexOf('('); + return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; +}; + +/** + * Filters all functions from input abi + * + * @method filterFunctions + * @param {Array} abi + * @returns {Array} abi array with filtered objects of type 'function' + */ +var filterFunctions = function (json) { + return json.filter(function (current) { + return current.type === 'function'; + }); +}; + +/** + * Filters all events from input abi + * + * @method filterEvents + * @param {Array} abi + * @returns {Array} abi array with filtered objects of type 'event' + */ +var filterEvents = function (json) { + return json.filter(function (current) { + return current.type === 'event'; + }); +}; + +/** + * Converts value to it's decimal representation in string + * + * @method toDecimal + * @param {String|Number|BigNumber} + * @return {String} + */ +var toDecimal = function (value) { + return toBigNumber(value).toNumber(); +}; + +/** + * Converts value to it's hex representation + * + * @method fromDecimal + * @param {String|Number|BigNumber} + * @return {String} + */ +var fromDecimal = function (value) { + var number = toBigNumber(value); + var result = number.toString(16); + + return (number.lessThan(0)) + ? '-0x' + result.substr(1) + : '0x' + result; +}; + +/** + * Auto converts any given value into it's hex representation. + * + * And even stringifys objects before. + * + * @method toHex + * @param {String|Number|BigNumber|Object} + * @return {String} + */ +var toHex = function (val) { + /*jshint maxcomplexity:5 */ + + if(typeof val === 'boolean') + return val; + + if(isBigNumber(val)) + return fromDecimal(val); + + if(typeof val === 'object') + return fromAscii(JSON.stringify(val)); + + if(isString(val) && val.indexOf('0x') === 0) + return val; + // if its a negative number, pass it through fromDecimal + if(isString(val) && val.indexOf('-0x') === 0) + return fromDecimal(val); + + if(isString(val) && !isFinite(val)) + return fromAscii(val); + + if(isFinite(val)) + return fromDecimal(val); + + return val; +}; + +/** + * Returns value of unit in Wei + * + * @method getValueOfUnit + * @param {String} unit the unit to convert to, default ether + * @returns {BigNumber} value of the unit (in Wei) + * @throws error if the unit is not correct:w + */ +var getValueOfUnit = function (unit) { + unit = unit ? unit.toLowerCase() : 'ether'; + var unitValue = unitMap[unit]; + if (unitValue === undefined) { + throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2)); + } + return new BigNumber(unitValue, 10); +}; + +/** + * Takes a number of wei and converts it to any other ether unit. + * + * Possible units are: + * - kwei/ada + * - mwei/babbage + * - gwei/shannon + * - szabo + * - finney + * - ether + * - kether/grand/einstein + * - mether + * - gether + * - tether + * + * @method fromWei + * @param {Number|String} number can be a number, number string or a HEX of a decimal + * @param {String} unit the unit to convert to, default ether + * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number +*/ +var fromWei = function(number, unit) { + var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); + + return (isBigNumber(number)) + ? returnValue : returnValue.toString(10); +}; + +/** + * Takes a number of a unit and converts it to wei. + * + * Possible units are: + * - kwei/ada + * - mwei/babbage + * - gwei/shannon + * - szabo + * - finney + * - ether + * - kether/grand/einstein + * - mether + * - gether + * - tether + * + * @method toWei + * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal + * @param {String} unit the unit to convert from, default ether + * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number +*/ +var toWei = function(number, unit) { + var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); + + return (isBigNumber(number)) + ? returnValue : returnValue.toString(10); +}; + +/** + * Takes an input and transforms it into an bignumber + * + * @method toBigNumber + * @param {Number|String|BigNumber} a number, string, HEX string or BigNumber + * @return {BigNumber} BigNumber +*/ +var toBigNumber = function(number) { + number = number || 0; + if (isBigNumber(number)) + return number; + + return (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) + ? new BigNumber(number.replace('0x',''), 16) + : new BigNumber(number.toString(10), 10); +}; + +/** + * Takes and input transforms it into bignumber and if it is negative value, into two's complement + * + * @method toTwosComplement + * @param {Number|String|BigNumber} + * @return {BigNumber} + */ +var toTwosComplement = function (number) { + var bigNumber = toBigNumber(number); + if (bigNumber.lessThan(0)) { + return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1); + } + return bigNumber; +}; + +/** + * Checks if the given string has proper length + * + * @method isAddress + * @param {String} address the given HEX adress + * @return {Boolean} +*/ +var isAddress = function(address) { + if (!isString(address)) { + return false; + } + + return ((address.indexOf('0x') === 0 && address.length === 42) || + (address.indexOf('0x') === -1 && address.length === 40)); +}; + +/** + * Returns true if object is BigNumber, otherwise false + * + * @method isBigNumber + * @param {Object} + * @return {Boolean} + */ +var isBigNumber = function (object) { + return object instanceof BigNumber || + (object && object.constructor && object.constructor.name === 'BigNumber'); +}; + +/** + * Returns true if object is string, otherwise false + * + * @method isString + * @param {Object} + * @return {Boolean} + */ +var isString = function (object) { + return typeof object === 'string' || + (object && object.constructor && object.constructor.name === 'String'); +}; + +/** + * Returns true if object is function, otherwise false + * + * @method isFunction + * @param {Object} + * @return {Boolean} + */ +var isFunction = function (object) { + return typeof object === 'function'; +}; + +module.exports = { + findIndex: findIndex, + toHex: toHex, + toDecimal: toDecimal, + fromDecimal: fromDecimal, + toAscii: toAscii, + fromAscii: fromAscii, + extractDisplayName: extractDisplayName, + extractTypeName: extractTypeName, + filterFunctions: filterFunctions, + filterEvents: filterEvents, + toWei: toWei, + fromWei: fromWei, + toBigNumber: toBigNumber, + toTwosComplement: toTwosComplement, + isBigNumber: isBigNumber, + isAddress: isAddress, + isFunction: isFunction, + isString: isString +}; + + +},{}],6:[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 web3.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Gav Wood + * @date 2014 + */ + +var net = require('./web3/net'); +var eth = require('./web3/eth'); +var db = require('./web3/db'); +var shh = require('./web3/shh'); +var watches = require('./web3/watches'); +var filter = require('./web3/filter'); +var utils = require('./utils/utils'); +var formatters = require('./solidity/formatters'); +var requestManager = require('./web3/requestmanager'); +var c = require('./utils/config'); + +/// @returns an array of objects describing web3 api methods +var web3Methods = function () { + return [ + { name: 'sha3', call: 'web3_sha3' } + ]; +}; + +/// creates methods in a given object based on method description on input +/// setups api calls for these methods +var setupMethods = function (obj, methods) { + methods.forEach(function (method) { + // allow for object methods 'myObject.method' + var objectMethods = method.name.split('.'), + callFunction = function () { + /*jshint maxcomplexity:8 */ + + var callback = null, + args = Array.prototype.slice.call(arguments), + call = typeof method.call === 'function' ? method.call(args) : method.call; + + // get the callback if one is available + if(typeof args[args.length-1] === 'function'){ + callback = args[args.length-1]; + Array.prototype.pop.call(args); + } + + // add the defaultBlock if not given + if(method.addDefaultblock) { + if(args.length !== method.addDefaultblock) + Array.prototype.push.call(args, (isFinite(c.ETH_DEFAULTBLOCK) ? utils.fromDecimal(c.ETH_DEFAULTBLOCK) : c.ETH_DEFAULTBLOCK)); + else + args[args.length-1] = isFinite(args[args.length-1]) ? utils.fromDecimal(args[args.length-1]) : args[args.length-1]; + } + + // show deprecated warning + if(method.newMethod) + console.warn('This method is deprecated please use web3.'+ method.newMethod +'() instead.'); + + return web3.manager.send({ + method: call, + params: args, + outputFormatter: method.outputFormatter, + inputFormatter: method.inputFormatter, + addDefaultblock: method.addDefaultblock + }, callback); + }; + + if(objectMethods.length > 1) { + if(!obj[objectMethods[0]]) + obj[objectMethods[0]] = {}; + + obj[objectMethods[0]][objectMethods[1]] = callFunction; + + } else { + + obj[objectMethods[0]] = callFunction; + } + + }); +}; + +/// creates properties in a given object based on properties description on input +/// setups api calls for these properties +var setupProperties = function (obj, properties) { + properties.forEach(function (property) { + var proto = {}; + proto.get = function () { + + // show deprecated warning + if(property.newProperty) + console.warn('This property is deprecated please use web3.'+ property.newProperty +' instead.'); + + + return web3.manager.send({ + method: property.getter, + outputFormatter: property.outputFormatter + }); + }; + + if (property.setter) { + proto.set = function (val) { + + // show deprecated warning + if(property.newProperty) + console.warn('This property is deprecated please use web3.'+ property.newProperty +' instead.'); + + return web3.manager.send({ + method: property.setter, + params: [val], + inputFormatter: property.inputFormatter + }); + }; + } + + proto.enumerable = !property.newProperty; + Object.defineProperty(obj, property.name, proto); + + }); +}; + +/*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_getFilterChanges'), + stopPolling: stopPolling +}; + +var shhWatch = { + startPolling: startPolling.bind(null, 'shh_getFilterChanges'), + stopPolling: stopPolling +}; + +/// setups web3 object, and it's in-browser executed methods +var web3 = { + manager: requestManager(), + providers: {}, + + 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(); + }, + + /// @returns hex string of the input + toHex: utils.toHex, + + /// @returns ascii string representation of hex value prefixed with 0x + toAscii: utils.toAscii, + + /// @returns hex representation (prefixed by 0x) of ascii string + fromAscii: utils.fromAscii, + + /// @returns decimal representaton of hex value prefixed by 0x + toDecimal: utils.toDecimal, + + /// @returns hex representation (prefixed by 0x) of decimal value + fromDecimal: utils.fromDecimal, + + /// @returns a BigNumber object + toBigNumber: utils.toBigNumber, + + toWei: utils.toWei, + fromWei: utils.fromWei, + isAddress: utils.isAddress, + + // provide network information + net: { + // peerCount: + }, + + + /// eth object prototype + eth: { + // DEPRECATED + contractFromAbi: function (abi) { + console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); + + return function(addr) { + // Default to address of Config. TODO: rremove prior to genesis. + addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; + var ret = web3.eth.contract(addr, abi); + ret.address = addr; + return ret; + }; + }, + + /// @param filter may be a string, object or event + /// @param eventParams is optional, this is an object with optional event eventParams params + /// @param options is optional, this is an object with optional event options ('max'...) + /*jshint maxparams:4 */ + filter: function (fil, eventParams, options) { + + // if its event, treat it differently + if (fil._isEvent) + return fil(eventParams, options); + + return filter(fil, ethWatch, formatters.outputLogFormatter); + }, + // DEPRECATED + watch: function (fil, eventParams, options) { + console.warn('eth.watch() is deprecated please use eth.filter() instead.'); + return this.filter(fil, eventParams, options); + } + /*jshint maxparams:3 */ + }, + + /// db object prototype + db: {}, + + /// shh object prototype + shh: { + /// @param filter may be a string, object or event + filter: function (fil) { + return filter(fil, shhWatch, formatters.outputPostFormatter); + }, + // DEPRECATED + watch: function (fil) { + console.warn('shh.watch() is deprecated please use shh.filter() instead.'); + return this.filter(fil); + } + } +}; + + +// ADD defaultblock +Object.defineProperty(web3.eth, 'defaultBlock', { + get: function () { + return c.ETH_DEFAULTBLOCK; + }, + set: function (val) { + c.ETH_DEFAULTBLOCK = val; + return c.ETH_DEFAULTBLOCK; + } +}); + + +/// setups all api methods +setupMethods(web3, web3Methods()); +setupMethods(web3.net, net.methods); +setupProperties(web3.net, net.properties); +setupMethods(web3.eth, eth.methods); +setupProperties(web3.eth, eth.properties); +setupMethods(web3.db, db.methods()); +setupMethods(web3.shh, shh.methods()); +setupMethods(ethWatch, watches.eth()); +setupMethods(shhWatch, watches.shh()); + +module.exports = web3; + + +},{"./solidity/formatters":2,"./utils/config":4,"./utils/utils":5,"./web3/db":8,"./web3/eth":9,"./web3/filter":11,"./web3/net":15,"./web3/requestmanager":17,"./web3/shh":18,"./web3/watches":20}],7:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -283,9 +1339,9 @@ module.exports = { * @date 2014 */ -var web3 = require('./web3'); -var abi = require('./abi'); -var utils = require('./utils'); +var web3 = require('../web3'); +var abi = require('../solidity/abi'); +var utils = require('../utils/utils'); var eventImpl = require('./event'); var signature = require('./signature'); @@ -505,7 +1561,7 @@ function Contract(abi, address) { module.exports = contract; -},{"./abi":1,"./event":6,"./signature":14,"./utils":16,"./web3":18}],4:[function(require,module,exports){ +},{"../solidity/abi":1,"../utils/utils":5,"../web3":6,"./event":10,"./signature":19}],8:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -542,7 +1598,7 @@ module.exports = { methods: methods }; -},{}],5:[function(require,module,exports){ +},{}],9:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -565,81 +1621,124 @@ module.exports = { * @date 2015 */ +/** + * Web3 + * + * @module web3 + */ + +/** + * Eth methods and properties + * + * An example method object can look as follows: + * + * { + * name: 'getBlock', + * call: blockCall, + * outputFormatter: formatters.outputBlockFormatter, + * inputFormatter: [ // can be a formatter funciton or an array of functions. Where each item in the array will be used for one parameter + * utils.toHex, // formats paramter 1 + * function(param){ if(!param) return false; } // formats paramter 2 + * ] + * }, + * + * @class [web3] eth + * @constructor + */ + + var formatters = require('./formatters'); +var utils = require('../utils/utils'); var blockCall = function (args) { - return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; }; -var transactionCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; +var transactionFromBlockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; }; var uncleCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex'; }; -var transactionCountCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionCountByHash' : 'eth_transactionCountByNumber'; +var getBlockTransactionCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber'; }; var uncleCountCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleCountByHash' : 'eth_uncleCountByNumber'; + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber'; }; /// @returns an array of objects describing web3.eth api methods var methods = [ - { name: 'getBalance', call: 'eth_balanceAt', outputFormatter: formatters.convertToBigNumber}, - { name: 'getState', call: 'eth_stateAt' }, - { name: 'getStorage', call: 'eth_storageAt' }, - { name: 'getData', call: 'eth_codeAt' }, - { name: 'getBlock', call: blockCall, outputFormatter: formatters.outputBlockFormatter}, - { name: 'getUncle', call: uncleCall, outputFormatter: formatters.outputBlockFormatter}, - { name: 'getCompilers', call: 'eth_compilers' }, - { name: 'getBlockTransactionCount', call: transactionCountCall }, - { name: 'getBlockUncleCount', call: uncleCountCall }, - { name: 'getTransaction', call: transactionCall, outputFormatter: formatters.outputTransactionFormatter }, - { name: 'getTransactionCount', call: 'eth_countAt'}, - { name: 'sendTransaction', call: 'eth_transact', inputFormatter: formatters.inputTransactionFormatter }, - { name: 'call', call: 'eth_call' }, - { name: 'compile.solidity', call: 'eth_solidity' }, - { name: 'compile.lll', call: 'eth_lll' }, - { name: 'compile.serpent', call: 'eth_serpent' }, + { name: 'getBalance', call: 'eth_getBalance', addDefaultblock: 2, + outputFormatter: formatters.convertToBigNumber}, + { name: 'getStorage', call: 'eth_getStorage', addDefaultblock: 2}, + { name: 'getStorageAt', call: 'eth_getStorageAt', addDefaultblock: 3, + inputFormatter: utils.toHex}, + { name: 'getData', call: 'eth_getData', addDefaultblock: 2}, + { name: 'getBlock', call: blockCall, + outputFormatter: formatters.outputBlockFormatter, + inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]}, + { name: 'getUncle', call: uncleCall, + outputFormatter: formatters.outputBlockFormatter, + inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]}, + { name: 'getCompilers', call: 'eth_getCompilers' }, + { name: 'getBlockTransactionCount', call: getBlockTransactionCountCall, + outputFormatter: utils.toDecimal, + inputFormatter: utils.toHex }, + { name: 'getBlockUncleCount', call: uncleCountCall, + outputFormatter: utils.toDecimal, + inputFormatter: utils.toHex }, + { name: 'getTransaction', call: 'eth_getTransactionByHash', + outputFormatter: formatters.outputTransactionFormatter }, + { name: 'getTransactionFromBlock', call: transactionFromBlockCall, + outputFormatter: formatters.outputTransactionFormatter, + inputFormatter: utils.toHex }, + { name: 'getTransactionCount', call: 'eth_getTransactionCount', addDefaultblock: 2, + outputFormatter: utils.toDecimal}, + { name: 'sendTransaction', call: 'eth_sendTransaction', + inputFormatter: formatters.inputTransactionFormatter }, + { name: 'call', call: 'eth_call', addDefaultblock: 2, + inputFormatter: formatters.inputCallFormatter }, + { name: 'compile.solidity', call: 'eth_compileSolidity' }, + { name: 'compile.lll', call: 'eth_compileLLL' }, + { name: 'compile.serpent', call: 'eth_compileSerpent' }, { name: 'flush', call: 'eth_flush' }, // deprecated methods - { name: 'balanceAt', call: 'eth_balanceAt', newMethod: 'getBalance' }, - { name: 'stateAt', call: 'eth_stateAt', newMethod: 'getState' }, - { name: 'storageAt', call: 'eth_storageAt', newMethod: 'getStorage' }, - { name: 'countAt', call: 'eth_countAt', newMethod: 'getTransactionCount' }, - { name: 'codeAt', call: 'eth_codeAt', newMethod: 'getData' }, - { name: 'transact', call: 'eth_transact', newMethod: 'sendTransaction' }, - { name: 'block', call: blockCall, newMethod: 'getBlock' }, - { name: 'transaction', call: transactionCall, newMethod: 'getTransaction' }, - { name: 'uncle', call: uncleCall, newMethod: 'getUncle' }, - { name: 'compilers', call: 'eth_compilers', newMethod: 'getCompilers' }, - { name: 'solidity', call: 'eth_solidity', newMethod: 'compile.solidity' }, - { name: 'lll', call: 'eth_lll', newMethod: 'compile.lll' }, - { name: 'serpent', call: 'eth_serpent', newMethod: 'compile.serpent' }, - { name: 'transactionCount', call: transactionCountCall, newMethod: 'getBlockTransactionCount' }, - { name: 'uncleCount', call: uncleCountCall, newMethod: 'getBlockUncleCount' }, + { name: 'balanceAt', call: 'eth_balanceAt', newMethod: 'eth.getBalance' }, + { name: 'stateAt', call: 'eth_stateAt', newMethod: 'eth.getStorageAt' }, + { name: 'storageAt', call: 'eth_storageAt', newMethod: 'eth.getStorage' }, + { name: 'countAt', call: 'eth_countAt', newMethod: 'eth.getTransactionCount' }, + { name: 'codeAt', call: 'eth_codeAt', newMethod: 'eth.getData' }, + { name: 'transact', call: 'eth_transact', newMethod: 'eth.sendTransaction' }, + { name: 'block', call: blockCall, newMethod: 'eth.getBlock' }, + { name: 'transaction', call: transactionFromBlockCall, newMethod: 'eth.getTransaction' }, + { name: 'uncle', call: uncleCall, newMethod: 'eth.getUncle' }, + { name: 'compilers', call: 'eth_compilers', newMethod: 'eth.getCompilers' }, + { name: 'solidity', call: 'eth_solidity', newMethod: 'eth.compile.solidity' }, + { name: 'lll', call: 'eth_lll', newMethod: 'eth.compile.lll' }, + { name: 'serpent', call: 'eth_serpent', newMethod: 'eth.compile.serpent' }, + { name: 'transactionCount', call: getBlockTransactionCountCall, newMethod: 'eth.getBlockTransactionCount' }, + { name: 'uncleCount', call: uncleCountCall, newMethod: 'eth.getBlockUncleCount' }, { name: 'logs', call: 'eth_logs' } ]; /// @returns an array of objects describing web3.eth api properties var properties = [ - { 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: 'coinbase', getter: 'eth_coinbase'}, + { name: 'mining', getter: 'eth_mining'}, { name: 'gasPrice', getter: 'eth_gasPrice', outputFormatter: formatters.convertToBigNumber}, { name: 'accounts', getter: 'eth_accounts' }, - { name: 'peerCount', getter: 'eth_peerCount' }, - { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, - { name: 'blockNumber', getter: 'eth_number'}, + { name: 'blockNumber', getter: 'eth_blockNumber', outputFormatter: utils.toDecimal}, // deprecated properties - { name: 'number', getter: 'eth_number', newProperty: 'blockNumber'} + { name: 'listening', getter: 'net_listening', setter: 'eth_setListening', newProperty: 'net.listening'}, + { name: 'peerCount', getter: 'net_peerCount', newProperty: 'net.peerCount'}, + { name: 'number', getter: 'eth_number', newProperty: 'eth.blockNumber'} ]; @@ -649,7 +1748,7 @@ module.exports = { }; -},{"./formatters":8}],6:[function(require,module,exports){ +},{"../utils/utils":5,"./formatters":12}],10:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -672,8 +1771,8 @@ module.exports = { * @date 2014 */ -var abi = require('./abi'); -var utils = require('./utils'); +var abi = require('../solidity/abi'); +var utils = require('../utils/utils'); var signature = require('./signature'); /// filter inputs array && returns only indexed (or not) inputs @@ -789,7 +1888,7 @@ module.exports = { }; -},{"./abi":1,"./signature":14,"./utils":16}],7:[function(require,module,exports){ +},{"../solidity/abi":1,"../utils/utils":5,"./signature":19}],11:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -815,6 +1914,8 @@ module.exports = { * @date 2014 */ +var utils = require('../utils/utils'); + /// Should be called to check if filter implementation is valid /// @returns true if it is, otherwise false var implementationIsValid = function (i) { @@ -830,16 +1931,25 @@ var implementationIsValid = function (i) { /// @param should be string or object /// @returns options string or object var getOptions = function (options) { + /*jshint maxcomplexity:5 */ + if (typeof options === 'string') { return options; } options = options || {}; - if (options.topics) { + if (options.topics) console.warn('"topics" is deprecated, is "topic" instead'); + + // make sure topics, get converted to hex + if(options.topic instanceof Array) { + options.topic = options.topic.map(function(topic){ + return utils.toHex(topic); + }); } + // evaluate lazy properties return { to: options.to, @@ -866,6 +1976,8 @@ var filter = function(options, implementation, formatter) { options = getOptions(options); var callbacks = []; var filterId = implementation.newFilter(options); + + // call the callbacks var onMessages = function (messages) { messages.forEach(function (message) { message = formatter ? formatter(message) : message; @@ -927,7 +2039,7 @@ var filter = function(options, implementation, formatter) { module.exports = filter; -},{}],8:[function(require,module,exports){ +},{"../utils/utils":5}],12:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -947,286 +2059,188 @@ module.exports = filter; /** @file formatters.js * @authors: * Marek Kotewicz + * Fabian Vogelsteller * @date 2015 */ -if ("build" !== 'build') {/* - var BigNumber = require('bignumber.js'); // jshint ignore:line -*/} - -var utils = require('./utils'); -var c = require('./const'); - -/// @param string string to be padded -/// @param number of characters that result string should have -/// @param sign, by default 0 -/// @returns right aligned string -var padLeft = function (string, chars, sign) { - return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; -}; - -/// Formats input value to byte representation of int -/// If value is negative, return it's two's complement -/// If the value is floating point, round it down -/// @returns right-aligned byte representation of int -var formatInputInt = function (value) { - /*jshint maxcomplexity:7 */ - var padding = c.ETH_PADDING * 2; - if (utils.isBigNumber(value) || typeof value === 'number') { - if (typeof value === 'number') - value = new BigNumber(value); - BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); - value = value.round(); - - if (value.lessThan(0)) - value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1); - value = value.toString(16); - } - else if (typeof value === 'string') { - if (value.indexOf('0x') === 0) { - value = value.substr(2); - } else { - value = formatInputInt(new BigNumber(value)); - } - } - else - value = (+value).toString(16); - return padLeft(value, padding); -}; - -/// Formats input value to byte representation of string -/// @returns left-algined byte representation of string -var formatInputString = function (value) { - return utils.fromAscii(value, c.ETH_PADDING).substr(2); -}; - -/// Formats input value to byte representation of bool -/// @returns right-aligned byte representation bool -var formatInputBool = function (value) { - return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); -}; - -/// Formats input value to byte representation of real -/// Values are multiplied by 2^m and encoded as integers -/// @returns byte representation of real -var formatInputReal = function (value) { - return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); -}; - - -/// Check if input value is negative -/// @param value is hex format -/// @returns true if it is negative, otherwise false -var signedIsNegative = function (value) { - return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; -}; - -/// Formats input right-aligned input bytes to int -/// @returns right-aligned input bytes formatted to int -var formatOutputInt = function (value) { - - value = value || "0"; - - // check if it's negative number - // it it is, return two's complement - if (signedIsNegative(value)) { - return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); - } - return new BigNumber(value, 16); -}; - - -/// Formats big right-aligned input bytes to uint -/// @returns right-aligned input bytes formatted to uint -var formatOutputUInt = function (value) { - value = value || "0"; - return new BigNumber(value, 16); -}; - -/// @returns input bytes formatted to real -var formatOutputReal = function (value) { - return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/// @returns input bytes formatted to ureal -var formatOutputUReal = function (value) { - return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); -}; - -/// @returns right-aligned input bytes formatted to hex -var formatOutputHash = function (value) { - return "0x" + value; -}; - -/// @returns right-aligned input bytes formatted to bool -var formatOutputBool = function (value) { - return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; -}; - -/// @returns left-aligned input bytes formatted to ascii string -var formatOutputString = function (value) { - return utils.toAscii(value); -}; - -/// @returns right-aligned input bytes formatted to address -var formatOutputAddress = function (value) { - return "0x" + value.slice(value.length - 40, value.length); -}; - - -/// Formats the input to a big number -/// @returns a BigNumber object -var convertToBigNumber = function (value) { - - // remove the leading 0x - if(typeof value === 'string') - value = value.replace('0x', ''); - - value = value || "0"; - - return new BigNumber(value, 16); -}; - +var utils = require('../utils/utils'); /** -Formats the input of a transaction and converts all values to HEX + * Should the input to a big number + * + * @method convertToBigNumber + * @param {String|Number|BigNumber} + * @returns {BigNumber} object + */ +var convertToBigNumber = function (value) { + return utils.toBigNumber(value); +}; -@returns object +/** + * Formats the input of a transaction and converts all values to HEX + * + * @method inputTransactionFormatter + * @param {Object} transaction options + * @returns object */ -var inputTransactionFormatter = function(options){ +var inputTransactionFormatter = function (options){ // make code -> data - if(options.code) { + if (options.code) { options.data = options.code; delete options.code; } - // make endowment -> value - if(options.endowment) { - options.value = options.endowment; - delete options.endowment; - } - - - // format the following options - /*jshint maxcomplexity:5 */ - ['gasPrice', 'value'].forEach(function(key){ - - // if hex or string integer - if(typeof options[key] === 'string') { - - // if not hex assume its a number string - if(options[key].indexOf('0x') === -1) - options[key] = utils.fromDecimal(options[key]); - - // if number - } else if(typeof options[key] === 'number') { - options[key] = utils.fromDecimal(options[key]); - - // if bignumber - } else if(options[key] instanceof BigNumber) { - options[key] = '0x'+ options[key].toString(16); - } + ['gasPrice', 'gas', 'value'].forEach(function(key){ + options[key] = utils.fromDecimal(options[key]); }); - // format gas to number - options.gas = Number(options.gas); - - return options; }; /** -Formats the output of a transaction to its proper values - -@returns object + * Formats the output of a transaction to its proper values + * + * @method outputTransactionFormatter + * @param {Object} transaction + * @returns {Object} transaction */ -var outputTransactionFormatter = function(tx){ - // transform to number - tx.gas = Number(tx.gas); - - // gasPrice to bignumber - if(typeof tx.gasPrice === 'string' && tx.gasPrice.indexOf('0x') === 0) - tx.gasPrice = new BigNumber(tx.gasPrice, 16); - else - tx.gasPrice = new BigNumber(tx.gasPrice.toString(10), 10); - - // value to bignumber - if(typeof tx.value === 'string' && tx.value.indexOf('0x') === 0) - tx.value = new BigNumber(tx.value, 16); - else - tx.value = new BigNumber(tx.value.toString(10), 10); - +var outputTransactionFormatter = function (tx){ + tx.gas = utils.toDecimal(tx.gas); + tx.gasPrice = utils.toBigNumber(tx.gasPrice); + tx.value = utils.toBigNumber(tx.value); return tx; }; +/** + * Formats the input of a call and converts all values to HEX + * + * @method inputCallFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputCallFormatter = function (options){ + + // make code -> data + if (options.code) { + options.data = options.code; + delete options.code; + } + + return options; +}; + /** -Formats the output of a block to its proper values - -@returns object + * Formats the output of a block to its proper values + * + * @method outputBlockFormatter + * @param {Object} block object + * @returns {Object} block object */ var outputBlockFormatter = function(block){ - /*jshint maxcomplexity:7 */ // transform to number - block.gasLimit = Number(block.gasLimit); - block.gasUsed = Number(block.gasUsed); - block.size = Number(block.size); - block.timestamp = Number(block.timestamp); - block.number = Number(block.number); + block.gasLimit = utils.toDecimal(block.gasLimit); + block.gasUsed = utils.toDecimal(block.gasUsed); + block.size = utils.toDecimal(block.size); + block.timestamp = utils.toDecimal(block.timestamp); + block.number = utils.toDecimal(block.number); - // minGasPrice to bignumber - if(block.minGasPrice) { - if(typeof block.minGasPrice === 'string' && block.minGasPrice.indexOf('0x') === 0) - block.minGasPrice = new BigNumber(block.minGasPrice, 16); - else - block.minGasPrice = new BigNumber(block.minGasPrice.toString(10), 10); - } + block.minGasPrice = utils.toBigNumber(block.minGasPrice); + block.difficulty = utils.toBigNumber(block.difficulty); + block.totalDifficulty = utils.toBigNumber(block.totalDifficulty); - - // difficulty to bignumber - if(block.difficulty) { - if(typeof block.difficulty === 'string' && block.difficulty.indexOf('0x') === 0) - block.difficulty = new BigNumber(block.difficulty, 16); - else - block.difficulty = new BigNumber(block.difficulty.toString(10), 10); - } - - - // difficulty to bignumber - if(block.totalDifficulty) { - if(typeof block.totalDifficulty === 'string' && block.totalDifficulty.indexOf('0x') === 0) - block.totalDifficulty = new BigNumber(block.totalDifficulty, 16); - else - block.totalDifficulty = new BigNumber(block.totalDifficulty.toString(10), 10); + if(block.transactions instanceof Array) { + block.transactions.forEach(function(item){ + if(!utils.isString(item)) + return outputTransactionFormatter(item); + }); } return block; }; - -module.exports = { - formatInputInt: formatInputInt, - formatInputString: formatInputString, - formatInputBool: formatInputBool, - formatInputReal: formatInputReal, - formatOutputInt: formatOutputInt, - formatOutputUInt: formatOutputUInt, - formatOutputReal: formatOutputReal, - formatOutputUReal: formatOutputUReal, - formatOutputHash: formatOutputHash, - formatOutputBool: formatOutputBool, - formatOutputString: formatOutputString, - formatOutputAddress: formatOutputAddress, - convertToBigNumber: convertToBigNumber, - inputTransactionFormatter: inputTransactionFormatter, - outputTransactionFormatter: outputTransactionFormatter, - outputBlockFormatter: outputBlockFormatter +/** + * Formats the output of a log + * + * @method outputLogFormatter + * @param {Object} log object + * @returns {Object} log +*/ +var outputLogFormatter = function(log){ + log.number = utils.toDecimal(log.number); + return log; }; -},{"./const":2,"./utils":16}],9:[function(require,module,exports){ +/** + * Formats the input of a whisper post and converts all values to HEX + * + * @method inputPostFormatter + * @param {Object} transaction object + * @returns {Object} +*/ +var inputPostFormatter = function(post){ + + post.payload = utils.toHex(post.payload); + post.ttl = utils.fromDecimal(post.ttl); + post.workToProve = utils.fromDecimal(post.workToProve); + + if(!(post.topic instanceof Array)) + post.topic = [post.topic]; + + + // format the following options + post.topic = post.topic.map(function(topic){ + return utils.fromAscii(topic); + }); + + return post; +}; + +/** + * Formats the output of a received post message + * + * @method outputPostFormatter + * @param {Object} + * @returns {Object} + */ +var outputPostFormatter = function(post){ + + post.expiry = utils.toDecimal(post.expiry); + post.sent = utils.toDecimal(post.sent); + post.ttl = utils.toDecimal(post.ttl); + post.payloadRaw = post.payload; + post.payload = utils.toAscii(post.payload); + + if(post.payload.indexOf('{') === 0 || post.payload.indexOf('[') === 0) { + try { + post.payload = JSON.parse(post.payload); + } catch (e) { } + } + + // format the following options + post.topic = post.topic.map(function(topic){ + return utils.toAscii(topic); + }); + + return post; +}; + +module.exports = { + convertToBigNumber: convertToBigNumber, + inputTransactionFormatter: inputTransactionFormatter, + outputTransactionFormatter: outputTransactionFormatter, + inputCallFormatter: inputCallFormatter, + outputBlockFormatter: outputBlockFormatter, + outputLogFormatter: outputLogFormatter, + inputPostFormatter: inputPostFormatter, + outputPostFormatter: outputPostFormatter +}; + + +},{"../utils/utils":5}],13:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1243,7 +2257,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file httpsync.js +/** @file httpprovider.js * @authors: * Marek Kotewicz * Marian Oancea @@ -1254,29 +2268,43 @@ if ("build" !== 'build') {/* var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line */} -var HttpSyncProvider = function (host) { +var HttpProvider = function (host) { this.handlers = []; this.host = host || 'http://localhost:8080'; }; -HttpSyncProvider.prototype.send = function (payload) { - //var data = formatJsonRpcObject(payload); - +HttpProvider.prototype.send = function (payload, callback) { var request = new XMLHttpRequest(); request.open('POST', this.host, false); - request.send(JSON.stringify(payload)); - var result = request.responseText; - // check request.status - if(request.status !== 200) - return; - return JSON.parse(result); + // ASYNC + if(typeof callback === 'function') { + request.onreadystatechange = function() { + if(request.readyState === 4 && request.status === 200) { + callback(JSON.parse(request.responseText)); + } + }; + + request.open('POST', this.host, true); + request.send(JSON.stringify(payload)); + + // SYNC + } else { + request.open('POST', this.host, false); + request.send(JSON.stringify(payload)); + + // check request.status + if(request.status !== 200) + return; + return JSON.parse(request.responseText); + + } }; -module.exports = HttpSyncProvider; +module.exports = HttpProvider; -},{}],10:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1343,7 +2371,50 @@ module.exports = { -},{}],11:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file eth.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var utils = require('../utils/utils'); + +/// @returns an array of objects describing web3.eth api methods +var methods = [ + // { name: 'getBalance', call: 'eth_balanceAt', outputFormatter: formatters.convertToBigNumber}, +]; + +/// @returns an array of objects describing web3.eth api properties +var properties = [ + { name: 'listening', getter: 'net_listening'}, + { name: 'peerCount', getter: 'net_peerCount', outputFormatter: utils.toDecimal }, +]; + + +module.exports = { + methods: methods, + properties: properties +}; + + +},{"../utils/utils":5}],16:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1378,7 +2449,7 @@ QtSyncProvider.prototype.send = function (payload) { module.exports = QtSyncProvider; -},{}],12:[function(require,module,exports){ +},{}],17:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1405,7 +2476,7 @@ module.exports = QtSyncProvider; */ var jsonrpc = require('./jsonrpc'); -var c = require('./const'); +var c = require('../utils/config'); /** * It's responsible for passing messages to providers @@ -1417,16 +2488,29 @@ var requestManager = function() { var timeout = null; var provider; - var send = function (data) { - /*jshint maxcomplexity: 6 */ + var send = function (data, callback) { + /*jshint maxcomplexity: 7 */ - // format the input before sending + // FORMAT BASED ON ONE FORMATTER function if(typeof data.inputFormatter === 'function') { - data.params = Array.prototype.map.call(data.params, function(item){ - return data.inputFormatter(item); + data.params = Array.prototype.map.call(data.params, function(item, index){ + // format everything besides the defaultblock, which is already formated + return (!data.addDefaultblock || index+1 < data.addDefaultblock) + ? data.inputFormatter(item) + : item; + }); + + // FORMAT BASED ON the input FORMATTER ARRAY + } else if(data.inputFormatter instanceof Array) { + data.params = Array.prototype.map.call(data.inputFormatter, function(formatter, index){ + // format everything besides the defaultblock, which is already formated + return (!data.addDefaultblock || index+1 < data.addDefaultblock) + ? formatter(data.params[index]) + : data.params[index]; }); } + var payload = jsonrpc.toPayload(data.method, data.params); if (!provider) { @@ -1434,17 +2518,36 @@ var requestManager = function() { return null; } - var result = provider.send(payload); + // ASYNC (only when callback is given, and it a HttpProvidor) + if(typeof callback === 'function' && provider.host){ + provider.send(payload, function(result){ - if (!jsonrpc.isValidResponse(result)) { - console.log(result); - if(typeof result === 'object' && result.error && result.error.message) - console.error(result.error.message); - return null; + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + if(typeof result === 'object' && result.error && result.error.message) + console.error(result.error.message); + return null; + } + + // format the output + callback((typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result); + }); + + // SYNC + } else { + var result = provider.send(payload); + + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + if(typeof result === 'object' && result.error && result.error.message) + console.error(result.error.message); + return null; + } + + // format the output + return (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result; } - // format the output - return (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result; }; var setProvider = function (p) { @@ -1481,11 +2584,13 @@ var requestManager = function() { var poll = function () { polls.forEach(function (data) { - var result = send(data.data); - if (!(result instanceof Array) || result.length === 0) { - return; - } - data.callback(result); + // send async + send(data.data, function(result){ + if (!(result instanceof Array) || result.length === 0) { + return; + } + data.callback(result); + }); }); timeout = setTimeout(poll, c.ETH_POLLING_TIMEOUT); }; @@ -1504,7 +2609,7 @@ var requestManager = function() { module.exports = requestManager; -},{"./const":2,"./jsonrpc":10}],13:[function(require,module,exports){ +},{"../utils/config":4,"./jsonrpc":14}],18:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1527,17 +2632,19 @@ module.exports = requestManager; * @date 2015 */ +var formatters = require('./formatters'); + /// @returns an array of objects describing web3.shh api methods var methods = function () { return [ - { name: 'post', call: 'shh_post' }, + { name: 'post', call: 'shh_post', inputFormatter: formatters.inputPostFormatter }, { name: 'newIdentity', call: 'shh_newIdentity' }, - { name: 'hasIdentity', call: 'shh_haveIdentity' }, + { name: 'hasIdentity', call: 'shh_hasIdentity' }, { name: 'newGroup', call: 'shh_newGroup' }, { name: 'addToGroup', call: 'shh_addToGroup' }, // deprecated - { name: 'haveIdentity', call: 'shh_haveIdentity', newMethod: 'hasIdentity' }, + { name: 'haveIdentity', call: 'shh_haveIdentity', newMethod: 'shh.hasIdentity' }, ]; }; @@ -1546,7 +2653,7 @@ module.exports = { }; -},{}],14:[function(require,module,exports){ +},{"./formatters":12}],19:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1569,8 +2676,8 @@ module.exports = { * @date 2015 */ -var web3 = require('./web3'); -var c = require('./const'); +var web3 = require('../web3'); +var c = require('../utils/config'); /// @param function name for which we want to get signature /// @returns signature of function with given name @@ -1590,402 +2697,7 @@ module.exports = { }; -},{"./const":2,"./web3":18}],15:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file types.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var f = require('./formatters'); - -/// @param expected type prefix (string) -/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false -var prefixedType = function (prefix) { - return function (type) { - return type.indexOf(prefix) === 0; - }; -}; - -/// @param expected type name (string) -/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false -var namedType = function (name) { - return function (type) { - return name === type; - }; -}; - -/// Setups input formatters for solidity types -/// @returns an array of input formatters -var inputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatInputInt }, - { type: prefixedType('int'), format: f.formatInputInt }, - { type: prefixedType('hash'), format: f.formatInputInt }, - { type: prefixedType('string'), format: f.formatInputString }, - { type: prefixedType('real'), format: f.formatInputReal }, - { type: prefixedType('ureal'), format: f.formatInputReal }, - { type: namedType('address'), format: f.formatInputInt }, - { type: namedType('bool'), format: f.formatInputBool } - ]; -}; - -/// Setups output formaters for solidity types -/// @returns an array of output formatters -var outputTypes = function () { - - return [ - { type: prefixedType('uint'), format: f.formatOutputUInt }, - { type: prefixedType('int'), format: f.formatOutputInt }, - { type: prefixedType('hash'), format: f.formatOutputHash }, - { type: prefixedType('string'), format: f.formatOutputString }, - { type: prefixedType('real'), format: f.formatOutputReal }, - { type: prefixedType('ureal'), format: f.formatOutputUReal }, - { type: namedType('address'), format: f.formatOutputAddress }, - { type: namedType('bool'), format: f.formatOutputBool } - ]; -}; - -module.exports = { - prefixedType: prefixedType, - namedType: namedType, - inputTypes: inputTypes, - outputTypes: outputTypes -}; - - -},{"./formatters":8}],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 utils.js - * @authors: - * Marek Kotewicz - * @date 2015 - */ - -var c = require('./const'); - -if ("build" !== 'build') {/* - var BigNumber = require('bignumber.js'); // jshint ignore:line -*/} - -var unitMap = { - 'wei': '1', - 'kwei': '1000', - 'ada': '1000', - 'mwei': '1000000', - 'babbage': '1000000', - 'gwei': '1000000000', - 'shannon': '1000000000', - 'szabo': '1000000000000', - 'finney': '1000000000000000', - 'ether': '1000000000000000000', - 'kether': '1000000000000000000000', - 'grand': '1000000000000000000000', - 'einstein': '1000000000000000000000', - 'mether': '1000000000000000000000000', - 'gether': '1000000000000000000000000000', - 'tether': '1000000000000000000000000000000' -}; - - -/// Finds first index of array element matching pattern -/// @param array -/// @param callback pattern -/// @returns index of element -var findIndex = function (array, callback) { - var end = false; - var i = 0; - for (; i < array.length && !end; i++) { - end = callback(array[i]); - } - return end ? i - 1 : -1; -}; - -/// @returns ascii string representation of hex value prefixed with 0x -var toAscii = function(hex) { -// Find termination - var str = ""; - var i = 0, l = hex.length; - if (hex.substring(0, 2) === '0x') { - i = 2; - } - for (; i < l; i+=2) { - var code = parseInt(hex.substr(i, 2), 16); - if (code === 0) { - break; - } - - str += String.fromCharCode(code); - } - - return str; -}; - -var toHex = function(str) { - var hex = ""; - for(var i = 0; i < str.length; i++) { - var n = str.charCodeAt(i).toString(16); - hex += n.length < 2 ? '0' + n : n; - } - - return hex; -}; - -/// @returns hex representation (prefixed by 0x) of ascii string -var fromAscii = function(str, pad) { - pad = pad === undefined ? 0 : pad; - var hex = toHex(str); - while (hex.length < pad*2) - hex += "00"; - return "0x" + hex; -}; - -/// @returns display name for function/event eg. multiply(uint256) -> multiply -var extractDisplayName = function (name) { - var length = name.indexOf('('); - return length !== -1 ? name.substr(0, length) : name; -}; - -/// @returns overloaded part of function/event name -var extractTypeName = function (name) { - /// TODO: make it invulnerable - var length = name.indexOf('('); - return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; -}; - -/// Filters all function from input abi -/// @returns abi array with filtered objects of type 'function' -var filterFunctions = function (json) { - return json.filter(function (current) { - return current.type === 'function'; - }); -}; - -/// Filters all events form input abi -/// @returns abi array with filtered objects of type 'event' -var filterEvents = function (json) { - return json.filter(function (current) { - return current.type === 'event'; - }); -}; - -/// used to transform value/string to eth string -/// TODO: use BigNumber.js to parse int -/// TODO: add tests for it! -var toEth = function (str) { - - console.warn('This method is deprecated please use eth.fromWei(BigNumberOrNumber, unit) instead.'); - - /*jshint maxcomplexity:7 */ - var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str.replace(/,/g,'').replace(/ /g,'')) : str; - var unit = 0; - var units = c.ETH_UNITS; - while (val > 3000 && unit < units.length - 1) - { - val /= 1000; - unit++; - } - var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); - var replaceFunction = function($0, $1, $2) { - return $1 + ',' + $2; - }; - - while (true) { - var o = s; - s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); - if (o === s) - break; - } - return s + ' ' + units[unit]; -}; - - -var toDecimal = function (val) { - // remove 0x and place 0, if it's required - val = val.length > 2 ? val.substring(2) : "0"; - return (new BigNumber(val, 16).toString(10)); -}; - -var fromDecimal = function (val) { - return "0x" + (new BigNumber(val).toString(16)); -}; - - -/** -Takes a number of wei and converts it to any other ether unit. - -Possible units are: - - - kwei/ada - - mwei/babbage - - gwei/shannon - - szabo - - finney - - ether - - kether/grand/einstein - - mether - - gether - - tether - -@method fromWei -@param {Number|String} number can be a number, number string or a HEX of a decimal -@param {String} unit the unit to convert to -@return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var fromWei = function(number, unit) { - /*jshint maxcomplexity: 6 */ - unit = unit.toLowerCase(); - - var isBigNumber = true; - - if(!unitMap[unit]) { - console.warn('This unit doesn\'t exists, please use the one of the following units' , unitMap); - return number; - } - - if(!number) - return number; - - if(typeof number === 'string' && number.indexOf('0x') === 0) { - isBigNumber = false; - number = new BigNumber(number, 16); - } - - if(!(number instanceof BigNumber)) { - isBigNumber = false; - number = new BigNumber(number.toString(10), 10); // toString to prevent errors, the user have to handle giving correct bignums themselves - } - - number = number.dividedBy(new BigNumber(unitMap[unit], 10)); - - return (isBigNumber) ? number : number.toString(10); -}; - -/** -Takes a number of a unit and converts it to wei. - -Possible units are: - - - kwei/ada - - mwei/babbage - - gwei/shannon - - szabo - - finney - - ether - - kether/grand/einstein - - mether - - gether - - tether - -@method toWei -@param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal -@param {String} unit the unit to convert to -@return {String|Object} When given a BigNumber object it returns one as well, otherwise a number -*/ -var toWei = function(number, unit) { - /*jshint maxcomplexity: 6 */ - unit = unit.toLowerCase(); - - var isBigNumber = true; - - if(!unitMap[unit]) { - console.warn('This unit doesn\'t exists, please use the one of the following units' , unitMap); - return number; - } - - if(!number) - return number; - - if(typeof number === 'string' && number.indexOf('0x') === 0) { - isBigNumber = false; - number = new BigNumber(number, 16); - } - - if(!(number instanceof BigNumber)) { - isBigNumber = false; - number = new BigNumber(number.toString(10), 10);// toString to prevent errors, the user have to handle giving correct bignums themselves - } - - - number = number.times(new BigNumber(unitMap[unit], 10)); - - return (isBigNumber) ? number : number.toString(10); -}; - - -/** -Checks if the given string is a valid ethereum HEX address. - -@method isAddress -@param {String} address the given HEX adress -@return {Boolean} -*/ -var isAddress = function(address) { - if(address.indexOf('0x') === 0 && address.length !== 42) - return false; - if(address.indexOf('0x') === -1 && address.length !== 40) - return false; - - return /^\w+$/.test(address); -}; - -var isBigNumber = function (value) { - return value instanceof BigNumber || - (value && value.constructor && value.constructor.name === 'BigNumber'); -}; - - -module.exports = { - findIndex: findIndex, - toDecimal: toDecimal, - fromDecimal: fromDecimal, - toAscii: toAscii, - fromAscii: fromAscii, - extractDisplayName: extractDisplayName, - extractTypeName: extractTypeName, - filterFunctions: filterFunctions, - filterEvents: filterEvents, - toEth: toEth, - toWei: toWei, - fromWei: fromWei, - isAddress: isAddress, - isBigNumber: isBigNumber -}; - - -},{"./const":2}],17:[function(require,module,exports){ +},{"../utils/config":4,"../web3":6}],20:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2011,13 +2723,13 @@ module.exports = { /// @returns an array of objects describing web3.eth.filter api methods var eth = function () { var newFilter = function (args) { - return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; + return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter'; }; return [ { name: 'newFilter', call: newFilter }, { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, - { name: 'getLogs', call: 'eth_filterLogs' } + { name: 'getLogs', call: 'eth_getFilterLogs' } ]; }; @@ -2036,259 +2748,16 @@ module.exports = { }; -},{}],18:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file web3.js - * @authors: - * Jeffrey Wilcke - * Marek Kotewicz - * Marian Oancea - * Gav Wood - * @date 2014 - */ - -// if (process.env.NODE_ENV !== '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 () { - return [ - { name: 'sha3', call: 'web3_sha3' } - ]; -}; - -/// creates methods in a given object based on method description on input -/// setups api calls for these methods -var setupMethods = function (obj, methods) { - methods.forEach(function (method) { - // allow for object methods 'myObject.method' - var objectMethods = method.name.split('.'), - callFunction = function () { - var args = Array.prototype.slice.call(arguments); - var call = typeof method.call === 'function' ? method.call(args) : method.call; - - // show deprecated warning - if(method.newMethod) - console.warn('This method is deprecated please use eth.'+ method.newMethod +'() instead.'); - - return web3.manager.send({ - method: call, - params: args, - outputFormatter: method.outputFormatter, - inputFormatter: method.inputFormatter - }); - }; - - if(objectMethods.length > 1) { - if(!obj[objectMethods[0]]) - obj[objectMethods[0]] = {}; - - obj[objectMethods[0]][objectMethods[1]] = callFunction; - - } else { - - obj[objectMethods[0]] = callFunction; - } - - }); -}; - -/// creates properties in a given object based on properties description on input -/// setups api calls for these properties -var setupProperties = function (obj, properties) { - properties.forEach(function (property) { - var proto = {}; - proto.get = function () { - - // show deprecated warning - if(property.newProperty) - console.warn('This property is deprecated please use eth.'+ property.newProperty +' instead.'); - - - return web3.manager.send({ - method: property.getter, - outputFormatter: property.outputFormatter - }); - }; - - if (property.setter) { - proto.set = function (val) { - - // show deprecated warning - if(property.newProperty) - console.warn('This property is deprecated please use eth.'+ property.newProperty +' instead.'); - - return web3.manager.send({ - method: property.setter, - params: [val], - inputFormatter: property.inputFormatter - }); - }; - } - - proto.enumerable = !property.newProperty; - Object.defineProperty(obj, property.name, proto); - - }); -}; - -/*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 = { - manager: requestManager(), - providers: {}, - - 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(); - }, - - /// @returns ascii string representation of hex value prefixed with 0x - toAscii: utils.toAscii, - - /// @returns hex representation (prefixed by 0x) of ascii string - fromAscii: utils.fromAscii, - - /// @returns decimal representaton of hex value prefixed by 0x - toDecimal: utils.toDecimal, - - /// @returns hex representation (prefixed by 0x) of decimal value - fromDecimal: utils.fromDecimal, - - /// used to transform value/string to eth string - toEth: utils.toEth, - - toWei: utils.toWei, - fromWei: utils.fromWei, - isAddress: utils.isAddress, - - - /// eth object prototype - eth: { - // DEPRECATED - contractFromAbi: function (abi) { - console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); - - return function(addr) { - // Default to address of Config. TODO: rremove prior to genesis. - addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; - var ret = web3.eth.contract(addr, abi); - ret.address = addr; - return ret; - }; - }, - - /// @param filter may be a string, object or event - /// @param eventParams is optional, this is an object with optional event eventParams params - /// @param options is optional, this is an object with optional event options ('max'...) - /// TODO: fix it, 4 params? no way - /*jshint maxparams:4 */ - filter: function (fil, eventParams, options, formatter) { - - // if its event, treat it differently - if (fil._isEvent) - return fil(eventParams, options); - - return filter(fil, ethWatch, formatter); - }, - // DEPRECATED - watch: function (fil, eventParams, options, formatter) { - console.warn('eth.watch() is deprecated please use eth.filter() instead.'); - return this.filter(fil, eventParams, options, formatter); - } - /*jshint maxparams:3 */ - }, - - /// db object prototype - db: {}, - - /// shh object prototype - shh: { - /// @param filter may be a string, object or event - filter: function (fil) { - return filter(fil, shhWatch); - }, - // DEPRECATED - watch: function (fil) { - console.warn('shh.watch() is deprecated please use shh.filter() instead.'); - return this.filter(fil); - } - } -}; - -/// setups all api methods -setupMethods(web3, web3Methods()); -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; - - -},{"./db":4,"./eth":5,"./filter":7,"./requestmanager":12,"./shh":13,"./utils":16,"./watches":17}],"web3":[function(require,module,exports){ +},{}],"web3":[function(require,module,exports){ var web3 = require('./lib/web3'); -web3.providers.HttpSyncProvider = require('./lib/httpsync'); -web3.providers.QtSyncProvider = require('./lib/qtsync'); -web3.eth.contract = require('./lib/contract'); -web3.abi = require('./lib/abi'); +web3.providers.HttpProvider = require('./lib/web3/httpprovider'); +web3.providers.QtSyncProvider = require('./lib/web3/qtsync'); +web3.eth.contract = require('./lib/web3/contract'); +web3.abi = require('./lib/solidity/abi'); module.exports = web3; -},{"./lib/abi":1,"./lib/contract":3,"./lib/httpsync":9,"./lib/qtsync":11,"./lib/web3":18}]},{},["web3"]) +},{"./lib/solidity/abi":1,"./lib/web3":6,"./lib/web3/contract":7,"./lib/web3/httpprovider":13,"./lib/web3/qtsync":16}]},{},["web3"]) //# sourceMappingURL=ethereum.js.map diff --git a/rpc/args.go b/rpc/args.go index 63969e598b..887d63d465 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -8,6 +8,30 @@ import ( "github.com/ethereum/go-ethereum/ethutil" ) +// Unmarshal state is a helper method which has the ability to decode messsages +// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter) +// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction +// message and the second one refers to the block height (or state) to which to apply this `call`. +func unmarshalState(b []byte, iface interface{}, str *string) (err error) { + var data []json.RawMessage + if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 { + return errDecodeArgs + } + + if err = json.Unmarshal(data[0], iface); err != nil { + return errDecodeArgs + } + + // Second argument is optional (transact doesn't require it) + if len(data) > 1 { + if err = json.Unmarshal(data[1], str); err != nil { + return errDecodeArgs + } + } + + return nil +} + type GetBlockByHashArgs struct { BlockHash string Transactions bool @@ -63,10 +87,12 @@ type NewTxArgs struct { Gas *big.Int GasPrice *big.Int Data string + + BlockHeight string } func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { - var obj []struct { + var obj struct { From string `json:"from"` To string `json:"to"` Value string `json:"value"` @@ -74,20 +100,18 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { GasPrice string `json:"gasPrice"` Data string `json:"data"` } - - if err = json.Unmarshal(b, &obj); err != nil { - return errDecodeArgs + var height string + if err = unmarshalState(b, &obj, &height); err != nil { + return err } - if len(obj) < 1 { - return errArguments - } - args.From = obj[0].From - args.To = obj[0].To - args.Value = ethutil.Big(obj[0].Value) - args.Gas = ethutil.Big(obj[0].Gas) - args.GasPrice = ethutil.Big(obj[0].GasPrice) - args.Data = obj[0].Data + args.From = obj.From + args.To = obj.To + args.Value = ethutil.Big(obj.Value) + args.Gas = ethutil.Big(obj.Gas) + args.GasPrice = ethutil.Big(obj.GasPrice) + args.Data = obj.Data + args.BlockHeight = height return nil } From 0395c174ca5221dd936c19143ced454dac56d54a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 14:31:54 +0100 Subject: [PATCH 54/82] cmd/ethereum: fix line editing setup and history --- cmd/ethereum/js.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index de73e83a2f..5432fb9b1b 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -63,6 +63,7 @@ type jsre struct { ethereum *eth.Ethereum xeth *xeth.XEth ps1 string + atexit func() prompter } @@ -77,11 +78,13 @@ func newJSRE(ethereum *eth.Ethereum) *jsre { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() - lr.SetCtrlCAborts(true) - defer lr.Close() js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) - defer js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + lr.SetCtrlCAborts(true) js.prompter = lr + js.atexit = func() { + js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + lr.Close() + } } return js } @@ -100,7 +103,6 @@ func (self *jsre) UnlockAccount(addr []byte) bool { } // TODO: allow retry if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil { - fmt.Println("Unlocking failed: ", err) return false } else { fmt.Println("Account is now unlocked for this session.") @@ -127,7 +129,7 @@ func (self *jsre) interactive() { for { input, err := self.Prompt(self.ps1) if err != nil { - return + break } if input == "" { continue @@ -136,7 +138,7 @@ func (self *jsre) interactive() { self.setIndent() if indentCount <= 0 { if input == "exit" { - return + break } hist := str[:len(str)-1] self.AppendHistory(hist) @@ -144,6 +146,9 @@ func (self *jsre) interactive() { str = "" } } + if self.atexit != nil { + self.atexit() + } } func (self *jsre) withHistory(op func(*os.File)) { From 0f67f1e9de856404136dcdce78fc0ab93bab6a84 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 14:55:00 +0100 Subject: [PATCH 55/82] xeth: fix signing transaction after unlock --- xeth/xeth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xeth/xeth.go b/xeth/xeth.go index f8b537321e..60262bf175 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -360,7 +360,7 @@ func (self *XEth) sign(tx *types.Transaction, from []byte, didUnlock bool) error return fmt.Errorf("could not unlock sender account") } // retry signing, the account should now be unlocked. - self.sign(tx, from, true) + return self.sign(tx, from, true) } else if err != nil { return err } From 4ba7871374fd3af08bfad972509267842db6df99 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 15:41:08 +0100 Subject: [PATCH 56/82] accounts: return ErrNoKeys if key directory does not exist --- accounts/account_manager.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index fdd7d83e9c..646dc8376e 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -36,6 +36,7 @@ import ( "bytes" "crypto/ecdsa" crand "crypto/rand" + "os" "errors" "sync" @@ -89,7 +90,9 @@ func (am *Manager) Coinbase() (addr []byte, err error) { func (am *Manager) firstAddr() ([]byte, error) { addrs, err := am.keyStore.GetKeyAddresses() - if err != nil { + if os.IsNotExist(err) { + return nil, ErrNoKeys + } else if err != nil { return nil, err } if len(addrs) == 0 { @@ -147,7 +150,9 @@ func (am *Manager) NewAccount(auth string) (Account, error) { func (am *Manager) Accounts() ([]Account, error) { addresses, err := am.keyStore.GetKeyAddresses() - if err != nil { + if os.IsNotExist(err) { + return nil, ErrNoKeys + } else if err != nil { return nil, err } accounts := make([]Account, len(addresses)) From c3f94a4341056d36ec9f42dd80702ec7ec6986af Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 15:42:25 +0100 Subject: [PATCH 57/82] cmd/utils: remove extra space in fatal error message --- cmd/utils/cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index a802a08da3..c5568948d6 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -98,7 +98,7 @@ func initDataDir(Datadir string) { func exit(err error) { status := 0 if err != nil { - fmt.Fprintln(os.Stderr, "Fatal: ", err) + fmt.Fprintln(os.Stderr, "Fatal:", err) status = 1 } logger.Flush() From 9d4e1e8f8bbdfa84937bcdcdc6b4ca4ba6cd79a2 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 15:42:58 +0100 Subject: [PATCH 58/82] eth: return account errors directly --- eth/backend.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/backend.go b/eth/backend.go index ac8a016fe5..9d7ce49883 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -168,7 +168,7 @@ func New(config *Config) (*Ethereum, error) { cb, err := eth.accountManager.Coinbase() if err != nil { - return nil, fmt.Errorf("no coinbase: %v", err) + return nil, err } eth.chainManager = core.NewChainManager(blockDb, stateDb, eth.EventMux()) From 0bb7377ebee69c3467c21d355dd24945d0becad5 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 15:44:05 +0100 Subject: [PATCH 59/82] cmd/ethereum: show more helpful message if no accounts exist --- cmd/ethereum/main.go | 19 +++++++++++++++++-- cmd/mist/main.go | 5 ++++- cmd/utils/flags.go | 8 ++------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 1703c02bb9..8beba471af 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -29,6 +29,7 @@ import ( "time" "github.com/codegangsta/cli" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" @@ -148,14 +149,28 @@ func main() { func run(ctx *cli.Context) { fmt.Printf("Welcome to the FRONTIER\n") utils.HandleInterrupt() - eth := utils.GetEthereum(ClientIdentifier, Version, ctx) + eth, err := utils.GetEthereum(ClientIdentifier, Version, ctx) + if err == accounts.ErrNoKeys { + utils.Fatalf(`No accounts configured. +Please run 'ethereum account new' to create a new account.`) + } else if err != nil { + utils.Fatalf("%v", err) + } + startEth(ctx, eth) // this blocks the thread eth.WaitForShutdown() } func runjs(ctx *cli.Context) { - eth := utils.GetEthereum(ClientIdentifier, Version, ctx) + eth, err := utils.GetEthereum(ClientIdentifier, Version, ctx) + if err == accounts.ErrNoKeys { + utils.Fatalf(`No accounts configured. +Please run 'ethereum account new' to create a new account.`) + } else if err != nil { + utils.Fatalf("%v", err) + } + startEth(ctx, eth) repl := newJSRE(eth) if len(ctx.Args()) == 0 { diff --git a/cmd/mist/main.go b/cmd/mist/main.go index c27f1dba9a..9a773e33a8 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -95,7 +95,10 @@ func run(ctx *cli.Context) { tstart := time.Now() // TODO: show qml popup instead of exiting if initialization fails. - ethereum := utils.GetEthereum(ClientIdentifier, Version, ctx) + ethereum, err := utils.GetEthereum(ClientIdentifier, Version, ctx) + if err != nil { + utils.Fatalf("%v", err) + } utils.StartRPC(ethereum, ctx) go utils.StartEthereum(ethereum) fmt.Println("initializing eth stack took", time.Since(tstart)) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index cde5fa0248..97d312dd41 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -157,8 +157,8 @@ func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) { return key } -func GetEthereum(clientID, version string, ctx *cli.Context) *eth.Ethereum { - ethereum, err := eth.New(ð.Config{ +func GetEthereum(clientID, version string, ctx *cli.Context) (*eth.Ethereum, error) { + return eth.New(ð.Config{ Name: p2p.MakeName(clientID, version), DataDir: ctx.GlobalString(DataDirFlag.Name), LogFile: ctx.GlobalString(LogFileFlag.Name), @@ -175,10 +175,6 @@ func GetEthereum(clientID, version string, ctx *cli.Context) *eth.Ethereum { Dial: true, BootNodes: ctx.GlobalString(BootnodesFlag.Name), }) - if err != nil { - exit(err) - } - return ethereum } func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database, ethutil.Database) { From 2407f006adbcbe5f5405b8591c5e9845cfa7dd5c Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 16:41:33 +0100 Subject: [PATCH 60/82] Godeps: bump github.com/codegangsta/cli --- Godeps/Godeps.json | 4 +-- .../src/github.com/codegangsta/cli/app.go | 31 ++++++++++++++++--- .../github.com/codegangsta/cli/app_test.go | 3 ++ .../src/github.com/codegangsta/cli/command.go | 2 +- .../src/github.com/codegangsta/cli/help.go | 13 +++++--- 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 8118b4509c..e0a932f8b4 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -17,8 +17,8 @@ }, { "ImportPath": "github.com/codegangsta/cli", - "Comment": "1.2.0-74-g50c77ec", - "Rev": "50c77ecec0068c9aef9d90ae0fd0fdf410041da3" + "Comment": "1.2.0-81-g3e09053", + "Rev": "3e0905345cd2c5366530dbcdce62457f2ce16e7c" }, { "ImportPath": "github.com/ethereum/ethash", diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/app.go b/Godeps/_workspace/src/github.com/codegangsta/cli/app.go index 928983ebd3..3e7d5a63ca 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/app.go +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/app.go @@ -43,9 +43,11 @@ type App struct { CommandNotFound func(context *Context, command string) // Compilation date Compiled time.Time - // Author + // List of all authors who contributed + Authors []Author + // Name of Author (Note: Use App.Authors, this is deprecated) Author string - // Author e-mail + // Email of Author (Note: Use App.Authors, this is deprecated) Email string // Writer writer to write output to Writer io.Writer @@ -70,14 +72,19 @@ func NewApp() *App { BashComplete: DefaultAppComplete, Action: helpCommand.Action, Compiled: compileTime(), - Author: "Author", - Email: "unknown@email", + Author: "Dr. James", + Email: "who@gmail.com", + Authors: []Author{{"Jim", "jim@corporate.com"}, {"Hank", "hank@indiepalace.com"}}, Writer: os.Stdout, } } // Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination func (a *App) Run(arguments []string) (err error) { + if a.Author != "" && a.Author != "" { + a.Authors = append(a.Authors, Author{a.Author, a.Email}) + } + if HelpPrinter == nil { defer func() { HelpPrinter = nil @@ -294,3 +301,19 @@ func (a *App) appendFlag(flag Flag) { a.Flags = append(a.Flags, flag) } } + +// Author represents someone who has contributed to a cli project. +type Author struct { + Name string // The Authors name + Email string // The Authors email +} + +// String makes Author comply to the Stringer interface, to allow an easy print in the templating process +func (a Author) String() string { + e := "" + if a.Email != "" { + e = "<" + a.Email + "> " + } + + return fmt.Sprintf("%v %v", a.Name, e) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go index fd2b0e8262..6143d364b5 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go @@ -21,6 +21,9 @@ func ExampleApp() { app.Action = func(c *cli.Context) { fmt.Printf("Hello %v\n", c.String("name")) } + app.Author = "Harrison" + app.Email = "harrison@lolwut.com" + app.Authors = []cli.Author{{"Oliver Allen", "oliver@toyshop.com"}} app.Run(os.Args) // Output: // Hello Jeremy diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/command.go b/Godeps/_workspace/src/github.com/codegangsta/cli/command.go index 5747e52e8e..07c919a87f 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/command.go +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/command.go @@ -119,7 +119,7 @@ func (c Command) Run(ctx *Context) error { // Returns true if Command.Name or Command.ShortName matches given name func (c Command) HasName(name string) bool { - return c.Name == name || c.ShortName == name + return c.Name == name || (c.ShortName != "" && c.ShortName == name) } func (c Command) startApp(ctx *Context) error { diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/help.go b/Godeps/_workspace/src/github.com/codegangsta/cli/help.go index bfb2788519..8d176556a2 100644 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/help.go +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/help.go @@ -12,11 +12,10 @@ USAGE: {{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] VERSION: - {{.Version}}{{if or .Author .Email}} + {{.Version}} -AUTHOR:{{if .Author}} - {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}} - {{.Email}}{{end}}{{end}} +AUTHOR(S): + {{range .Authors}}{{ . }} {{end}} COMMANDS: {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} @@ -112,6 +111,12 @@ func DefaultAppComplete(c *Context) { // Prints help for the given command func ShowCommandHelp(c *Context, command string) { + // show the subcommand help for a command with subcommands + if command == "" { + HelpPrinter(SubcommandHelpTemplate, c.App) + return + } + for _, c := range c.App.Commands { if c.HasName(command) { HelpPrinter(CommandHelpTemplate, c) From 221fae411278825dcaa0ec4058f065752046869b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 16:44:22 +0100 Subject: [PATCH 61/82] cmd/ethereum: show some help before prompting for encryption passphrase --- cmd/ethereum/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 8beba471af..ed17fcc1fc 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -186,6 +186,7 @@ Please run 'ethereum account new' to create a new account.`) func startEth(ctx *cli.Context, eth *eth.Ethereum) { utils.StartEthereum(eth) + // Start auxiliary services if enabled. if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { utils.StartRPC(eth, ctx) } @@ -207,6 +208,8 @@ func accountList(ctx *cli.Context) { func accountCreate(ctx *cli.Context) { am := utils.GetAccountManager(ctx) + fmt.Println("The new account will be encrypted with a passphrase.") + fmt.Println("Please enter a passphrase now.") auth, err := readPassword("Passphrase: ", true) if err != nil { utils.Fatalf("%v", err) From 972e2c1e31067a9bab77228c19348b66964ce643 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 16:44:48 +0100 Subject: [PATCH 62/82] cmd/utils: improve CLI help templates Help for a specific command now shows available subcommands. --- cmd/utils/flags.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 97d312dd41..e945a32eec 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -24,11 +24,38 @@ import ( "github.com/ethereum/go-ethereum/xeth" ) +func init() { + cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] + +VERSION: + {{.Version}} + +COMMANDS: + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}}{{if .Flags}} +GLOBAL OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{end}} +` + + cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...] +{{if .Description}}{{.Description}} +{{end}}{{if .Subcommands}} +SUBCOMMANDS: + {{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}}{{end}}{{if .Flags}} +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{end}} +` +} + // NewApp creates an app with sane defaults. func NewApp(version, usage string) *cli.App { app := cli.NewApp() app.Name = path.Base(os.Args[0]) app.Author = "" + app.Authors = nil app.Email = "" app.Version = version app.Usage = usage From 3d6519e45bbd689b75c3ad6966c2044f29c649df Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 10 Mar 2015 12:52:45 -0500 Subject: [PATCH 63/82] Update response types --- rpc/api.go | 95 ++++++++++++++++++++-- rpc/args_test.go | 2 +- rpc/responses.go | 206 +++++++++++++++++++++++++++++++++++++++++++++++ xeth/xeth.go | 15 ++++ 4 files changed, 312 insertions(+), 6 deletions(-) create mode 100644 rpc/responses.go diff --git a/rpc/api.go b/rpc/api.go index dc0945d198..20e0e705c4 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -415,6 +415,44 @@ func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error { return nil } +func (p *EthereumApi) GetBlockByHash(blockhash string, includetx bool) (*BlockRes, error) { + block := p.xeth().EthBlockByHash(blockhash) + br := NewBlockRes(block) + br.fullTx = includetx + return br, nil +} + +func (p *EthereumApi) GetBlockByNumber(blocknum int64, includetx bool) (*BlockRes, error) { + block := p.xeth().EthBlockByNumber(blocknum) + br := NewBlockRes(block) + br.fullTx = includetx + return br, nil +} + +func (p *EthereumApi) GetBlockTransactionCountByHash(blockhash string) (int64, error) { + block := p.xeth().EthBlockByHash(blockhash) + br := NewBlockRes(block) + return int64(len(br.Transactions)), nil +} + +func (p *EthereumApi) GetBlockTransactionCountByNumber(blocknum int64) (int64, error) { + block := p.xeth().EthBlockByNumber(blocknum) + br := NewBlockRes(block) + return int64(len(br.Transactions)), nil +} + +func (p *EthereumApi) GetBlockUncleCountByHash(blockhash string) (int64, error) { + block := p.xeth().EthBlockByHash(blockhash) + br := NewBlockRes(block) + return int64(len(br.Uncles)), nil +} + +func (p *EthereumApi) GetBlockUncleCountByNumber(blocknum int64) (int64, error) { + block := p.xeth().EthBlockByNumber(blocknum) + br := NewBlockRes(block) + return int64(len(br.Uncles)), nil +} + func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { // Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC rpclogger.DebugDetailf("%T %s", req.Params, req.Params) @@ -468,10 +506,49 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } return p.GetTxCountAt(args, reply) case "eth_getBlockTransactionCountByHash": + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockTransactionCountByHash(args.BlockHash) + if err != nil { + return err + } + *reply = toHex(big.NewInt(v).Bytes()) case "eth_getBlockTransactionCountByNumber": + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockTransactionCountByNumber(args.BlockNumber) + if err != nil { + return err + } + *reply = toHex(big.NewInt(v).Bytes()) case "eth_getUncleCountByBlockHash": + args := new(GetBlockByHashArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockUncleCountByHash(args.BlockHash) + if err != nil { + return err + } + *reply = toHex(big.NewInt(v).Bytes()) case "eth_getUncleCountByBlockNumber": - return errNotImplemented + args := new(GetBlockByNumberArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockUncleCountByNumber(args.BlockNumber) + if err != nil { + return err + } + *reply = toHex(big.NewInt(v).Bytes()) case "eth_getData": // TODO handle BlockNumber args := new(GetDataArgs) @@ -494,19 +571,27 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error case "eth_flush": return errNotImplemented case "eth_getBlockByHash": - // TODO handle second param for "include transaction objects" args := new(GetBlockByHashArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err } - *reply = p.xeth().BlockByHash(args.BlockHash) + + v, err := p.GetBlockByHash(args.BlockHash, args.Transactions) + if err != nil { + return err + } + *reply = v case "eth_getBlockByNumber": - // TODO handle second param for "include transaction objects" args := new(GetBlockByNumberArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err } - *reply = p.xeth().BlockByNumber(args.BlockNumber) + + v, err := p.GetBlockByNumber(args.BlockNumber, args.Transactions) + if err != nil { + return err + } + *reply = v case "eth_getTransactionByHash": case "eth_getTransactionByBlockHashAndIndex": case "eth_getTransactionByBlockNumberAndIndex": diff --git a/rpc/args_test.go b/rpc/args_test.go index 1e6609b79e..ec5400f3fc 100644 --- a/rpc/args_test.go +++ b/rpc/args_test.go @@ -243,7 +243,7 @@ func TestFilterOptions(t *testing.T) { expected.Max = 3 expected.Skip = 0 expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" - expected.Topic = []string{"0x12341234"} + // expected.Topics = []string{"0x12341234"} args := new(FilterOptions) if err := json.Unmarshal([]byte(input), &args); err != nil { diff --git a/rpc/responses.go b/rpc/responses.go new file mode 100644 index 0000000000..06d2ffef7d --- /dev/null +++ b/rpc/responses.go @@ -0,0 +1,206 @@ +package rpc + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethutil" +) + +type BlockRes struct { + fullTx bool + + BlockNumber int64 `json:"number"` + BlockHash []byte `json:"hash"` + ParentHash []byte `json:"parentHash"` + Nonce []byte `json:"nonce"` + Sha3Uncles []byte `json:"sha3Uncles"` + LogsBloom []byte `json:"logsBloom"` + TransactionRoot []byte `json:"transactionRoot"` + StateRoot []byte `json:"stateRoot"` + Miner []byte `json:"miner"` + Difficulty int64 `json:"difficulty"` + TotalDifficulty int64 `json:"totalDifficulty"` + Size int64 `json:"size"` + ExtraData []byte `json:"extraData"` + GasLimit int64 `json:"gasLimit"` + MinGasPrice int64 `json:"minGasPrice"` + GasUsed int64 `json:"gasUsed"` + UnixTimestamp int64 `json:"timestamp"` + Transactions []*TransactionRes `json:"transactions"` + Uncles [][]byte `json:"uncles"` +} + +func (b *BlockRes) MarshalJSON() ([]byte, error) { + var ext struct { + BlockNumber string `json:"number"` + BlockHash string `json:"hash"` + ParentHash string `json:"parentHash"` + Nonce string `json:"nonce"` + Sha3Uncles string `json:"sha3Uncles"` + LogsBloom string `json:"logsBloom"` + TransactionRoot string `json:"transactionRoot"` + StateRoot string `json:"stateRoot"` + Miner string `json:"miner"` + Difficulty string `json:"difficulty"` + TotalDifficulty string `json:"totalDifficulty"` + Size string `json:"size"` + ExtraData string `json:"extraData"` + GasLimit string `json:"gasLimit"` + MinGasPrice string `json:"minGasPrice"` + GasUsed string `json:"gasUsed"` + UnixTimestamp string `json:"timestamp"` + Transactions []interface{} `json:"transactions"` + Uncles []string `json:"uncles"` + } + + // convert strict types to hexified strings + ext.BlockNumber = ethutil.Bytes2Hex(big.NewInt(b.BlockNumber).Bytes()) + ext.BlockHash = ethutil.Bytes2Hex(b.BlockHash) + ext.ParentHash = ethutil.Bytes2Hex(b.ParentHash) + ext.Nonce = ethutil.Bytes2Hex(b.Nonce) + ext.Sha3Uncles = ethutil.Bytes2Hex(b.Sha3Uncles) + ext.LogsBloom = ethutil.Bytes2Hex(b.LogsBloom) + ext.TransactionRoot = ethutil.Bytes2Hex(b.TransactionRoot) + ext.StateRoot = ethutil.Bytes2Hex(b.StateRoot) + ext.Miner = ethutil.Bytes2Hex(b.Miner) + ext.Difficulty = ethutil.Bytes2Hex(big.NewInt(b.Difficulty).Bytes()) + ext.TotalDifficulty = ethutil.Bytes2Hex(big.NewInt(b.TotalDifficulty).Bytes()) + ext.Size = ethutil.Bytes2Hex(big.NewInt(b.Size).Bytes()) + ext.ExtraData = ethutil.Bytes2Hex(b.ExtraData) + ext.GasLimit = ethutil.Bytes2Hex(big.NewInt(b.GasLimit).Bytes()) + ext.MinGasPrice = ethutil.Bytes2Hex(big.NewInt(b.MinGasPrice).Bytes()) + ext.GasUsed = ethutil.Bytes2Hex(big.NewInt(b.GasUsed).Bytes()) + ext.UnixTimestamp = ethutil.Bytes2Hex(big.NewInt(b.UnixTimestamp).Bytes()) + ext.Transactions = make([]interface{}, len(b.Transactions)) + if b.fullTx { + for i, tx := range b.Transactions { + ext.Transactions[i] = tx + } + } else { + for i, tx := range b.Transactions { + ext.Transactions[i] = ethutil.Bytes2Hex(tx.Hash) + } + } + ext.Uncles = make([]string, len(b.Uncles)) + for i, v := range b.Uncles { + ext.Uncles[i] = ethutil.Bytes2Hex(v) + } + + return json.Marshal(ext) +} + +func NewBlockRes(block *types.Block) *BlockRes { + res := new(BlockRes) + res.BlockNumber = block.Number().Int64() + res.BlockHash = block.Hash() + res.ParentHash = block.ParentHash() + res.Nonce = block.Header().Nonce + res.Sha3Uncles = block.Header().UncleHash + res.LogsBloom = block.Bloom() + res.TransactionRoot = block.Header().TxHash + res.StateRoot = block.Root() + res.Miner = block.Header().Coinbase + res.Difficulty = block.Difficulty().Int64() + res.TotalDifficulty = block.Td.Int64() + res.Size = int64(block.Size()) + // res.ExtraData = + res.GasLimit = block.GasLimit().Int64() + // res.MinGasPrice = + res.GasUsed = block.GasUsed().Int64() + res.UnixTimestamp = block.Time() + res.Transactions = make([]*TransactionRes, len(block.Transactions())) + for i, tx := range block.Transactions() { + v := NewTransactionRes(tx) + v.BlockHash = block.Hash() + v.BlockNumber = block.Number().Int64() + v.TxIndex = int64(i) + res.Transactions[i] = v + } + res.Uncles = make([][]byte, len(block.Uncles())) + for i, uncle := range block.Uncles() { + res.Uncles[i] = uncle.Hash() + } + return res +} + +type TransactionRes struct { + Hash []byte `json:"hash"` + Nonce int64 `json:"nonce"` + BlockHash []byte `json:"blockHash,omitempty"` + BlockNumber int64 `json:"blockNumber,omitempty"` + TxIndex int64 `json:"transactionIndex,omitempty"` + From []byte `json:"from"` + To []byte `json:"to"` + Value int64 `json:"value"` + Gas int64 `json:"gas"` + GasPrice int64 `json:"gasPrice"` + Input []byte `json:"input"` +} + +func (t *TransactionRes) MarshalJSON() ([]byte, error) { + var ext struct { + Hash string `json:"hash"` + Nonce string `json:"nonce"` + BlockHash string `json:"blockHash,omitempty"` + BlockNumber string `json:"blockNumber,omitempty"` + TxIndex string `json:"transactionIndex,omitempty"` + From string `json:"from"` + To string `json:"to"` + Value string `json:"value"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Input string `json:"input"` + } + + ext.Hash = ethutil.Bytes2Hex(t.Hash) + ext.Nonce = ethutil.Bytes2Hex(big.NewInt(t.Nonce).Bytes()) + ext.BlockHash = ethutil.Bytes2Hex(t.BlockHash) + ext.BlockNumber = ethutil.Bytes2Hex(big.NewInt(t.BlockNumber).Bytes()) + ext.TxIndex = ethutil.Bytes2Hex(big.NewInt(t.TxIndex).Bytes()) + ext.From = ethutil.Bytes2Hex(t.From) + ext.To = ethutil.Bytes2Hex(t.To) + ext.Value = ethutil.Bytes2Hex(big.NewInt(t.Value).Bytes()) + ext.Gas = ethutil.Bytes2Hex(big.NewInt(t.Gas).Bytes()) + ext.GasPrice = ethutil.Bytes2Hex(big.NewInt(t.GasPrice).Bytes()) + ext.Input = ethutil.Bytes2Hex(t.Input) + + return json.Marshal(ext) +} + +func NewTransactionRes(tx *types.Transaction) *TransactionRes { + var v = new(TransactionRes) + v.Hash = tx.Hash() + v.Nonce = int64(tx.Nonce()) + v.From = tx.From() + v.To = tx.To() + v.Value = tx.Value().Int64() + v.Gas = tx.Gas().Int64() + v.GasPrice = tx.GasPrice().Int64() + v.Input = tx.Data() + return v +} + +type FilterLogRes struct { + Hash string `json:"hash"` + Address string `json:"address"` + Data string `json:"data"` + BlockNumber string `json:"blockNumber"` + TransactionHash string `json:"transactionHash"` + BlockHash string `json:"blockHash"` + TransactionIndex string `json:"transactionIndex"` + LogIndex string `json:"logIndex"` +} + +type FilterWhisperRes struct { + Hash string `json:"hash"` + From string `json:"from"` + To string `json:"to"` + Expiry string `json:"expiry"` + Sent string `json:"sent"` + Ttl string `json:"ttl"` + Topics string `json:"topics"` + Payload string `json:"payload"` + WorkProved string `json:"workProved"` +} diff --git a/xeth/xeth.go b/xeth/xeth.go index c0281f1da9..67bb3c622d 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -99,6 +99,13 @@ func (self *XEth) BlockByHash(strHash string) *Block { return NewBlock(block) } +func (self *XEth) EthBlockByHash(strHash string) *types.Block { + hash := fromHex(strHash) + block := self.chainManager.GetBlock(hash) + + return block +} + func (self *XEth) BlockByNumber(num int64) *Block { if num == -1 { return NewBlock(self.chainManager.CurrentBlock()) @@ -107,6 +114,14 @@ func (self *XEth) BlockByNumber(num int64) *Block { return NewBlock(self.chainManager.GetBlockByNumber(uint64(num))) } +func (self *XEth) EthBlockByNumber(num int64) *types.Block { + if num == -1 { + return self.chainManager.CurrentBlock() + } + + return self.chainManager.GetBlockByNumber(uint64(num)) +} + func (self *XEth) Block(v interface{}) *Block { if n, ok := v.(int32); ok { return self.BlockByNumber(int64(n)) From f22684439a807f88406e90718e61d536edd469f1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 10 Mar 2015 20:14:38 +0100 Subject: [PATCH 64/82] Updated RPC * Added a generic RawMessage deserialiser * Updated ethereum.js * Updated coin test app --- cmd/mist/assets/examples/coin.html | 5 +- .../assets/ext/ethereum.js/dist/ethereum.js | 80 ++++++++--- rpc/api.go | 6 +- rpc/args.go | 136 ++++-------------- rpc/util.go | 61 ++++++++ 5 files changed, 152 insertions(+), 136 deletions(-) diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html index 115145c4c4..b6bab682fe 100644 --- a/cmd/mist/assets/examples/coin.html +++ b/cmd/mist/assets/examples/coin.html @@ -77,7 +77,8 @@ } document.querySelector("#contract_addr").innerHTML = address; - var contract = web3.eth.contract(address, desc); + var Contract = web3.eth.contract(desc); + contract = new Contract(address); contract.Changed({from: eth.coinbase}).changed(function() { refresh(); }); @@ -88,7 +89,7 @@ var table = document.querySelector("#table_body"); table.innerHTML = ""; // clear - var storage = eth.storageAt(address); + var storage = eth.getStorage(address); table.innerHTML = ""; for( var item in storage ) { table.innerHTML += ""+item+""+web3.toDecimal(storage[item])+""; diff --git a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js index 7b2531677a..c0b37641c7 100644 --- a/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js +++ b/cmd/mist/assets/ext/ethereum.js/dist/ethereum.js @@ -1684,7 +1684,7 @@ var methods = [ inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]}, { name: 'getUncle', call: uncleCall, outputFormatter: formatters.outputBlockFormatter, - inputFormatter: [utils.toHex, function(param){ return (!param) ? false : true; }]}, + inputFormatter: [utils.toHex, utils.toHex, function(param){ return (!param) ? false : true; }]}, { name: 'getCompilers', call: 'eth_getCompilers' }, { name: 'getBlockTransactionCount', call: getBlockTransactionCountCall, outputFormatter: utils.toDecimal, @@ -1703,9 +1703,9 @@ var methods = [ inputFormatter: formatters.inputTransactionFormatter }, { name: 'call', call: 'eth_call', addDefaultblock: 2, inputFormatter: formatters.inputCallFormatter }, - { name: 'compile.solidity', call: 'eth_compileSolidity' }, - { name: 'compile.lll', call: 'eth_compileLLL' }, - { name: 'compile.serpent', call: 'eth_compileSerpent' }, + { name: 'compile.solidity', call: 'eth_compileSolidity', inputFormatter: utils.toHex }, + { name: 'compile.lll', call: 'eth_compileLLL', inputFormatter: utils.toHex }, + { name: 'compile.serpent', call: 'eth_compileSerpent', inputFormatter: utils.toHex }, { name: 'flush', call: 'eth_flush' }, // deprecated methods @@ -1939,12 +1939,34 @@ var getOptions = function (options) { options = options || {}; - if (options.topics) - console.warn('"topics" is deprecated, is "topic" instead'); + if (options.topic) { + console.warn('"topic" is deprecated, is "topics" instead'); + options.topics = options.topic; + } + + if (options.earliest) { + console.warn('"earliest" is deprecated, is "fromBlock" instead'); + options.fromBlock = options.earliest; + } + + if (options.latest) { + console.warn('"latest" is deprecated, is "toBlock" instead'); + options.toBlock = options.latest; + } + + if (options.skip) { + console.warn('"skip" is deprecated, is "offset" instead'); + options.offset = options.skip; + } + + if (options.max) { + console.warn('"max" is deprecated, is "limit" instead'); + options.limit = options.max; + } // make sure topics, get converted to hex - if(options.topic instanceof Array) { - options.topic = options.topic.map(function(topic){ + if(options.topics instanceof Array) { + options.topics = options.topics.map(function(topic){ return utils.toHex(topic); }); } @@ -1952,13 +1974,13 @@ var getOptions = function (options) { // evaluate lazy properties return { + fromBlock: utils.toHex(options.fromBlock), + toBlock: utils.toHex(options.toBlock), + limit: utils.toHex(options.limit), + offset: utils.toHex(options.offset), to: options.to, - topic: options.topic, - earliest: options.earliest, - latest: options.latest, - max: options.max, - skip: options.skip, - address: options.address + address: options.address, + topics: options.topics }; }; @@ -2269,6 +2291,7 @@ if ("build" !== 'build') {/* */} var HttpProvider = function (host) { + this.name = 'HTTP'; this.handlers = []; this.host = host || 'http://localhost:8080'; }; @@ -2280,8 +2303,14 @@ HttpProvider.prototype.send = function (payload, callback) { // ASYNC if(typeof callback === 'function') { request.onreadystatechange = function() { - if(request.readyState === 4 && request.status === 200) { - callback(JSON.parse(request.responseText)); + if(request.readyState === 4) { + var result = ''; + try { + result = JSON.parse(request.responseText) + } catch(error) { + result = error; + } + callback(result, request.status); } }; @@ -2518,19 +2547,26 @@ var requestManager = function() { return null; } - // ASYNC (only when callback is given, and it a HttpProvidor) - if(typeof callback === 'function' && provider.host){ - provider.send(payload, function(result){ + // HTTP ASYNC (only when callback is given, and it a HttpProvidor) + if(typeof callback === 'function' && provider.name === 'HTTP'){ + provider.send(payload, function(result, status){ if (!jsonrpc.isValidResponse(result)) { - console.log(result); - if(typeof result === 'object' && result.error && result.error.message) + if(typeof result === 'object' && result.error && result.error.message) { console.error(result.error.message); + callback(result.error); + } else { + callback(new Error({ + status: status, + error: result, + message: 'Bad Request' + })); + } return null; } // format the output - callback((typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result); + callback(null, (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result); }); // SYNC diff --git a/rpc/api.go b/rpc/api.go index dc0945d198..c031688637 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -165,7 +165,7 @@ func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) erro id = self.filterManager.InstallFilter(filter) self.logs[id] = &logFilter{timeout: time.Now()} - *reply = id + *reply = i2hex(id) return nil } @@ -417,7 +417,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-JSON-RPC - rpclogger.DebugDetailf("%T %s", req.Params, req.Params) + rpclogger.Infof("%s %s", req.Method, req.Params) switch req.Method { case "web3_sha3": args := new(Sha3Args) @@ -446,7 +446,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.GetBalance(args, reply) - case "eth_getStorage": + case "eth_getStorage", "eth_storageAt": // TODO handle BlockNumber args := new(GetStorageArgs) if err := json.Unmarshal(req.Params, &args); err != nil { diff --git a/rpc/args.go b/rpc/args.go index 887d63d465..3d4c8667a7 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -3,32 +3,26 @@ package rpc import ( "bytes" "encoding/json" + "fmt" "math/big" "github.com/ethereum/go-ethereum/ethutil" ) -// Unmarshal state is a helper method which has the ability to decode messsages -// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter) -// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction -// message and the second one refers to the block height (or state) to which to apply this `call`. -func unmarshalState(b []byte, iface interface{}, str *string) (err error) { - var data []json.RawMessage - if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 { +func blockNumber(raw json.RawMessage, number *int64) (err error) { + var str string + if err = json.Unmarshal(raw, &str); err != nil { return errDecodeArgs } - if err = json.Unmarshal(data[0], iface); err != nil { - return errDecodeArgs + switch str { + case "latest": + *number = -1 + case "pending": + *number = 0 + default: + *number = ethutil.String2Big(str).Int64() } - - // Second argument is optional (transact doesn't require it) - if len(data) > 1 { - if err = json.Unmarshal(data[1], str); err != nil { - return errDecodeArgs - } - } - return nil } @@ -88,20 +82,12 @@ type NewTxArgs struct { GasPrice *big.Int Data string - BlockHeight string + BlockNumber int64 } func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { - var obj struct { - From string `json:"from"` - To string `json:"to"` - Value string `json:"value"` - Gas string `json:"gas"` - GasPrice string `json:"gasPrice"` - Data string `json:"data"` - } - var height string - if err = unmarshalState(b, &obj, &height); err != nil { + var obj struct{ From, To, Value, Gas, GasPrice, Data string } + if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil { return err } @@ -111,7 +97,6 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { args.Gas = ethutil.Big(obj.Gas) args.GasPrice = ethutil.Big(obj.GasPrice) args.Data = obj.Data - args.BlockHeight = height return nil } @@ -122,24 +107,10 @@ type GetStorageArgs struct { } func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - r := bytes.NewReader(b) - if err := json.NewDecoder(r).Decode(&obj); err != nil { + if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { return errDecodeArgs } - - if len(obj) < 1 { - return errArguments - } - args.Address = obj[0].(string) - - if len(obj) > 1 { - if obj[1].(string) == "latest" { - args.BlockNumber = -1 - } else { - args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() - } - } + fmt.Println(args) return nil } @@ -158,25 +129,18 @@ type GetStorageAtArgs struct { } func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - r := bytes.NewReader(b) - if err := json.NewDecoder(r).Decode(&obj); err != nil { + var obj []string + if err = UnmarshalRawMessages(b, &obj, &args.BlockNumber); err != nil { + return errDecodeArgs + } + if len(obj) < 2 { return errDecodeArgs } - if len(obj) < 2 { - return errArguments - } - args.Address = obj[0].(string) - args.Key = obj[1].(string) + args.Address = obj[0] + args.Key = obj[1] - if len(obj) > 2 { - if obj[2].(string) == "latest" { - args.BlockNumber = -1 - } else { - args.BlockNumber = ethutil.Big(obj[2].(string)).Int64() - } - } + fmt.Println(args) return nil } @@ -198,26 +162,10 @@ type GetTxCountArgs struct { } func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - r := bytes.NewReader(b) - if err := json.NewDecoder(r).Decode(&obj); err != nil { + if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { return errDecodeArgs } - if len(obj) < 1 { - return errArguments - - } - args.Address = obj[0].(string) - - if len(obj) > 1 { - if obj[1].(string) == "latest" { - args.BlockNumber = -1 - } else { - args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() - } - } - return nil } @@ -234,25 +182,10 @@ type GetBalanceArgs struct { } func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - r := bytes.NewReader(b) - if err := json.NewDecoder(r).Decode(&obj); err != nil { + if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { return errDecodeArgs } - if len(obj) < 1 { - return errArguments - } - args.Address = obj[0].(string) - - if len(obj) > 1 { - if obj[1].(string) == "latest" { - args.BlockNumber = -1 - } else { - args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() - } - } - return nil } @@ -269,25 +202,10 @@ type GetDataArgs struct { } func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) { - var obj []interface{} - r := bytes.NewReader(b) - if err := json.NewDecoder(r).Decode(&obj); err != nil { + if err = UnmarshalRawMessages(b, &args.Address, &args.BlockNumber); err != nil { return errDecodeArgs } - if len(obj) < 1 { - return errArguments - } - args.Address = obj[0].(string) - - if len(obj) > 1 { - if obj[1].(string) == "latest" { - args.BlockNumber = -1 - } else { - args.BlockNumber = ethutil.Big(obj[1].(string)).Int64() - } - } - return nil } diff --git a/rpc/util.go b/rpc/util.go index 69c7b629f2..fb4efdb45f 100644 --- a/rpc/util.go +++ b/rpc/util.go @@ -18,8 +18,11 @@ package rpc import ( "encoding/json" + "fmt" "io" + "math/big" "net/http" + "reflect" "time" "github.com/ethereum/go-ethereum/ethutil" @@ -32,6 +35,60 @@ var rpclogger = logger.NewLogger("RPC") type JsonWrapper struct{} +// Unmarshal state is a helper method which has the ability to decode messsages +// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter) +// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction +// message and the second one refers to the block height (or state) to which to apply this `call`. +func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error) { + var data []json.RawMessage + if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 { + return errDecodeArgs + } + + // Number index determines the index in the array for a possible block number + numberIndex := 0 + + value := reflect.ValueOf(iface) + rvalue := reflect.Indirect(value) + + switch rvalue.Kind() { + case reflect.Slice: + // This is a bit of a cheat, but `data` is expected to be larger than 2 if iface is a slice + if number != nil { + numberIndex = len(data) - 1 + } else { + numberIndex = len(data) + } + + slice := reflect.MakeSlice(rvalue.Type(), numberIndex, numberIndex) + for i, raw := range data[0:numberIndex] { + v := slice.Index(i).Interface() + if err = json.Unmarshal(raw, &v); err != nil { + fmt.Println(err, v) + return err + } + slice.Index(i).Set(reflect.ValueOf(v)) + } + reflect.Indirect(rvalue).Set(slice) //value.Set(slice) + case reflect.Struct: + fallthrough + default: + if err = json.Unmarshal(data[0], iface); err != nil { + return errDecodeArgs + } + numberIndex = 1 + } + + // <0 index means out of bound for block number + if numberIndex >= 0 && len(data) > numberIndex { + if err = blockNumber(data[numberIndex], number); err != nil { + return errDecodeArgs + } + } + + return nil +} + func (self JsonWrapper) Send(writer io.Writer, v interface{}) (n int, err error) { var payload []byte payload, err = json.Marshal(v) @@ -80,6 +137,10 @@ func fromHex(s string) []byte { return nil } +func i2hex(n int) string { + return toHex(big.NewInt(int64(n)).Bytes()) +} + type RpcServer interface { Start() Stop() From 35841e51901149a496645b3c3c6be4ec7a58e6c6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 10 Mar 2015 20:16:53 +0100 Subject: [PATCH 65/82] id to hex --- rpc/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/api.go b/rpc/api.go index 0abed3c140..511b819c11 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -195,7 +195,7 @@ func (self *EthereumApi) NewFilterString(args string, reply *interface{}) error id = self.filterManager.InstallFilter(filter) self.logs[id] = &logFilter{timeout: time.Now()} - *reply = id + *reply = i2hex(id) return nil } From ce595b9266d658a5eae626d78aec7f47c04222ca Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 10 Mar 2015 20:28:20 +0100 Subject: [PATCH 66/82] fallback --- cmd/mist/assets/examples/coin.html | 2 +- core/chain_manager.go | 2 ++ rpc/api.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html index b6bab682fe..96f2299a5f 100644 --- a/cmd/mist/assets/examples/coin.html +++ b/cmd/mist/assets/examples/coin.html @@ -72,7 +72,7 @@ // deploy if not exist if (address == null) { var code = "0x60056013565b61014f8061003a6000396000f35b620f42406000600033600160a060020a0316815260200190815260200160002081905550560060e060020a600035048063d0679d3414610020578063e3d670d71461003457005b61002e600435602435610049565b60006000f35b61003f600435610129565b8060005260206000f35b806000600033600160a060020a03168152602001908152602001600020541061007157610076565b610125565b806000600033600160a060020a03168152602001908152602001600020908154039081905550806000600084600160a060020a031681526020019081526020016000209081540190819055508033600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a38082600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a35b5050565b60006000600083600160a060020a0316815260200190815260200160002054905091905056"; - address = web3.eth.transact({data: code}); + address = web3.eth.transact({from: eth.coinbase, data: code}); localStorage.setItem("address", address); } document.querySelector("#contract_addr").innerHTML = address; diff --git a/core/chain_manager.go b/core/chain_manager.go index 9dc41f421d..97c61395ea 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -440,12 +440,14 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { self.setTotalDifficulty(td) self.insert(block) + /* XXX crashes jsonlogger.LogJson(&logger.EthChainNewHead{ BlockHash: ethutil.Bytes2Hex(block.Hash()), BlockNumber: block.Number(), ChainHeadHash: ethutil.Bytes2Hex(cblock.Hash()), BlockPrevHash: ethutil.Bytes2Hex(block.ParentHash()), }) + */ self.setTransState(state.New(block.Root(), self.stateDb)) queue[i] = ChainEvent{block} diff --git a/rpc/api.go b/rpc/api.go index 511b819c11..38f02a4735 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -556,7 +556,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.GetData(args, reply) - case "eth_sendTransaction": + case "eth_sendTransaction", "eth_transact": args := new(NewTxArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err From eba4f389a6c494bc3e15b3bbc6516b51a5e61236 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 10 Mar 2015 22:43:07 +0100 Subject: [PATCH 67/82] don't log whisper start ups --- whisper/peer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/whisper/peer.go b/whisper/peer.go index 66cfec88c4..4bd9428f5f 100644 --- a/whisper/peer.go +++ b/whisper/peer.go @@ -38,11 +38,11 @@ func (self *peer) init() error { func (self *peer) start() { go self.update() - self.peer.Infoln("whisper started") + self.peer.Debugln("whisper started") } func (self *peer) stop() { - self.peer.Infoln("whisper stopped") + self.peer.Debugln("whisper stopped") close(self.quit) } From fda4d02f946dcef8b6896c8798c318876de0138d Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 10 Mar 2015 18:08:31 -0500 Subject: [PATCH 68/82] Left-pad odd length hex inputs and tests --- rpc/util.go | 3 +++ rpc/util_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 rpc/util_test.go diff --git a/rpc/util.go b/rpc/util.go index fb4efdb45f..a0d858a678 100644 --- a/rpc/util.go +++ b/rpc/util.go @@ -132,6 +132,9 @@ func fromHex(s string) []byte { if s[0:2] == "0x" { s = s[2:] } + if len(s)%2 == 1 { + s = "0" + s + } return ethutil.Hex2Bytes(s) } return nil diff --git a/rpc/util_test.go b/rpc/util_test.go new file mode 100644 index 0000000000..b0a4979b5a --- /dev/null +++ b/rpc/util_test.go @@ -0,0 +1,25 @@ +package rpc + +import ( + "bytes" + "testing" +) + +//fromHex +func TestFromHex(t *testing.T) { + input := "0x01" + expected := []byte{1} + result := fromHex(input) + if bytes.Compare(expected, result) != 0 { + t.Errorf("Expected % x got % x", expected, result) + } +} + +func TestFromHexOddLength(t *testing.T) { + input := "0x1" + expected := []byte{1} + result := fromHex(input) + if bytes.Compare(expected, result) != 0 { + t.Errorf("Expected % x got % x", expected, result) + } +} From 499f816e3034c3e781f8ad35da070c9989cf6e3c Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 10 Mar 2015 19:10:02 -0500 Subject: [PATCH 69/82] Output hex should be prepended with 0x --- rpc/api.go | 2 +- rpc/responses.go | 65 ++++++++++++++++++++++++------------------------ rpc/util.go | 3 ++- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 511b819c11..a8c365b22a 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -455,7 +455,7 @@ func (p *EthereumApi) GetBlockUncleCountByNumber(blocknum int64) (int64, error) func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { // Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC - rpclogger.Infof("%s %s", req.Method, req.Params) + rpclogger.Debugf("%s %s", req.Method, req.Params) switch req.Method { case "web3_sha3": args := new(Sha3Args) diff --git a/rpc/responses.go b/rpc/responses.go index 06d2ffef7d..f5db285e12 100644 --- a/rpc/responses.go +++ b/rpc/responses.go @@ -5,7 +5,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethutil" ) type BlockRes struct { @@ -56,23 +55,23 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { } // convert strict types to hexified strings - ext.BlockNumber = ethutil.Bytes2Hex(big.NewInt(b.BlockNumber).Bytes()) - ext.BlockHash = ethutil.Bytes2Hex(b.BlockHash) - ext.ParentHash = ethutil.Bytes2Hex(b.ParentHash) - ext.Nonce = ethutil.Bytes2Hex(b.Nonce) - ext.Sha3Uncles = ethutil.Bytes2Hex(b.Sha3Uncles) - ext.LogsBloom = ethutil.Bytes2Hex(b.LogsBloom) - ext.TransactionRoot = ethutil.Bytes2Hex(b.TransactionRoot) - ext.StateRoot = ethutil.Bytes2Hex(b.StateRoot) - ext.Miner = ethutil.Bytes2Hex(b.Miner) - ext.Difficulty = ethutil.Bytes2Hex(big.NewInt(b.Difficulty).Bytes()) - ext.TotalDifficulty = ethutil.Bytes2Hex(big.NewInt(b.TotalDifficulty).Bytes()) - ext.Size = ethutil.Bytes2Hex(big.NewInt(b.Size).Bytes()) - ext.ExtraData = ethutil.Bytes2Hex(b.ExtraData) - ext.GasLimit = ethutil.Bytes2Hex(big.NewInt(b.GasLimit).Bytes()) - ext.MinGasPrice = ethutil.Bytes2Hex(big.NewInt(b.MinGasPrice).Bytes()) - ext.GasUsed = ethutil.Bytes2Hex(big.NewInt(b.GasUsed).Bytes()) - ext.UnixTimestamp = ethutil.Bytes2Hex(big.NewInt(b.UnixTimestamp).Bytes()) + ext.BlockNumber = toHex(big.NewInt(b.BlockNumber).Bytes()) + ext.BlockHash = toHex(b.BlockHash) + ext.ParentHash = toHex(b.ParentHash) + ext.Nonce = toHex(b.Nonce) + ext.Sha3Uncles = toHex(b.Sha3Uncles) + ext.LogsBloom = toHex(b.LogsBloom) + ext.TransactionRoot = toHex(b.TransactionRoot) + ext.StateRoot = toHex(b.StateRoot) + ext.Miner = toHex(b.Miner) + ext.Difficulty = toHex(big.NewInt(b.Difficulty).Bytes()) + ext.TotalDifficulty = toHex(big.NewInt(b.TotalDifficulty).Bytes()) + ext.Size = toHex(big.NewInt(b.Size).Bytes()) + // ext.ExtraData = toHex(b.ExtraData) + ext.GasLimit = toHex(big.NewInt(b.GasLimit).Bytes()) + // ext.MinGasPrice = toHex(big.NewInt(b.MinGasPrice).Bytes()) + ext.GasUsed = toHex(big.NewInt(b.GasUsed).Bytes()) + ext.UnixTimestamp = toHex(big.NewInt(b.UnixTimestamp).Bytes()) ext.Transactions = make([]interface{}, len(b.Transactions)) if b.fullTx { for i, tx := range b.Transactions { @@ -80,12 +79,12 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { } } else { for i, tx := range b.Transactions { - ext.Transactions[i] = ethutil.Bytes2Hex(tx.Hash) + ext.Transactions[i] = toHex(tx.Hash) } } ext.Uncles = make([]string, len(b.Uncles)) for i, v := range b.Uncles { - ext.Uncles[i] = ethutil.Bytes2Hex(v) + ext.Uncles[i] = toHex(v) } return json.Marshal(ext) @@ -103,7 +102,9 @@ func NewBlockRes(block *types.Block) *BlockRes { res.StateRoot = block.Root() res.Miner = block.Header().Coinbase res.Difficulty = block.Difficulty().Int64() - res.TotalDifficulty = block.Td.Int64() + if block.Td != nil { + res.TotalDifficulty = block.Td.Int64() + } res.Size = int64(block.Size()) // res.ExtraData = res.GasLimit = block.GasLimit().Int64() @@ -154,17 +155,17 @@ func (t *TransactionRes) MarshalJSON() ([]byte, error) { Input string `json:"input"` } - ext.Hash = ethutil.Bytes2Hex(t.Hash) - ext.Nonce = ethutil.Bytes2Hex(big.NewInt(t.Nonce).Bytes()) - ext.BlockHash = ethutil.Bytes2Hex(t.BlockHash) - ext.BlockNumber = ethutil.Bytes2Hex(big.NewInt(t.BlockNumber).Bytes()) - ext.TxIndex = ethutil.Bytes2Hex(big.NewInt(t.TxIndex).Bytes()) - ext.From = ethutil.Bytes2Hex(t.From) - ext.To = ethutil.Bytes2Hex(t.To) - ext.Value = ethutil.Bytes2Hex(big.NewInt(t.Value).Bytes()) - ext.Gas = ethutil.Bytes2Hex(big.NewInt(t.Gas).Bytes()) - ext.GasPrice = ethutil.Bytes2Hex(big.NewInt(t.GasPrice).Bytes()) - ext.Input = ethutil.Bytes2Hex(t.Input) + ext.Hash = toHex(t.Hash) + ext.Nonce = toHex(big.NewInt(t.Nonce).Bytes()) + ext.BlockHash = toHex(t.BlockHash) + ext.BlockNumber = toHex(big.NewInt(t.BlockNumber).Bytes()) + ext.TxIndex = toHex(big.NewInt(t.TxIndex).Bytes()) + ext.From = toHex(t.From) + ext.To = toHex(t.To) + ext.Value = toHex(big.NewInt(t.Value).Bytes()) + ext.Gas = toHex(big.NewInt(t.Gas).Bytes()) + ext.GasPrice = toHex(big.NewInt(t.GasPrice).Bytes()) + ext.Input = toHex(t.Input) return json.Marshal(ext) } diff --git a/rpc/util.go b/rpc/util.go index a0d858a678..8ff3c6d315 100644 --- a/rpc/util.go +++ b/rpc/util.go @@ -91,7 +91,7 @@ func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error func (self JsonWrapper) Send(writer io.Writer, v interface{}) (n int, err error) { var payload []byte - payload, err = json.Marshal(v) + payload, err = json.MarshalIndent(v, "", "\t") if err != nil { rpclogger.Fatalln("Error marshalling JSON", err) return 0, err @@ -127,6 +127,7 @@ func toHex(b []byte) string { } return "0x" + hex } + func fromHex(s string) []byte { if len(s) > 1 { if s[0:2] == "0x" { From 58909117bea6a8185df3335300426b8a49542235 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Mar 2015 01:10:25 +0100 Subject: [PATCH 70/82] Use ECDSA instead of elliptic --- crypto/key.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crypto/key.go b/crypto/key.go index ec4908c30e..9dbf374675 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -26,7 +26,6 @@ package crypto import ( "bytes" "crypto/ecdsa" - "crypto/elliptic" "encoding/json" "io" @@ -87,18 +86,16 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { } func NewKey(rand io.Reader) *Key { - randBytes := make([]byte, 32) + randBytes := make([]byte, 64) _, err := rand.Read(randBytes) if err != nil { panic("key generation: could not read from random source: " + err.Error()) } reader := bytes.NewReader(randBytes) - _, x, y, err := elliptic.GenerateKey(S256(), reader) + privateKeyECDSA, err := ecdsa.GenerateKey(S256(), reader) if err != nil { - panic("key generation: elliptic.GenerateKey failed: " + err.Error()) + panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) } - privateKeyMarshalled := elliptic.Marshal(S256(), x, y) - privateKeyECDSA := ToECDSA(privateKeyMarshalled) id := uuid.NewRandom() key := &Key{ From ece29c5d8da2a8477ec2a95c8d0307a55ab21b76 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 10 Mar 2015 19:34:07 -0500 Subject: [PATCH 71/82] Nil check block --- rpc/responses.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rpc/responses.go b/rpc/responses.go index f5db285e12..20b13f6e25 100644 --- a/rpc/responses.go +++ b/rpc/responses.go @@ -91,6 +91,10 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { } func NewBlockRes(block *types.Block) *BlockRes { + if block == nil { + return &BlockRes{} + } + res := new(BlockRes) res.BlockNumber = block.Number().Int64() res.BlockHash = block.Hash() From 9ce5229ddfccfccf1215e44477b665de031e4e1e Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 10 Mar 2015 22:25:07 -0500 Subject: [PATCH 72/82] Get transaction via block & index --- rpc/api.go | 27 +++++++++++++++++++++++++++ rpc/args.go | 10 ++++++++++ 2 files changed, 37 insertions(+) diff --git a/rpc/api.go b/rpc/api.go index a8c365b22a..510939cd77 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -593,8 +593,35 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } *reply = v case "eth_getTransactionByHash": + return errNotImplemented case "eth_getTransactionByBlockHashAndIndex": + args := new(HashIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByHash(args.BlockHash, true) + if err != nil { + return err + } + if args.TxIndex > int64(len(v.Transactions)) || args.TxIndex < 0 { + return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist") + } + *reply = v.Transactions[args.TxIndex] case "eth_getTransactionByBlockNumberAndIndex": + args := new(BlockNumIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByNumber(args.BlockNumber, true) + if err != nil { + return err + } + if args.TxIndex > int64(len(v.Transactions)) || args.TxIndex < 0 { + return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist") + } + *reply = v.Transactions[args.TxIndex] case "eth_getUncleByBlockHashAndIndex": case "eth_getUncleByBlockNumberAndIndex": return errNotImplemented diff --git a/rpc/args.go b/rpc/args.go index ab711e78f9..2f361526a6 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -217,6 +217,16 @@ func (args *GetDataArgs) requirements() error { return nil } +type BlockNumIndexArgs struct { + BlockNumber int64 + TxIndex int64 +} + +type HashIndexArgs struct { + BlockHash string + TxIndex int64 +} + type Sha3Args struct { Data string } From d465e410eff0ab255d0e427a8cf2f37ac10311da Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 11 Mar 2015 10:25:15 -0500 Subject: [PATCH 73/82] TxIndex -> Index --- rpc/api.go | 8 ++++---- rpc/args.go | 4 ++-- rpc/responses.go | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index f552b3ef3a..335f6cabd7 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -608,10 +608,10 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error if err != nil { return err } - if args.TxIndex > int64(len(v.Transactions)) || args.TxIndex < 0 { + if args.Index > int64(len(v.Transactions)) || args.Index < 0 { return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist") } - *reply = v.Transactions[args.TxIndex] + *reply = v.Transactions[args.Index] case "eth_getTransactionByBlockNumberAndIndex": args := new(BlockNumIndexArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -622,10 +622,10 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error if err != nil { return err } - if args.TxIndex > int64(len(v.Transactions)) || args.TxIndex < 0 { + if args.Index > int64(len(v.Transactions)) || args.Index < 0 { return NewErrorWithMessage(errDecodeArgs, "Transaction index does not exist") } - *reply = v.Transactions[args.TxIndex] + *reply = v.Transactions[args.Index] case "eth_getUncleByBlockHashAndIndex": case "eth_getUncleByBlockNumberAndIndex": return errNotImplemented diff --git a/rpc/args.go b/rpc/args.go index 2f361526a6..ff41e501fd 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -219,12 +219,12 @@ func (args *GetDataArgs) requirements() error { type BlockNumIndexArgs struct { BlockNumber int64 - TxIndex int64 + Index int64 } type HashIndexArgs struct { BlockHash string - TxIndex int64 + Index int64 } type Sha3Args struct { diff --git a/rpc/responses.go b/rpc/responses.go index 20b13f6e25..f41ce7b968 100644 --- a/rpc/responses.go +++ b/rpc/responses.go @@ -2,6 +2,7 @@ package rpc import ( "encoding/json" + // "fmt" "math/big" "github.com/ethereum/go-ethereum/core/types" From 53ac85cfd3188f96bc595a131732bbdbd5d49687 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 11 Mar 2015 10:27:24 -0500 Subject: [PATCH 74/82] BlockHash must be a string --- rpc/args.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rpc/args.go b/rpc/args.go index ff41e501fd..26a61b1a10 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -40,7 +40,12 @@ func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { if len(obj) < 1 { return errArguments } - args.BlockHash = obj[0].(string) + + argstr, ok := obj[0].(string) + if !ok { + return errDecodeArgs + } + args.BlockHash = argstr if len(obj) > 1 { args.Transactions = obj[1].(bool) From 9e89c803f124e98530067c409a67c03d7ab99ef3 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 11 Mar 2015 10:27:32 -0500 Subject: [PATCH 75/82] Get Uncles --- rpc/api.go | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/rpc/api.go b/rpc/api.go index 335f6cabd7..90d09d2811 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -627,8 +627,43 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } *reply = v.Transactions[args.Index] case "eth_getUncleByBlockHashAndIndex": + args := new(HashIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByHash(args.BlockHash, false) + if err != nil { + return err + } + if args.Index > int64(len(v.Uncles)) || args.Index < 0 { + return NewErrorWithMessage(errDecodeArgs, "Uncle index does not exist") + } + + uncle, err := p.GetBlockByHash(toHex(v.Uncles[args.Index]), false) + if err != nil { + return err + } + *reply = uncle case "eth_getUncleByBlockNumberAndIndex": - return errNotImplemented + args := new(BlockNumIndexArgs) + if err := json.Unmarshal(req.Params, &args); err != nil { + return err + } + + v, err := p.GetBlockByNumber(args.BlockNumber, true) + if err != nil { + return err + } + if args.Index > int64(len(v.Uncles)) || args.Index < 0 { + return NewErrorWithMessage(errDecodeArgs, "Uncle index does not exist") + } + + uncle, err := p.GetBlockByHash(toHex(v.Uncles[args.Index]), false) + if err != nil { + return err + } + *reply = uncle case "eth_getCompilers": return p.GetCompilers(reply) case "eth_compileSolidity": From 7add66c8bba3b3ad8bcc0bab4a0344c7682b7303 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Mar 2015 16:32:37 +0100 Subject: [PATCH 76/82] Use the state to up the balance of the coinbase --- miner/worker.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index 21a0522e8a..61091f3c08 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -209,6 +209,8 @@ gasLimit: err := self.commitTransaction(tx) switch { case core.IsNonceErr(err): + fallthrough + case core.IsInvalidTxErr(err): // Remove invalid transactions remove = append(remove, tx) case state.IsGasLimitErr(err): @@ -222,7 +224,7 @@ gasLimit: } self.eth.TxPool().RemoveSet(remove) - self.current.coinbase.AddBalance(core.BlockReward) + self.current.state.AddBalance(self.coinbase, core.BlockReward) self.current.state.Update(ethutil.Big0) self.push() @@ -258,9 +260,11 @@ func (self *worker) commitUncle(uncle *types.Header) error { } func (self *worker) commitTransaction(tx *types.Transaction) error { + snap := self.current.state.Copy() //fmt.Printf("proc %x %v\n", tx.Hash()[:3], tx.Nonce()) receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true) - if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err)) { + if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err) || core.IsInvalidTxErr(err)) { + self.current.state.Set(snap) return err } From 6b63759ded5766396029445af772e35e3b8c1c67 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Mar 2015 16:33:04 +0100 Subject: [PATCH 77/82] Reverted global --- cmd/ethereum/main.go | 2 +- cmd/utils/flags.go | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index c9f6201428..10d9c8fa4e 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -201,7 +201,7 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) { if len(split) != 2 { utils.Fatalf("Illegal 'unlock' format (address:password)") } - am := utils.GetAccountManager(ctx) + am := eth.AccountManager() // Attempt to unlock the account err := am.Unlock(ethutil.Hex2Bytes(split[0]), split[1]) if err != nil { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2c7d37942a..6372aaa423 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -222,15 +222,10 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database, ethutil.D return core.NewChainManager(blockDb, stateDb, new(event.TypeMux)), blockDb, stateDb } -// Global account manager -var km *accounts.Manager - func GetAccountManager(ctx *cli.Context) *accounts.Manager { dataDir := ctx.GlobalString(DataDirFlag.Name) - if km == nil { - ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) - km = accounts.NewManager(ks) - } + ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) + km := accounts.NewManager(ks) return km } From 477815c04451542a7301511e23009d6728e59ee5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Mar 2015 16:33:39 +0100 Subject: [PATCH 78/82] Improved error reporting and removed commented code --- core/block_processor.go | 17 ++++++----------- core/state_transition.go | 33 +-------------------------------- 2 files changed, 7 insertions(+), 43 deletions(-) diff --git a/core/block_processor.go b/core/block_processor.go index ea9d06841a..7ac8a1bd20 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -80,7 +80,7 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated cb := statedb.GetStateObject(coinbase.Address()) st := NewStateTransition(NewEnv(statedb, self.bc, tx, block), tx, cb) _, err := st.TransitionState() - if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err)) { + if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { return nil, nil, err } @@ -120,17 +120,12 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state for _, tx := range txs { receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess) + if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { + return nil, nil, nil, nil, err + } + if err != nil { - switch { - case IsNonceErr(err): - return nil, nil, nil, nil, err - case state.IsGasLimitErr(err): - return nil, nil, nil, nil, err - default: - statelogger.Infoln(err) - erroneous = append(erroneous, tx) - err = nil - } + statelogger.Infoln("TX err:", err) } receipts = append(receipts, receipt) handled = append(handled, tx) diff --git a/core/state_transition.go b/core/state_transition.go index 9b67de149f..b1c66d8c90 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -184,6 +184,7 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) { } } if err = self.UseGas(big.NewInt(dgas)); err != nil { + println("2") return nil, InvalidTxError(err) } @@ -200,46 +201,14 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) { ref.SetCode(ret) } } - - /* - if vmenv, ok := vmenv.(*VMEnv); ok && tryJit { - statelogger.Infof("CREATE: re-running using JIT (PH=%x)\n", stateCopy.Root()[:4]) - // re-run using the JIT (validation for the JIT) - goodState := vmenv.State().Copy() - vmenv.state = stateCopy - vmenv.SetVmType(vm.JitVmTy) - vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) - statelogger.Infof("DONE PH=%x STD_H=%x JIT_H=%x\n", stateCopy.Root()[:4], goodState.Root()[:4], vmenv.State().Root()[:4]) - self.state.Set(goodState) - } - */ } else { ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) - - /* - if vmenv, ok := vmenv.(*VMEnv); ok && tryJit { - statelogger.Infof("CALL: re-running using JIT (PH=%x)\n", stateCopy.Root()[:4]) - // re-run using the JIT (validation for the JIT) - goodState := vmenv.State().Copy() - vmenv.state = stateCopy - vmenv.SetVmType(vm.JitVmTy) - vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) - statelogger.Infof("DONE PH=%x STD_H=%x JIT_H=%x\n", stateCopy.Root()[:4], goodState.Root()[:4], vmenv.State().Root()[:4]) - self.state.Set(goodState) - } - */ } if err != nil && IsValueTransferErr(err) { return nil, InvalidTxError(err) } - /* - if err != nil { - self.UseGas(self.gas) - } - */ - return } From 08b11540332b0aa0e006142ee75336eca7c32f9b Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Mar 2015 16:36:10 +0100 Subject: [PATCH 79/82] Changed logging to use internal logger --- vm/vm.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vm/vm.go b/vm/vm.go index 6f39454721..6e4a54844f 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -37,7 +37,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I context := NewContext(caller, me, code, gas, price) - vmlogger.Debugf("(%d) (%x) %x (code=%d) gas: %v (d) %x\n", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData) + self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData).Endl() if self.Recoverable { // Recover from any require exception @@ -696,7 +696,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I if err != nil { stack.push(ethutil.BigFalse) - vmlogger.Debugln(err) + self.Printf("%v").Endl() } else { stack.push(ethutil.BigTrue) @@ -726,7 +726,7 @@ func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.I return context.Return(nil), nil default: - vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) + self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl() panic(fmt.Errorf("Invalid opcode %x", op)) } @@ -894,7 +894,7 @@ func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine { func (self *Vm) Endl() VirtualMachine { if self.debug { if self.logTy == LogTyPretty { - vmlogger.Debugln(self.logStr) + vmlogger.Infoln(self.logStr) self.logStr = "" } } From 90f34ed40a1b3c87073d7e2e13b5515cc4cb3940 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Wed, 11 Mar 2015 10:56:44 -0500 Subject: [PATCH 80/82] Rename Topic to Topics --- rpc/api.go | 2 +- rpc/args.go | 6 +++--- rpc/args_test.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 90d09d2811..337611114a 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -400,7 +400,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.Topics, args.Priority, args.Ttl) if err != nil { return err } diff --git a/rpc/args.go b/rpc/args.go index 26a61b1a10..d4d8070608 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -367,7 +367,7 @@ type WhisperMessageArgs struct { Payload string To string From string - Topic []string + Topics []string Priority uint32 Ttl uint32 } @@ -377,7 +377,7 @@ func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { Payload string To string From string - Topic []string + Topics []string Priority string Ttl string } @@ -392,7 +392,7 @@ func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { args.Payload = obj[0].Payload args.To = obj[0].To args.From = obj[0].From - args.Topic = obj[0].Topic + args.Topics = obj[0].Topics args.Priority = uint32(ethutil.Big(obj[0].Priority).Int64()) args.Ttl = uint32(ethutil.Big(obj[0].Ttl).Int64()) diff --git a/rpc/args_test.go b/rpc/args_test.go index ec5400f3fc..0276245001 100644 --- a/rpc/args_test.go +++ b/rpc/args_test.go @@ -270,7 +270,7 @@ func TestFilterOptions(t *testing.T) { t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) } - // if expected.Topic != args.Topic { + // if expected.Topics != args.Topics { // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) // } } @@ -316,7 +316,7 @@ func TestWhisperMessageArgs(t *testing.T) { expected.Payload = "0x68656c6c6f20776f726c64" expected.Priority = 100 expected.Ttl = 100 - expected.Topic = []string{"0x68656c6c6f20776f726c64"} + expected.Topics = []string{"0x68656c6c6f20776f726c64"} args := new(WhisperMessageArgs) if err := json.Unmarshal([]byte(input), &args); err != nil { @@ -343,7 +343,7 @@ func TestWhisperMessageArgs(t *testing.T) { t.Errorf("Priority shoud be %#v but is %#v", expected.Priority, args.Priority) } - // if expected.Topic != args.Topic { + // if expected.Topics != args.Topics { // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) // } } From e8d1b622d9381a2717f1a09f29078e07c23b01bd Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Mar 2015 17:00:20 +0100 Subject: [PATCH 81/82] Default block number changes --- rpc/api.go | 70 +++++++++++++++++++--------------------------------- xeth/xeth.go | 2 +- 2 files changed, 27 insertions(+), 45 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index f552b3ef3a..f214c212b6 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -22,7 +22,7 @@ import ( var ( defaultGasPrice = big.NewInt(10000000000000) - defaultGas = big.NewInt(10000) + defaultGas = big.NewInt(50000) filterTickerTime = 15 * time.Second ) @@ -44,8 +44,6 @@ type EthereumApi struct { register map[string][]*NewTxArgs db ethutil.Database - - // defaultBlockAge int64 } func NewEthereumApi(eth *xeth.XEth, dataDir string) *EthereumApi { @@ -58,7 +56,6 @@ func NewEthereumApi(eth *xeth.XEth, dataDir string) *EthereumApi { logs: make(map[int]*logFilter), messages: make(map[int]*whisperFilter), db: db, - // defaultBlockAge: -1, } go api.filterManager.Start() go api.start() @@ -66,36 +63,33 @@ func NewEthereumApi(eth *xeth.XEth, dataDir string) *EthereumApi { return api } -// func (self *EthereumApi) setStateByBlockNumber(num int64) { -// chain := self.xeth().Backend().ChainManager() -// var block *types.Block +func (self *EthereumApi) xethWithStateNum(num int64) *xeth.XEth { + 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 num < 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().StateDb())) -// } else { -// self.useState(chain.State()) -// } -// } + var st *state.StateDB + if block != nil { + st = state.New(block.Root(), self.xeth().Backend().StateDb()) + } else { + st = chain.State() + } + return self.xeth().WithState(st) +} + +func (self *EthereumApi) getStateWithNum(num int64) *xeth.State { + return self.xethWithStateNum(num).State() +} 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: - // if self.defaultBlockAge < 0 { - // self.setStateByBlockNumber(self.defaultBlockAge) - // } - // } case <-timer.C: self.logMut.Lock() self.messagesMut.Lock() @@ -277,7 +271,7 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) (err error) } func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error { - result, err := p.xeth().Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) + result, err := p.xethWithStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) if err != nil { return err } @@ -290,7 +284,7 @@ func (p *EthereumApi) GetBalance(args *GetBalanceArgs, reply *interface{}) error if err := args.requirements(); err != nil { return err } - state := p.xeth().State().SafeGet(args.Address) + state := p.getStateWithNum(args.BlockNumber).SafeGet(args.Address) *reply = toHex(state.Balance().Bytes()) return nil } @@ -299,7 +293,7 @@ func (p *EthereumApi) GetStorage(args *GetStorageArgs, reply *interface{}) error if err := args.requirements(); err != nil { return err } - *reply = p.xeth().State().SafeGet(args.Address).Storage() + *reply = p.getStateWithNum(args.BlockNumber).SafeGet(args.Address).Storage() return nil } @@ -307,7 +301,7 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageAtArgs, reply *interface{}) e if err := args.requirements(); err != nil { return err } - state := p.xeth().State().SafeGet(args.Address) + state := p.getStateWithNum(args.BlockNumber).SafeGet(args.Address) value := state.StorageString(args.Key) var hx string @@ -328,7 +322,7 @@ func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) err if err != nil { return err } - *reply = p.xeth().TxCountAt(args.Address) + *reply = p.xethWithStateNum(args.BlockNumber).TxCountAt(args.Address) return nil } @@ -336,7 +330,7 @@ func (p *EthereumApi) GetData(args *GetDataArgs, reply *interface{}) error { if err := args.requirements(); err != nil { return err } - *reply = p.xeth().CodeAt(args.Address) + *reply = p.xethWithStateNum(args.BlockNumber).CodeAt(args.Address) return nil } @@ -482,28 +476,24 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error case "eth_blockNumber": *reply = toHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes()) case "eth_getBalance": - // TODO handle BlockNumber args := new(GetBalanceArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.GetBalance(args, reply) case "eth_getStorage", "eth_storageAt": - // TODO handle BlockNumber args := new(GetStorageArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.GetStorage(args, reply) case "eth_getStorageAt": - // TODO handle BlockNumber args := new(GetStorageAtArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err } return p.GetStorageAt(args, reply) case "eth_getTransactionCount": - // TODO handle BlockNumber args := new(GetTxCountArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err @@ -554,7 +544,6 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } *reply = toHex(big.NewInt(v).Bytes()) case "eth_getData": - // TODO handle BlockNumber args := new(GetDataArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err @@ -756,13 +745,6 @@ func (self *EthereumApi) xeth() *xeth.XEth { return self.eth } -func (self *EthereumApi) useState(statedb *state.StateDB) { - self.xethMu.Lock() - defer self.xethMu.Unlock() - - self.eth = self.eth.UseState(statedb) -} - func toFilterOptions(options *FilterOptions) core.FilterOptions { var opts core.FilterOptions diff --git a/xeth/xeth.go b/xeth/xeth.go index e73cd70c9c..891a1e0726 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -98,7 +98,7 @@ func New(eth Backend, frontend Frontend) *XEth { } func (self *XEth) Backend() Backend { return self.eth } -func (self *XEth) UseState(statedb *state.StateDB) *XEth { +func (self *XEth) WithState(statedb *state.StateDB) *XEth { xeth := &XEth{ eth: self.eth, blockProcessor: self.blockProcessor, From c01d4c2f4c8704656e407ab4d80d9ec82e016731 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Mar 2015 17:36:35 +0100 Subject: [PATCH 82/82] Lowered default gas price and increased default gas limit --- core/state_transition.go | 2 ++ rpc/api.go | 5 ++--- xeth/xeth.go | 20 +++++++++++++------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index b1c66d8c90..7659e3d50a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -199,6 +199,8 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) { dataGas.Mul(dataGas, vm.GasCreateByte) if err := self.UseGas(dataGas); err == nil { ref.SetCode(ret) + } else { + statelogger.Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas) } } } else { diff --git a/rpc/api.go b/rpc/api.go index fb974883aa..8bbe80bd83 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -21,8 +21,8 @@ import ( ) var ( - defaultGasPrice = big.NewInt(10000000000000) - defaultGas = big.NewInt(50000) + defaultGasPrice = big.NewInt(150000000000) + defaultGas = big.NewInt(500000) filterTickerTime = 15 * time.Second ) @@ -252,7 +252,6 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) (err error) */ // TODO: align default values to have the same type, e.g. not depend on // ethutil.Value conversions later on - fmt.Println("gas", args.Gas) if args.Gas.Cmp(big.NewInt(0)) == 0 { args.Gas = defaultGas } diff --git a/xeth/xeth.go b/xeth/xeth.go index 891a1e0726..d8dd66aeca 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -293,14 +293,12 @@ func (self *XEth) PushTx(encodedTx string) (string, error) { return toHex(tx.Hash()), nil } -func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { - if len(gasStr) == 0 { - gasStr = "100000" - } - if len(gasPriceStr) == 0 { - gasPriceStr = "1" - } +var ( + defaultGasPrice = big.NewInt(10000000000000) + defaultGas = big.NewInt(90000) +) +func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { statedb := self.State().State() //self.chainManager.TransState() msg := callmsg{ from: statedb.GetOrNewStateObject(fromHex(fromStr)), @@ -310,6 +308,14 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st value: ethutil.Big(valueStr), data: fromHex(dataStr), } + if msg.gas.Cmp(big.NewInt(0)) == 0 { + msg.gas = defaultGas + } + + if msg.gasPrice.Cmp(big.NewInt(0)) == 0 { + msg.gasPrice = defaultGasPrice + } + block := self.chainManager.CurrentBlock() vmenv := core.NewEnv(statedb, self.chainManager, msg, block)