From 540c79e6adbb5b616e6a1e0612d69dfba07776c9 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 2 Mar 2015 09:36:14 -0600 Subject: [PATCH 01/40] 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 28024c206..782b5848c 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/40] 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 782b5848c..28ec04916 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/40] 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 f79f948d1..afb90d72a 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 c9a07bfde..0ece20d2c 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 9b66763b8..88f4cd189 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 3ea2538d1..25da04cd9 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/40] 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 f67f9c78b..27d3f3b3f 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 28ec04916..f023709ca 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 d4c188fec..8dd905b86 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/40] 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 f023709ca..406960fce 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 e839da8bf..5686cbdec 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/40] 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 406960fce..d1158afe9 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/40] 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 d1158afe9..d82432d79 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/40] 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 3e8ca3fef..69c7b629f 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/40] Update RPC methods Re-order to match spec and explicitly list unimplemented methods --- rpc/api.go | 199 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 84 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index d82432d79..d0181463d 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_newFilter": - args, err := req.ToFilterArgs() + 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.NewFilter(args, reply) - case "eth_newFilterString": - args, err := req.ToFilterStringArgs() + 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.NewFilterString(args, reply) + 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": + 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_post": + args, err := req.ToWhisperPostArgs() + if 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 { + 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_post": - args, err := req.ToWhisperPostArgs() - if err != nil { - return err - } - return p.WhisperPost(args, reply) - case "shh_haveIdentity": - args, err := req.ToWhisperHasIdentityArgs() - if err != nil { - return err - } - return p.HasWhisperIdentity(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/40] 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 d0181463d..9d00ae340 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/40] 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 9d00ae340..b97558bda 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/40] 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 b97558bda..24491833d 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 5686cbdec..42d9855b7 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 b37d8229d..3ab99d2c5 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/40] Refactor & cleanup --- javascript/types.go | 2 +- rpc/api.go | 282 ++++++++++-------------- rpc/args.go | 514 ++++++++++++++++++++++++++++++++++---------- rpc/messages.go | 390 +++------------------------------ xeth/xeth.go | 12 +- 5 files changed, 543 insertions(+), 657 deletions(-) diff --git a/javascript/types.go b/javascript/types.go index 17f1b739e..d580d14f5 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 24491833d..35c9c51d7 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) GetStorageAt(args *GetStorageAtArgs, reply *interface{}) error { - err := args.requirements() - if err != 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 { + 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 42d9855b7..3284a232a 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 len(obj) < 1 { + return errArguments } - if err = json.Unmarshal(b, &argstr); err == nil { - obj.Hash = argstr - return + args.BlockHash = obj[0].(string) + + if len(obj) > 1 { + args.Transactions = obj[1].(bool) } - return errDecodeArgs + + 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"` +type GetBlockByNumberArgs struct { + BlockNumber uint64 + Transactions bool } -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 *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 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 + if len(obj) < 1 { + return errArguments + } + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[0].(string))) - return + if len(obj) > 1 { + args.Transactions = obj[1].(bool) } - return errDecodeArgs + return nil } -type PushTxArgs struct { - Tx string `json:"tx"` +type NewTxArgs struct { + From string + To string + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Data string } -func (obj *PushTxArgs) UnmarshalJSON(b []byte) (err error) { - arg0 := "" - if err = json.Unmarshal(b, &arg0); err == nil { - obj.Tx = arg0 - return +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"` } - return errDecodeArgs -} -func (a *PushTxArgs) requirementsPushTx() error { - if a.Tx == "" { - return NewErrorWithMessage(errArguments, "PushTx requires a 'tx' as argument") + if err = json.Unmarshal(b, &obj); err != nil { + return errDecodeArgs + } + + 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 + 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 + } + + 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 errDecodeArgs + + 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 + } + + if len(obj) < 1 { + return errArguments + } - return errDecodeArgs + 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 + } + + if len(obj) < 1 { + return errArguments + } + args.Address = obj[0].(string) + + if len(obj) > 1 { + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) } - return errDecodeArgs + + 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 + } + + if len(obj) < 1 { + return errArguments + } + args.Address = obj[0].(string) + + if len(obj) > 1 { + args.BlockNumber = ethutil.BytesToNumber(fromHex(obj[1].(string))) } - return errDecodeArgs + + 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 3ab99d2c5..4f5b00990 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()) - } +// func (req *RpcRequest) ToRegisterArgs() (string, error) { +// if len(req.Params) < 1 { +// return "", errArguments +// } - return &args, nil -} - -func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) { - if len(req.Params) < 2 { - return nil, errArguments - } +// var args string +// err := json.Unmarshal(req.Params, &args) +// if err != nil { +// return "", err +// } - var args DbArgs - err := json.Unmarshal(req.Params[0], &args.Database) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } +// return args, nil +// } - err = json.Unmarshal(req.Params[1], &args.Key) - if err != nil { - return nil, NewErrorWithMessage(errDecodeArgs, err.Error()) - } +// func (req *RpcRequest) ToWatchTxArgs() (string, error) { +// if len(req.Params) < 1 { +// return "", errArguments +// } - return &args, nil -} +// var args string +// err := json.Unmarshal(req.Params, &args) +// if err != nil { +// return "", err +// } -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 -} +// return args, nil +// } diff --git a/xeth/xeth.go b/xeth/xeth.go index 8dd905b86..451d3ceea 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/40] 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 35c9c51d7..ded91def1 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 3284a232a..f56051242 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 4f5b00990..a3ebbf330 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/40] 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 a9fc16cd3..ec03822c5 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/40] 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 f56051242..ba2ef731a 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 000000000..380ed176b --- /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 dc7022cd613113b90b491cffe3eab30c102e6345 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Fri, 6 Mar 2015 09:54:08 -0600 Subject: [PATCH 17/40] Increased args test coverage --- javascript/types.go | 2 +- rpc/args.go | 126 ++++++++++-------- rpc/args_test.go | 309 ++++++++++++++++++++++++++++++++++++++++++++ xeth/xeth.go | 12 +- 4 files changed, 393 insertions(+), 56 deletions(-) diff --git a/javascript/types.go b/javascript/types.go index d580d14f5..001b50a70 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 ba2ef731a..43cad5fde 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 -} - -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 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 +// type FilterArgs struct { +// FromBlock uint64 +// ToBlock uint64 +// Limit uint64 +// Offset uint64 +// Address string +// Topics []string +// } - return nil -} +// 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 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 +// } 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 380ed176b..1e6609b79 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 451d3ceea..b78a45f88 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 fb23a0cfe06b52919107879e646e7d9b066acb0e Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 9 Mar 2015 10:55:18 -0500 Subject: [PATCH 18/40] 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 ded91def1..64e9ed285 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 19/40] 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 afb90d72a..f79f948d1 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 0ece20d2c..c9a07bfde 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 27d3f3b3f..f67f9c78b 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 88f4cd189..9b66763b8 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 25da04cd9..3ea2538d1 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 64e9ed285..ddba422f8 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 b78a45f88..091743a95 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 a7538d0020d3a51ab3b25997b3c4f01db87d4c7a Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 9 Mar 2015 18:19:35 +0100 Subject: [PATCH 20/40] 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 617e4998d..dc0945d19 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 d34cfb2fa..63969e598 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 3de51f76eea87f8e8a4b8a7277cd294529e28491 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Mon, 9 Mar 2015 20:38:37 -0500 Subject: [PATCH 21/40] 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 63969e598..9735feb62 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 bbe8b186600992ada6da9e75e9976cd5a9dc0ae3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 10 Mar 2015 13:40:49 +0100 Subject: [PATCH 22/40] Added unmarshalState --- cmd/mist/assets/examples/coin.html | 2 +- .../assets/ext/ethereum.js/dist/ethereum.js | 3459 ++++++++++------- rpc/args.go | 50 +- 3 files changed, 2002 insertions(+), 1509 deletions(-) diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html index 509a9aeeb..115145c4c 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 5b7d87270..7b2531677 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,49 +262,206 @@ 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 */ -/// required to define ETH_BIGNUMBER_ROUNDING_MODE if ("build" !== 'build') {/* var BigNumber = require('bignumber.js'); // jshint ignore:line */} -var ETH_UNITS = [ - 'wei', - 'Kwei', - 'Mwei', - 'Gwei', - 'szabo', - 'finney', - 'ether', - 'grand', - 'Mether', - 'Gether', - 'Tether', - 'Pether', - 'Eether', - 'Zether', - 'Yether', - 'Nether', - 'Dether', - 'Vether', - 'Uether' -]; +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 = { - ETH_PADDING: 32, - ETH_SIGNATURE_LENGTH: 4, - ETH_UNITS: ETH_UNITS, - ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, - ETH_POLLING_TIMEOUT: 1000 + formatInputInt: formatInputInt, + formatInputString: formatInputString, + formatInputBool: formatInputBool, + formatInputReal: formatInputReal, + formatOutputInt: formatOutputInt, + formatOutputUInt: formatOutputUInt, + formatOutputReal: formatOutputReal, + formatOutputUReal: formatOutputUReal, + formatOutputHash: formatOutputHash, + formatOutputBool: formatOutputBool, + formatOutputString: formatOutputString, + formatOutputAddress: formatOutputAddress }; -},{}],3:[function(require,module,exports){ +},{"../utils/config":4,"../utils/utils":5}],3:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -277,235 +478,71 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file contract.js +/** @file types.js * @authors: * Marek Kotewicz - * @date 2014 + * @date 2015 */ -var web3 = require('./web3'); -var abi = require('./abi'); -var utils = require('./utils'); -var eventImpl = require('./event'); -var signature = require('./signature'); - -var exportNatspecGlobals = function (vars) { - // it's used byt natspec.js - // TODO: figure out better way to solve this - web3._currentContractAbi = vars.abi; - web3._currentContractAddress = vars.address; - web3._currentContractMethodName = vars.method; - web3._currentContractMethodParams = vars.params; -}; +var f = require('./formatters'); -var addFunctionRelatedPropertiesToContract = function (contract) { - - contract.call = function (options) { - contract._isTransaction = false; - contract._options = options; - return contract; +/// @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; }; +}; - - contract.sendTransaction = function (options) { - contract._isTransaction = true; - contract._options = options; - return contract; +/// @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; }; - // DEPRECATED - contract.transact = function (options) { +}; - console.warn('myContract.transact() is deprecated please use myContract.sendTransaction() instead.'); +/// 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 } + ]; +}; - return contract.sendTransaction(options); - }; +/// Setups output formaters for solidity types +/// @returns an array of output formatters +var outputTypes = function () { - contract._options = {}; - ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) { - contract[p] = function (v) { - contract._options[p] = v; - return contract; - }; - }); + 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 }; -var addFunctionsToContract = function (contract, desc, address) { - var inputParser = abi.inputParser(desc); - var outputParser = abi.outputParser(desc); - // create contract functions - utils.filterFunctions(desc).forEach(function (method) { - - var displayName = utils.extractDisplayName(method.name); - var typeName = utils.extractTypeName(method.name); - - var impl = function () { - /*jshint maxcomplexity:7 */ - var params = Array.prototype.slice.call(arguments); - var sign = signature.functionSignatureFromAscii(method.name); - var parsed = inputParser[displayName][typeName].apply(null, params); - - var options = contract._options || {}; - options.to = address; - options.data = sign + parsed; - - var isTransaction = contract._isTransaction === true || (contract._isTransaction !== false && !method.constant); - var collapse = options.collapse !== false; - - // reset - contract._options = {}; - contract._isTransaction = null; - - if (isTransaction) { - - exportNatspecGlobals({ - abi: desc, - address: address, - method: method.name, - params: params - }); - - // transactions do not have any output, cause we do not know, when they will be processed - web3.eth.sendTransaction(options); - return; - } - - var output = web3.eth.call(options); - var ret = outputParser[displayName][typeName](output); - if (collapse) - { - if (ret.length === 1) - ret = ret[0]; - else if (ret.length === 0) - ret = null; - } - return ret; - }; - - if (contract[displayName] === undefined) { - contract[displayName] = impl; - } - - contract[displayName][typeName] = impl; - }); -}; - -var addEventRelatedPropertiesToContract = function (contract, desc, address) { - contract.address = address; - contract._onWatchEventResult = function (data) { - var matchingEvent = event.getMatchingEvent(utils.filterEvents(desc)); - var parser = eventImpl.outputParser(matchingEvent); - return parser(data); - }; - - Object.defineProperty(contract, 'topic', { - get: function() { - return utils.filterEvents(desc).map(function (e) { - return signature.eventSignatureFromAscii(e.name); - }); - } - }); - -}; - -var addEventsToContract = function (contract, desc, address) { - // create contract events - utils.filterEvents(desc).forEach(function (e) { - - var impl = function () { - var params = Array.prototype.slice.call(arguments); - var sign = signature.eventSignatureFromAscii(e.name); - var event = eventImpl.inputParser(address, sign, e); - var o = event.apply(null, params); - var outputFormatter = function (data) { - var parser = eventImpl.outputParser(e); - return parser(data); - }; - return web3.eth.filter(o, undefined, undefined, outputFormatter); - }; - - // this property should be used by eth.filter to check if object is an event - impl._isEvent = true; - - var displayName = utils.extractDisplayName(e.name); - var typeName = utils.extractTypeName(e.name); - - if (contract[displayName] === undefined) { - contract[displayName] = impl; - } - - contract[displayName][typeName] = impl; - - }); -}; - - -/** - * This method should be called when we want to call / transact some solidity method from javascript - * it returns an object which has same methods available as solidity contract description - * usage example: - * - * var abi = [{ - * name: 'myMethod', - * inputs: [{ name: 'a', type: 'string' }], - * outputs: [{name: 'd', type: 'string' }] - * }]; // contract abi - * - * var MyContract = web3.eth.contract(abi); // creation of contract prototype - * - * var contractInstance = new MyContract('0x0123123121'); - * - * contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default) - * contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit) - * contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction - * - * @param abi - abi json description of the contract, which is being created - * @returns contract object - */ -var contract = function (abi) { - - // return prototype - if(abi instanceof Array && arguments.length === 1) { - return Contract.bind(null, abi); - - // deprecated: auto initiate contract - } else { - - console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); - - return new Contract(arguments[1], arguments[0]); - } - -}; - -function Contract(abi, address) { - - // workaround for invalid assumption that method.name is the full anonymous prototype of the method. - // it's not. it's just the name. the rest of the code assumes it's actually the anonymous - // prototype, so we make it so as a workaround. - // TODO: we may not want to modify input params, maybe use copy instead? - abi.forEach(function (method) { - if (method.name.indexOf('(') === -1) { - var displayName = method.name; - var typeName = method.inputs.map(function(i){return i.type; }).join(); - method.name = displayName + '(' + typeName + ')'; - } - }); - - var result = {}; - addFunctionRelatedPropertiesToContract(result); - addFunctionsToContract(result, abi, address); - addEventRelatedPropertiesToContract(result, abi, address); - addEventsToContract(result, abi, address); - - return result; -} - -module.exports = contract; - - -},{"./abi":1,"./event":6,"./signature":14,"./utils":16,"./web3":18}],4:[function(require,module,exports){ +},{"./formatters":2}],4:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -522,26 +559,62 @@ module.exports = contract; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file db.js +/** @file config.js * @authors: * Marek Kotewicz * @date 2015 */ -/// @returns an array of objects describing web3.db api methods -var methods = function () { - return [ - { name: 'put', call: 'db_put' }, - { name: 'get', call: 'db_get' }, - { name: 'putString', call: 'db_putString' }, - { name: 'getString', call: 'db_getString' } - ]; -}; +/** + * 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 +*/} + +var ETH_UNITS = [ + 'wei', + 'Kwei', + 'Mwei', + 'Gwei', + 'szabo', + 'finney', + 'ether', + 'grand', + 'Mether', + 'Gether', + 'Tether', + 'Pether', + 'Eether', + 'Zether', + 'Yether', + 'Nether', + 'Dether', + 'Vether', + 'Uether' +]; module.exports = { - methods: methods + ETH_PADDING: 32, + ETH_SIGNATURE_LENGTH: 4, + ETH_UNITS: ETH_UNITS, + ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, + ETH_POLLING_TIMEOUT: 1000, + ETH_DEFAULTBLOCK: 'latest' }; + },{}],5:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -559,237 +632,409 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file eth.js +/** @file utils.js * @authors: * Marek Kotewicz * @date 2015 */ -var formatters = require('./formatters'); - - -var blockCall = function (args) { - return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; -}; +/** + * Utils + * + * @module utils + */ -var transactionCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; -}; +/** + * Utility functions + * + * @class [utils] utils + * @constructor + */ -var uncleCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; -}; +if ("build" !== 'build') {/* + var BigNumber = require('bignumber.js'); // jshint ignore:line +*/} -var transactionCountCall = function (args) { - return typeof args[0] === "string" ? 'eth_transactionCountByHash' : 'eth_transactionCountByNumber'; +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' }; -var uncleCountCall = function (args) { - return typeof args[0] === "string" ? 'eth_uncleCountByHash' : 'eth_uncleCountByNumber'; -}; -/// @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: 'flush', call: 'eth_flush' }, +/** 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; +}; - // 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: 'logs', call: 'eth_logs' } -]; +/** + * 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; + } -/// @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: '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'}, + str += String.fromCharCode(code); + } - // deprecated properties - { name: 'number', getter: 'eth_number', newProperty: 'blockNumber'} -]; + 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; +}; -module.exports = { - methods: methods, - properties: properties +/** + * 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; +}; -},{"./formatters":8}],6:[function(require,module,exports){ -/* - This file is part of ethereum.js. +/// @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(' ', '') : ""; +}; - 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. +/** + * 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'; + }); +}; - 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. +/** + * 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'; + }); +}; - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** @file event.js - * @authors: - * Marek Kotewicz - * @date 2014 +/** + * 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(); +}; -var abi = require('./abi'); -var utils = require('./utils'); -var signature = require('./signature'); +/** + * 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); -/// filter inputs array && returns only indexed (or not) inputs -/// @param inputs array -/// @param bool if result should be an array of indexed params on not -/// @returns array of (not?) indexed params -var filterInputs = function (inputs, indexed) { - return inputs.filter(function (current) { - return current.indexed === indexed; - }); + return (number.lessThan(0)) + ? '-0x' + result.substr(1) + : '0x' + result; }; -var inputWithName = function (inputs, name) { - var index = utils.findIndex(inputs, function (input) { - return input.name === name; - }); - - if (index === -1) { - console.error('indexed param with name ' + name + ' not found'); - return undefined; +/** + * 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 inputs[index]; + return new BigNumber(unitValue, 10); }; -var indexedParamsToTopics = function (event, indexed) { - // sort keys? - return Object.keys(indexed).map(function (key) { - var inputs = [inputWithName(filterInputs(event.inputs, true), key)]; +/** + * 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)); - var value = indexed[key]; - if (value instanceof Array) { - return value.map(function (v) { - return abi.formatInput(inputs, [v]); - }); - } - return abi.formatInput(inputs, [value]); - }); + return (isBigNumber(number)) + ? returnValue : returnValue.toString(10); }; -var inputParser = function (address, sign, event) { - - // valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.filter' - return function (indexed, options) { - var o = options || {}; - o.address = address; - o.topic = []; - o.topic.push(sign); - if (indexed) { - o.topic = o.topic.concat(indexedParamsToTopics(event, indexed)); - } - return o; - }; +/** + * 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); }; -var getArgumentsObject = function (inputs, indexed, notIndexed) { - var indexedCopy = indexed.slice(); - var notIndexedCopy = notIndexed.slice(); - return inputs.reduce(function (acc, current) { - var value; - if (current.indexed) - value = indexedCopy.splice(0, 1)[0]; - else - value = notIndexedCopy.splice(0, 1)[0]; +/** + * 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; - acc[current.name] = value; - return acc; - }, {}); + return (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) + ? new BigNumber(number.replace('0x',''), 16) + : new BigNumber(number.toString(10), 10); }; - -var outputParser = function (event) { - - return function (output) { - var result = { - event: utils.extractDisplayName(event.name), - number: output.number, - hash: output.hash, - args: {} - }; - output.topics = output.topic; // fallback for go-ethereum - if (!output.topic) { - return result; - } - - var indexedOutputs = filterInputs(event.inputs, true); - var indexedData = "0x" + output.topic.slice(1, output.topic.length).map(function (topic) { return topic.slice(2); }).join(""); - var indexedRes = abi.formatOutput(indexedOutputs, indexedData); +/** + * 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; +}; - var notIndexedOutputs = filterInputs(event.inputs, false); - var notIndexedRes = abi.formatOutput(notIndexedOutputs, output.data); +/** + * 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; + } - result.args = getArgumentsObject(event.inputs, indexedRes, notIndexedRes); + return ((address.indexOf('0x') === 0 && address.length === 42) || + (address.indexOf('0x') === -1 && address.length === 40)); +}; - return result; - }; +/** + * 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'); }; -var getMatchingEvent = function (events, payload) { - for (var i = 0; i < events.length; i++) { - var sign = signature.eventSignatureFromAscii(events[i].name); - if (sign === payload.topic[0]) { - return events[i]; - } - } - return undefined; +/** + * 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 = { - inputParser: inputParser, - outputParser: outputParser, - getMatchingEvent: getMatchingEvent + 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 }; -},{"./abi":1,"./signature":14,"./utils":16}],7:[function(require,module,exports){ +},{}],6:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -806,7 +1051,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file filter.js +/** @file web3.js * @authors: * Jeffrey Wilcke * Marek Kotewicz @@ -815,119 +1060,263 @@ module.exports = { * @date 2014 */ -/// Should be called to check if filter implementation is valid -/// @returns true if it is, otherwise false -var implementationIsValid = function (i) { - return !!i && - typeof i.newFilter === 'function' && - typeof i.getLogs === 'function' && - typeof i.uninstallFilter === 'function' && - typeof i.startPolling === 'function' && - typeof i.stopPolling === 'function'; +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; + } + + }); }; -/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones -/// @param should be string or object -/// @returns options string or object -var getOptions = function (options) { - if (typeof options === 'string') { - return options; - } +/// 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, - options = options || {}; + /// @returns ascii string representation of hex value prefixed with 0x + toAscii: utils.toAscii, - if (options.topics) { - console.warn('"topics" is deprecated, is "topic" instead'); - } + /// @returns hex representation (prefixed by 0x) of ascii string + fromAscii: utils.fromAscii, - // evaluate lazy properties - return { - to: options.to, - topic: options.topic, - earliest: options.earliest, - latest: options.latest, - max: options.max, - skip: options.skip, - address: options.address - }; -}; + /// @returns decimal representaton of hex value prefixed by 0x + toDecimal: utils.toDecimal, -/// Should be used when we want to watch something -/// it's using inner polling mechanism and is notified about changes -/// @param options are filter options -/// @param implementation, an abstract polling implementation -/// @param formatter (optional), callback function which formats output before 'real' callback -var filter = function(options, implementation, formatter) { - if (!implementationIsValid(implementation)) { - console.error('filter implemenation is invalid'); - return; - } + /// @returns hex representation (prefixed by 0x) of decimal value + fromDecimal: utils.fromDecimal, - options = getOptions(options); - var callbacks = []; - var filterId = implementation.newFilter(options); - var onMessages = function (messages) { - messages.forEach(function (message) { - message = formatter ? formatter(message) : message; - callbacks.forEach(function (callback) { - callback(message); - }); - }); - }; + /// @returns a BigNumber object + toBigNumber: utils.toBigNumber, - implementation.startPolling(filterId, onMessages, implementation.uninstallFilter); + toWei: utils.toWei, + fromWei: utils.fromWei, + isAddress: utils.isAddress, - var watch = function(callback) { - callbacks.push(callback); - }; + // provide network information + net: { + // peerCount: + }, - var stopWatching = function() { - implementation.stopPolling(filterId); - implementation.uninstallFilter(filterId); - callbacks = []; - }; - var get = function () { - return implementation.getLogs(filterId); - }; - - return { - watch: watch, - stopWatching: stopWatching, - get: get, + /// 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.'); - // DEPRECATED methods - changed: function(){ - console.warn('watch().changed() is deprecated please use filter().watch() instead.'); - return watch.apply(this, arguments); - }, - arrived: function(){ - console.warn('watch().arrived() is deprecated please use filter().watch() instead.'); - return watch.apply(this, arguments); - }, - happened: function(){ - console.warn('watch().happened() is deprecated please use filter().watch() instead.'); - return watch.apply(this, arguments); + 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; + }; }, - uninstall: function(){ - console.warn('watch().uninstall() is deprecated please use filter().stopWatching() instead.'); - return stopWatching.apply(this, arguments); + + /// @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); }, - messages: function(){ - console.warn('watch().messages() is deprecated please use filter().get() instead.'); - return get.apply(this, arguments); + // 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); }, - logs: function(){ - console.warn('watch().logs() is deprecated please use filter().get() instead.'); - return get.apply(this, arguments); + // DEPRECATED + watch: function (fil) { + console.warn('shh.watch() is deprecated please use shh.filter() instead.'); + return this.filter(fil); } - }; + } }; -module.exports = filter; + +// ADD defaultblock +Object.defineProperty(web3.eth, 'defaultBlock', { + get: function () { + return c.ETH_DEFAULTBLOCK; + }, + set: function (val) { + c.ETH_DEFAULTBLOCK = val; + return c.ETH_DEFAULTBLOCK; + } +}); -},{}],8:[function(require,module,exports){ +/// 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. @@ -944,289 +1333,235 @@ module.exports = filter; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file formatters.js +/** @file contract.js * @authors: * Marek Kotewicz - * @date 2015 + * @date 2014 */ -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; -}; +var web3 = require('../web3'); +var abi = require('../solidity/abi'); +var utils = require('../utils/utils'); +var eventImpl = require('./event'); +var signature = require('./signature'); -/// @returns right-aligned input bytes formatted to bool -var formatOutputBool = function (value) { - return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; +var exportNatspecGlobals = function (vars) { + // it's used byt natspec.js + // TODO: figure out better way to solve this + web3._currentContractAbi = vars.abi; + web3._currentContractAddress = vars.address; + web3._currentContractMethodName = vars.method; + web3._currentContractMethodParams = vars.params; }; -/// @returns left-aligned input bytes formatted to ascii string -var formatOutputString = function (value) { - return utils.toAscii(value); -}; +var addFunctionRelatedPropertiesToContract = function (contract) { + + contract.call = function (options) { + contract._isTransaction = false; + contract._options = options; + return contract; + }; -/// @returns right-aligned input bytes formatted to address -var formatOutputAddress = function (value) { - return "0x" + value.slice(value.length - 40, value.length); -}; + contract.sendTransaction = function (options) { + contract._isTransaction = true; + contract._options = options; + return contract; + }; + // DEPRECATED + contract.transact = function (options) { -/// Formats the input to a big number -/// @returns a BigNumber object -var convertToBigNumber = function (value) { + console.warn('myContract.transact() is deprecated please use myContract.sendTransaction() instead.'); - // remove the leading 0x - if(typeof value === 'string') - value = value.replace('0x', ''); + return contract.sendTransaction(options); + }; - value = value || "0"; + contract._options = {}; + ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) { + contract[p] = function (v) { + contract._options[p] = v; + return contract; + }; + }); - return new BigNumber(value, 16); }; +var addFunctionsToContract = function (contract, desc, address) { + var inputParser = abi.inputParser(desc); + var outputParser = abi.outputParser(desc); -/** -Formats the input of a transaction and converts all values to HEX - -@returns object -*/ -var inputTransactionFormatter = function(options){ + // create contract functions + utils.filterFunctions(desc).forEach(function (method) { - // make code -> data - if(options.code) { - options.data = options.code; - delete options.code; - } + var displayName = utils.extractDisplayName(method.name); + var typeName = utils.extractTypeName(method.name); - // make endowment -> value - if(options.endowment) { - options.value = options.endowment; - delete options.endowment; - } + var impl = function () { + /*jshint maxcomplexity:7 */ + var params = Array.prototype.slice.call(arguments); + var sign = signature.functionSignatureFromAscii(method.name); + var parsed = inputParser[displayName][typeName].apply(null, params); + var options = contract._options || {}; + options.to = address; + options.data = sign + parsed; + + var isTransaction = contract._isTransaction === true || (contract._isTransaction !== false && !method.constant); + var collapse = options.collapse !== false; + + // reset + contract._options = {}; + contract._isTransaction = null; - // format the following options - /*jshint maxcomplexity:5 */ - ['gasPrice', 'value'].forEach(function(key){ + if (isTransaction) { + + exportNatspecGlobals({ + abi: desc, + address: address, + method: method.name, + params: params + }); - // if hex or string integer - if(typeof options[key] === 'string') { + // transactions do not have any output, cause we do not know, when they will be processed + web3.eth.sendTransaction(options); + return; + } + + var output = web3.eth.call(options); + var ret = outputParser[displayName][typeName](output); + if (collapse) + { + if (ret.length === 1) + ret = ret[0]; + else if (ret.length === 0) + ret = null; + } + return ret; + }; - // if not hex assume its a number string - if(options[key].indexOf('0x') === -1) - options[key] = utils.fromDecimal(options[key]); + if (contract[displayName] === undefined) { + contract[displayName] = impl; + } - // if number - } else if(typeof options[key] === 'number') { - options[key] = utils.fromDecimal(options[key]); + contract[displayName][typeName] = impl; + }); +}; - // if bignumber - } else if(options[key] instanceof BigNumber) { - options[key] = '0x'+ options[key].toString(16); +var addEventRelatedPropertiesToContract = function (contract, desc, address) { + contract.address = address; + contract._onWatchEventResult = function (data) { + var matchingEvent = event.getMatchingEvent(utils.filterEvents(desc)); + var parser = eventImpl.outputParser(matchingEvent); + return parser(data); + }; + + Object.defineProperty(contract, 'topic', { + get: function() { + return utils.filterEvents(desc).map(function (e) { + return signature.eventSignatureFromAscii(e.name); + }); } }); - // format gas to number - options.gas = Number(options.gas); - - - return options; }; -/** -Formats the output of a transaction to its proper values +var addEventsToContract = function (contract, desc, address) { + // create contract events + utils.filterEvents(desc).forEach(function (e) { -@returns object -*/ -var outputTransactionFormatter = function(tx){ - // transform to number - tx.gas = Number(tx.gas); + var impl = function () { + var params = Array.prototype.slice.call(arguments); + var sign = signature.eventSignatureFromAscii(e.name); + var event = eventImpl.inputParser(address, sign, e); + var o = event.apply(null, params); + var outputFormatter = function (data) { + var parser = eventImpl.outputParser(e); + return parser(data); + }; + return web3.eth.filter(o, undefined, undefined, outputFormatter); + }; + + // this property should be used by eth.filter to check if object is an event + impl._isEvent = true; + + var displayName = utils.extractDisplayName(e.name); + var typeName = utils.extractTypeName(e.name); - // 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); + if (contract[displayName] === undefined) { + contract[displayName] = impl; + } - // 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); + contract[displayName][typeName] = impl; - return tx; + }); }; /** -Formats the output of a block to its proper values + * This method should be called when we want to call / transact some solidity method from javascript + * it returns an object which has same methods available as solidity contract description + * usage example: + * + * var abi = [{ + * name: 'myMethod', + * inputs: [{ name: 'a', type: 'string' }], + * outputs: [{name: 'd', type: 'string' }] + * }]; // contract abi + * + * var MyContract = web3.eth.contract(abi); // creation of contract prototype + * + * var contractInstance = new MyContract('0x0123123121'); + * + * contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default) + * contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit) + * contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction + * + * @param abi - abi json description of the contract, which is being created + * @returns contract object + */ +var contract = function (abi) { -@returns object -*/ -var outputBlockFormatter = function(block){ - /*jshint maxcomplexity:7 */ + // return prototype + if(abi instanceof Array && arguments.length === 1) { + return Contract.bind(null, abi); - // 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); - - // 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); - } + // deprecated: auto initiate contract + } else { + console.warn('Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead.'); - // 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); + return new Contract(arguments[1], arguments[0]); } +}; - // 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); - } +function Contract(abi, address) { - return block; -}; + // workaround for invalid assumption that method.name is the full anonymous prototype of the method. + // it's not. it's just the name. the rest of the code assumes it's actually the anonymous + // prototype, so we make it so as a workaround. + // TODO: we may not want to modify input params, maybe use copy instead? + abi.forEach(function (method) { + if (method.name.indexOf('(') === -1) { + var displayName = method.name; + var typeName = method.inputs.map(function(i){return i.type; }).join(); + method.name = displayName + '(' + typeName + ')'; + } + }); + var result = {}; + addFunctionRelatedPropertiesToContract(result); + addFunctionsToContract(result, abi, address); + addEventRelatedPropertiesToContract(result, abi, address); + addEventsToContract(result, abi, address); -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 -}; + return result; +} + +module.exports = contract; -},{"./const":2,"./utils":16}],9:[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. @@ -1243,40 +1578,27 @@ 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 db.js * @authors: * Marek Kotewicz - * Marian Oancea - * @date 2014 + * @date 2015 */ -if ("build" !== 'build') {/* - var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line -*/} - -var HttpSyncProvider = function (host) { - this.handlers = []; - this.host = host || 'http://localhost:8080'; +/// @returns an array of objects describing web3.db api methods +var methods = function () { + return [ + { name: 'put', call: 'db_put' }, + { name: 'get', call: 'db_get' }, + { name: 'putString', call: 'db_putString' }, + { name: 'getString', call: 'db_getString' } + ]; }; -HttpSyncProvider.prototype.send = function (payload) { - //var data = formatJsonRpcObject(payload); - - 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); +module.exports = { + methods: methods }; -module.exports = HttpSyncProvider; - - -},{}],10:[function(require,module,exports){ +},{}],9:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1293,57 +1615,140 @@ module.exports = HttpSyncProvider; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file jsonrpc.js +/** @file eth.js * @authors: * Marek Kotewicz * @date 2015 */ -var messageId = 1; +/** + * Web3 + * + * @module web3 + */ -/// Should be called to valid json create payload object -/// @param method of jsonrpc call, required -/// @param params, an array of method params, optional -/// @returns valid jsonrpc payload object -var toPayload = function (method, params) { - if (!method) - console.error('jsonrpc method should be specified!'); +/** + * 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 + */ - return { - jsonrpc: '2.0', - method: method, - params: params || [], - id: messageId++ - }; + +var formatters = require('./formatters'); +var utils = require('../utils/utils'); + + +var blockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; +}; + +var transactionFromBlockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; +}; + +var uncleCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex'; }; -/// Should be called to check if jsonrpc response is valid -/// @returns true if response is valid, otherwise false -var isValidResponse = function (response) { - return !!response && - !response.error && - response.jsonrpc === '2.0' && - typeof response.id === 'number' && - response.result !== undefined; // only undefined is not valid json object +var getBlockTransactionCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber'; }; -/// Should be called to create batch payload object -/// @param messages, an array of objects with method (required) and params (optional) fields -var toBatchPayload = function (messages) { - return messages.map(function (message) { - return toPayload(message.method, message.params); - }); +var uncleCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber'; }; +/// @returns an array of objects describing web3.eth api methods +var methods = [ + { 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: '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'}, + { name: 'mining', getter: 'eth_mining'}, + { name: 'gasPrice', getter: 'eth_gasPrice', outputFormatter: formatters.convertToBigNumber}, + { name: 'accounts', getter: 'eth_accounts' }, + { name: 'blockNumber', getter: 'eth_blockNumber', outputFormatter: utils.toDecimal}, + + // deprecated properties + { 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'} +]; + + module.exports = { - toPayload: toPayload, - isValidResponse: isValidResponse, - toBatchPayload: toBatchPayload + methods: methods, + properties: properties }; - -},{}],11:[function(require,module,exports){ +},{"../utils/utils":5,"./formatters":12}],10:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1360,25 +1765,130 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file qtsync.js +/** @file event.js * @authors: * Marek Kotewicz - * Marian Oancea * @date 2014 */ -var QtSyncProvider = function () { +var abi = require('../solidity/abi'); +var utils = require('../utils/utils'); +var signature = require('./signature'); + +/// filter inputs array && returns only indexed (or not) inputs +/// @param inputs array +/// @param bool if result should be an array of indexed params on not +/// @returns array of (not?) indexed params +var filterInputs = function (inputs, indexed) { + return inputs.filter(function (current) { + return current.indexed === indexed; + }); }; -QtSyncProvider.prototype.send = function (payload) { - var result = navigator.qt.callMethod(JSON.stringify(payload)); - return JSON.parse(result); +var inputWithName = function (inputs, name) { + var index = utils.findIndex(inputs, function (input) { + return input.name === name; + }); + + if (index === -1) { + console.error('indexed param with name ' + name + ' not found'); + return undefined; + } + return inputs[index]; }; -module.exports = QtSyncProvider; +var indexedParamsToTopics = function (event, indexed) { + // sort keys? + return Object.keys(indexed).map(function (key) { + var inputs = [inputWithName(filterInputs(event.inputs, true), key)]; + + var value = indexed[key]; + if (value instanceof Array) { + return value.map(function (v) { + return abi.formatInput(inputs, [v]); + }); + } + return abi.formatInput(inputs, [value]); + }); +}; + +var inputParser = function (address, sign, event) { + + // valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.filter' + return function (indexed, options) { + var o = options || {}; + o.address = address; + o.topic = []; + o.topic.push(sign); + if (indexed) { + o.topic = o.topic.concat(indexedParamsToTopics(event, indexed)); + } + return o; + }; +}; + +var getArgumentsObject = function (inputs, indexed, notIndexed) { + var indexedCopy = indexed.slice(); + var notIndexedCopy = notIndexed.slice(); + return inputs.reduce(function (acc, current) { + var value; + if (current.indexed) + value = indexedCopy.splice(0, 1)[0]; + else + value = notIndexedCopy.splice(0, 1)[0]; + + acc[current.name] = value; + return acc; + }, {}); +}; + +var outputParser = function (event) { + + return function (output) { + var result = { + event: utils.extractDisplayName(event.name), + number: output.number, + hash: output.hash, + args: {} + }; + + output.topics = output.topic; // fallback for go-ethereum + if (!output.topic) { + return result; + } + + var indexedOutputs = filterInputs(event.inputs, true); + var indexedData = "0x" + output.topic.slice(1, output.topic.length).map(function (topic) { return topic.slice(2); }).join(""); + var indexedRes = abi.formatOutput(indexedOutputs, indexedData); + + var notIndexedOutputs = filterInputs(event.inputs, false); + var notIndexedRes = abi.formatOutput(notIndexedOutputs, output.data); + + result.args = getArgumentsObject(event.inputs, indexedRes, notIndexedRes); + + return result; + }; +}; + +var getMatchingEvent = function (events, payload) { + for (var i = 0; i < events.length; i++) { + var sign = signature.eventSignatureFromAscii(events[i].name); + if (sign === payload.topic[0]) { + return events[i]; + } + } + return undefined; +}; + + +module.exports = { + inputParser: inputParser, + outputParser: outputParser, + getMatchingEvent: getMatchingEvent +}; -},{}],12:[function(require,module,exports){ +},{"../solidity/abi":1,"../utils/utils":5,"./signature":19}],11:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1395,7 +1905,7 @@ module.exports = QtSyncProvider; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file requestmanager.js +/** @file filter.js * @authors: * Jeffrey Wilcke * Marek Kotewicz @@ -1404,107 +1914,132 @@ module.exports = QtSyncProvider; * @date 2014 */ -var jsonrpc = require('./jsonrpc'); -var c = require('./const'); +var utils = require('../utils/utils'); -/** - * It's responsible for passing messages to providers - * It's also responsible for polling the ethereum node for incoming messages - * Default poll timeout is 1 second - */ -var requestManager = function() { - var polls = []; - var timeout = null; - var provider; +/// Should be called to check if filter implementation is valid +/// @returns true if it is, otherwise false +var implementationIsValid = function (i) { + return !!i && + typeof i.newFilter === 'function' && + typeof i.getLogs === 'function' && + typeof i.uninstallFilter === 'function' && + typeof i.startPolling === 'function' && + typeof i.stopPolling === 'function'; +}; - var send = function (data) { - /*jshint maxcomplexity: 6 */ +/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones +/// @param should be string or object +/// @returns options string or object +var getOptions = function (options) { + /*jshint maxcomplexity:5 */ - // format the input before sending - if(typeof data.inputFormatter === 'function') { - data.params = Array.prototype.map.call(data.params, function(item){ - return data.inputFormatter(item); - }); - } + if (typeof options === 'string') { + return options; + } - var payload = jsonrpc.toPayload(data.method, data.params); - - if (!provider) { - console.error('provider is not set'); - return null; - } + options = options || {}; - var result = provider.send(payload); + if (options.topics) + console.warn('"topics" is deprecated, is "topic" instead'); - 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; - }; + // make sure topics, get converted to hex + if(options.topic instanceof Array) { + options.topic = options.topic.map(function(topic){ + return utils.toHex(topic); + }); + } - var setProvider = function (p) { - provider = p; - }; - /*jshint maxparams:4 */ - var startPolling = function (data, pollId, callback, uninstall) { - polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall}); + // evaluate lazy properties + return { + to: options.to, + topic: options.topic, + earliest: options.earliest, + latest: options.latest, + max: options.max, + skip: options.skip, + address: options.address }; - /*jshint maxparams:3 */ +}; - var stopPolling = function (pollId) { - for (var i = polls.length; i--;) { - var poll = polls[i]; - if (poll.id === pollId) { - polls.splice(i, 1); - } - } - }; +/// Should be used when we want to watch something +/// it's using inner polling mechanism and is notified about changes +/// @param options are filter options +/// @param implementation, an abstract polling implementation +/// @param formatter (optional), callback function which formats output before 'real' callback +var filter = function(options, implementation, formatter) { + if (!implementationIsValid(implementation)) { + console.error('filter implemenation is invalid'); + return; + } - var reset = function () { - polls.forEach(function (poll) { - poll.uninstall(poll.id); + 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; + callbacks.forEach(function (callback) { + callback(message); + }); }); - polls = []; + }; + + implementation.startPolling(filterId, onMessages, implementation.uninstallFilter); + + var watch = function(callback) { + callbacks.push(callback); + }; - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - poll(); + var stopWatching = function() { + implementation.stopPolling(filterId); + implementation.uninstallFilter(filterId); + callbacks = []; }; - var poll = function () { - polls.forEach(function (data) { - var result = send(data.data); - if (!(result instanceof Array) || result.length === 0) { - return; - } - data.callback(result); - }); - timeout = setTimeout(poll, c.ETH_POLLING_TIMEOUT); + var get = function () { + return implementation.getLogs(filterId); }; - poll(); - return { - send: send, - setProvider: setProvider, - startPolling: startPolling, - stopPolling: stopPolling, - reset: reset + watch: watch, + stopWatching: stopWatching, + get: get, + + // DEPRECATED methods + changed: function(){ + console.warn('watch().changed() is deprecated please use filter().watch() instead.'); + return watch.apply(this, arguments); + }, + arrived: function(){ + console.warn('watch().arrived() is deprecated please use filter().watch() instead.'); + return watch.apply(this, arguments); + }, + happened: function(){ + console.warn('watch().happened() is deprecated please use filter().watch() instead.'); + return watch.apply(this, arguments); + }, + uninstall: function(){ + console.warn('watch().uninstall() is deprecated please use filter().stopWatching() instead.'); + return stopWatching.apply(this, arguments); + }, + messages: function(){ + console.warn('watch().messages() is deprecated please use filter().get() instead.'); + return get.apply(this, arguments); + }, + logs: function(){ + console.warn('watch().logs() is deprecated please use filter().get() instead.'); + return get.apply(this, arguments); + } }; }; -module.exports = requestManager; +module.exports = filter; -},{"./const":2,"./jsonrpc":10}],13:[function(require,module,exports){ +},{"../utils/utils":5}],12:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1521,32 +2056,191 @@ module.exports = requestManager; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file shh.js +/** @file formatters.js * @authors: * Marek Kotewicz + * Fabian Vogelsteller * @date 2015 */ -/// @returns an array of objects describing web3.shh api methods -var methods = function () { - return [ - { name: 'post', call: 'shh_post' }, - { name: 'newIdentity', call: 'shh_newIdentity' }, - { name: 'hasIdentity', call: 'shh_haveIdentity' }, - { name: 'newGroup', call: 'shh_newGroup' }, - { name: 'addToGroup', call: 'shh_addToGroup' }, +var utils = require('../utils/utils'); - // deprecated - { name: 'haveIdentity', call: 'shh_haveIdentity', newMethod: 'hasIdentity' }, - ]; +/** + * Should the input to a big number + * + * @method convertToBigNumber + * @param {String|Number|BigNumber} + * @returns {BigNumber} object + */ +var convertToBigNumber = function (value) { + return utils.toBigNumber(value); +}; + +/** + * Formats the input of a transaction and converts all values to HEX + * + * @method inputTransactionFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputTransactionFormatter = function (options){ + + // make code -> data + if (options.code) { + options.data = options.code; + delete options.code; + } + + ['gasPrice', 'gas', 'value'].forEach(function(key){ + options[key] = utils.fromDecimal(options[key]); + }); + + return options; +}; + +/** + * Formats the output of a transaction to its proper values + * + * @method outputTransactionFormatter + * @param {Object} transaction + * @returns {Object} transaction +*/ +var outputTransactionFormatter = function (tx){ + tx.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 + * + * @method outputBlockFormatter + * @param {Object} block object + * @returns {Object} block object +*/ +var outputBlockFormatter = function(block){ + + // transform to number + block.gasLimit = utils.toDecimal(block.gasLimit); + block.gasUsed = utils.toDecimal(block.gasUsed); + block.size = utils.toDecimal(block.size); + block.timestamp = utils.toDecimal(block.timestamp); + block.number = utils.toDecimal(block.number); + + block.minGasPrice = utils.toBigNumber(block.minGasPrice); + block.difficulty = utils.toBigNumber(block.difficulty); + block.totalDifficulty = utils.toBigNumber(block.totalDifficulty); + + if(block.transactions instanceof Array) { + block.transactions.forEach(function(item){ + if(!utils.isString(item)) + return outputTransactionFormatter(item); + }); + } + + return block; +}; + +/** + * Formats the output of a log + * + * @method outputLogFormatter + * @param {Object} log object + * @returns {Object} log +*/ +var outputLogFormatter = function(log){ + log.number = utils.toDecimal(log.number); + return log; +}; + + +/** + * Formats the input of a whisper post and converts all values to HEX + * + * @method inputPostFormatter + * @param {Object} transaction object + * @returns {Object} +*/ +var inputPostFormatter = function(post){ + + post.payload = utils.toHex(post.payload); + post.ttl = utils.fromDecimal(post.ttl); + post.workToProve = utils.fromDecimal(post.workToProve); + + 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 = { - methods: methods + convertToBigNumber: convertToBigNumber, + inputTransactionFormatter: inputTransactionFormatter, + outputTransactionFormatter: outputTransactionFormatter, + inputCallFormatter: inputCallFormatter, + outputBlockFormatter: outputBlockFormatter, + outputLogFormatter: outputLogFormatter, + inputPostFormatter: inputPostFormatter, + outputPostFormatter: outputPostFormatter }; -},{}],14:[function(require,module,exports){ +},{"../utils/utils":5}],13:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1563,34 +2257,54 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file signature.js +/** @file httpprovider.js * @authors: * Marek Kotewicz - * @date 2015 + * Marian Oancea + * @date 2014 */ -var web3 = require('./web3'); -var c = require('./const'); +if ("build" !== 'build') {/* + var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line +*/} -/// @param function name for which we want to get signature -/// @returns signature of function with given name -var functionSignatureFromAscii = function (name) { - return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2); +var HttpProvider = function (host) { + this.handlers = []; + this.host = host || 'http://localhost:8080'; }; -/// @param event name for which we want to get signature -/// @returns signature of event with given name -var eventSignatureFromAscii = function (name) { - return web3.sha3(web3.fromAscii(name)); -}; +HttpProvider.prototype.send = function (payload, callback) { + var request = new XMLHttpRequest(); + request.open('POST', this.host, false); -module.exports = { - functionSignatureFromAscii: functionSignatureFromAscii, - eventSignatureFromAscii: eventSignatureFromAscii + // 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 = HttpProvider; -},{"./const":2,"./web3":18}],15:[function(require,module,exports){ + +},{}],14:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1607,71 +2321,57 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file types.js +/** @file jsonrpc.js * @authors: * Marek Kotewicz * @date 2015 */ -var f = require('./formatters'); +var messageId = 1; -/// @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; - }; -}; +/// Should be called to valid json create payload object +/// @param method of jsonrpc call, required +/// @param params, an array of method params, optional +/// @returns valid jsonrpc payload object +var toPayload = function (method, params) { + if (!method) + console.error('jsonrpc method should be specified!'); -/// @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; - }; + return { + jsonrpc: '2.0', + method: method, + params: params || [], + id: messageId++ + }; }; -/// 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 } - ]; +/// Should be called to check if jsonrpc response is valid +/// @returns true if response is valid, otherwise false +var isValidResponse = function (response) { + return !!response && + !response.error && + response.jsonrpc === '2.0' && + typeof response.id === 'number' && + response.result !== undefined; // only undefined is not valid json object }; -/// Setups output formaters for solidity types -/// @returns an array of output formatters -var outputTypes = function () { +/// Should be called to create batch payload object +/// @param messages, an array of objects with method (required) and params (optional) fields +var toBatchPayload = function (messages) { + return messages.map(function (message) { + return toPayload(message.method, message.params); + }); +}; - 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 = { + toPayload: toPayload, + isValidResponse: isValidResponse, + toBatchPayload: toBatchPayload }; -module.exports = { - prefixedType: prefixedType, - namedType: namedType, - inputTypes: inputTypes, - outputTypes: outputTypes -}; -},{"./formatters":8}],16:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1688,304 +2388,228 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file utils.js +/** @file eth.js * @authors: * Marek Kotewicz * @date 2015 */ -var c = require('./const'); +var utils = require('../utils/utils'); -if ("build" !== 'build') {/* - var BigNumber = require('bignumber.js'); // jshint ignore:line -*/} +/// @returns an array of objects describing web3.eth api methods +var methods = [ + // { name: 'getBalance', call: 'eth_balanceAt', outputFormatter: formatters.convertToBigNumber}, +]; -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' -}; +/// @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 }, +]; -/// 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; +module.exports = { + methods: methods, + properties: properties }; -/// @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; -}; +},{"../utils/utils":5}],16:[function(require,module,exports){ +/* + This file is part of ethereum.js. -/// @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; -}; + 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. -/// @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; -}; + 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. -/// @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(' ', '') : ""; -}; + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file qtsync.js + * @authors: + * Marek Kotewicz + * Marian Oancea + * @date 2014 + */ -/// 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'; - }); +var QtSyncProvider = 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'; - }); +QtSyncProvider.prototype.send = function (payload) { + var result = navigator.qt.callMethod(JSON.stringify(payload)); + return JSON.parse(result); }; -/// 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]; -}; +module.exports = QtSyncProvider; -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)); -}; +},{}],17:[function(require,module,exports){ +/* + This file is part of ethereum.js. -var fromDecimal = function (val) { - return "0x" + (new BigNumber(val).toString(16)); -}; + 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. -/** -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 + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . */ -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; - } +/** @file requestmanager.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Gav Wood + * @date 2014 + */ - if(!number) - return number; +var jsonrpc = require('./jsonrpc'); +var c = require('../utils/config'); - 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 - } +/** + * It's responsible for passing messages to providers + * It's also responsible for polling the ethereum node for incoming messages + * Default poll timeout is 1 second + */ +var requestManager = function() { + var polls = []; + var timeout = null; + var provider; - number = number.dividedBy(new BigNumber(unitMap[unit], 10)); + var send = function (data, callback) { + /*jshint maxcomplexity: 7 */ - return (isBigNumber) ? number : number.toString(10); -}; + // FORMAT BASED ON ONE FORMATTER function + if(typeof data.inputFormatter === 'function') { + 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; + }); -/** -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(); + // 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 isBigNumber = true; - if(!unitMap[unit]) { - console.warn('This unit doesn\'t exists, please use the one of the following units' , unitMap); - return number; - } + var payload = jsonrpc.toPayload(data.method, data.params); + + if (!provider) { + console.error('provider is not set'); + return null; + } - if(!number) - return number; + // ASYNC (only when callback is given, and it a HttpProvidor) + if(typeof callback === 'function' && provider.host){ + provider.send(payload, function(result){ - if(typeof number === 'string' && number.indexOf('0x') === 0) { - isBigNumber = false; - number = new BigNumber(number, 16); - } + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + if(typeof result === 'object' && result.error && result.error.message) + console.error(result.error.message); + return null; + } - 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 - } + // format the output + callback((typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result); + }); + // SYNC + } else { + var result = provider.send(payload); - number = number.times(new BigNumber(unitMap[unit], 10)); + if (!jsonrpc.isValidResponse(result)) { + console.log(result); + if(typeof result === 'object' && result.error && result.error.message) + console.error(result.error.message); + return null; + } - return (isBigNumber) ? number : number.toString(10); -}; + // format the output + return (typeof data.outputFormatter === 'function') ? data.outputFormatter(result.result) : result.result; + } + + }; + var setProvider = function (p) { + provider = p; + }; -/** -Checks if the given string is a valid ethereum HEX address. + /*jshint maxparams:4 */ + var startPolling = function (data, pollId, callback, uninstall) { + polls.push({data: data, id: pollId, callback: callback, uninstall: uninstall}); + }; + /*jshint maxparams:3 */ -@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; + var stopPolling = function (pollId) { + for (var i = polls.length; i--;) { + var poll = polls[i]; + if (poll.id === pollId) { + polls.splice(i, 1); + } + } + }; - return /^\w+$/.test(address); -}; + var reset = function () { + polls.forEach(function (poll) { + poll.uninstall(poll.id); + }); + polls = []; -var isBigNumber = function (value) { - return value instanceof BigNumber || - (value && value.constructor && value.constructor.name === 'BigNumber'); -}; + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + poll(); + }; + var poll = function () { + polls.forEach(function (data) { + // 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); + }; + + poll(); -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 + return { + send: send, + setProvider: setProvider, + startPolling: startPolling, + stopPolling: stopPolling, + reset: reset + }; }; +module.exports = requestManager; + -},{"./const":2}],17:[function(require,module,exports){ +},{"../utils/config":4,"./jsonrpc":14}],18:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2002,41 +2626,34 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file watches.js +/** @file shh.js * @authors: * Marek Kotewicz * @date 2015 */ -/// @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'; - }; +var formatters = require('./formatters'); +/// @returns an array of objects describing web3.shh api methods +var methods = function () { return [ - { name: 'newFilter', call: newFilter }, - { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, - { name: 'getLogs', call: 'eth_filterLogs' } - ]; -}; + { name: 'post', call: 'shh_post', inputFormatter: formatters.inputPostFormatter }, + { name: 'newIdentity', call: 'shh_newIdentity' }, + { name: 'hasIdentity', call: 'shh_hasIdentity' }, + { name: 'newGroup', call: 'shh_newGroup' }, + { name: 'addToGroup', call: 'shh_addToGroup' }, -/// @returns an array of objects describing web3.shh.watch api methods -var shh = function () { - return [ - { name: 'newFilter', call: 'shh_newFilter' }, - { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, - { name: 'getLogs', call: 'shh_getMessages' } + // deprecated + { name: 'haveIdentity', call: 'shh_haveIdentity', newMethod: 'shh.hasIdentity' }, ]; }; module.exports = { - eth: eth, - shh: shh + methods: methods }; -},{}],18:[function(require,module,exports){ +},{"./formatters":12}],19:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2053,242 +2670,94 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** @file web3.js +/** @file signature.js * @authors: - * Jeffrey Wilcke * Marek Kotewicz - * Marian Oancea - * Gav Wood - * @date 2014 + * @date 2015 */ -// if (process.env.NODE_ENV !== 'build') { -// var BigNumber = require('bignumber.js'); -// } +var web3 = require('../web3'); +var c = require('../utils/config'); -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' } - ]; +/// @param function name for which we want to get signature +/// @returns signature of function with given name +var functionSignatureFromAscii = function (name) { + return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2); }; -/// 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; - } - - }); +/// @param event name for which we want to get signature +/// @returns signature of event with given name +var eventSignatureFromAscii = function (name) { + return web3.sha3(web3.fromAscii(name)); }; -/// 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) { +module.exports = { + functionSignatureFromAscii: functionSignatureFromAscii, + eventSignatureFromAscii: eventSignatureFromAscii +}; - // 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 - }); - }; - } +},{"../utils/config":4,"../web3":6}],20:[function(require,module,exports){ +/* + This file is part of ethereum.js. - proto.enumerable = !property.newProperty; - Object.defineProperty(obj, property.name, proto); + 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. -/*jshint maxparams:4 */ -var startPolling = function (method, id, callback, uninstall) { - web3.manager.startPolling({ - method: method, - params: [id] - }, id, callback, uninstall); -}; -/*jshint maxparams:3 */ + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** @file watches.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ -var stopPolling = function (id) { - web3.manager.stopPolling(id); -}; +/// @returns an array of objects describing web3.eth.filter api methods +var eth = function () { + var newFilter = function (args) { + return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter'; + }; -var ethWatch = { - startPolling: startPolling.bind(null, 'eth_changed'), - stopPolling: stopPolling + return [ + { name: 'newFilter', call: newFilter }, + { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, + { name: 'getLogs', call: 'eth_getFilterLogs' } + ]; }; -var shhWatch = { - startPolling: startPolling.bind(null, 'shh_changed'), - stopPolling: stopPolling +/// @returns an array of objects describing web3.shh.watch api methods +var shh = function () { + return [ + { name: 'newFilter', call: 'shh_newFilter' }, + { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, + { name: 'getLogs', call: 'shh_getMessages' } + ]; }; -/// 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); - } - } +module.exports = { + eth: eth, + shh: shh }; -/// 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 63969e598..887d63d46 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 3d6519e45bbd689b75c3ad6966c2044f29c649df Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 10 Mar 2015 12:52:45 -0500 Subject: [PATCH 23/40] 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 dc0945d19..20e0e705c 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 1e6609b79..ec5400f3f 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 000000000..06d2ffef7 --- /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 c0281f1da..67bb3c622 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 24/40] 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 | 134 ++++-------------- rpc/util.go | 61 ++++++++ 5 files changed, 151 insertions(+), 135 deletions(-) diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html index 115145c4c..b6bab682f 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 7b2531677..c0b37641c 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 dc0945d19..c03168863 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 887d63d46..3d4c8667a 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 - } - - // Second argument is optional (transact doesn't require it) - if len(data) > 1 { - if err = json.Unmarshal(data[1], str); err != nil { - return errDecodeArgs - } + switch str { + case "latest": + *number = -1 + case "pending": + *number = 0 + default: + *number = ethutil.String2Big(str).Int64() } - 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 errArguments + return errDecodeArgs } - args.Address = obj[0].(string) - args.Key = obj[1].(string) - if len(obj) > 2 { - if obj[2].(string) == "latest" { - args.BlockNumber = -1 - } else { - args.BlockNumber = ethutil.Big(obj[2].(string)).Int64() - } - } + args.Address = obj[0] + args.Key = obj[1] + + 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 69c7b629f..fb4efdb45 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 25/40] 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 0abed3c14..511b819c1 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 26/40] 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 b6bab682f..96f2299a5 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 9dc41f421..97c61395e 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 511b819c1..38f02a473 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 fda4d02f946dcef8b6896c8798c318876de0138d Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 10 Mar 2015 18:08:31 -0500 Subject: [PATCH 27/40] 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 fb4efdb45..a0d858a67 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 000000000..b0a4979b5 --- /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 28/40] 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 511b819c1..a8c365b22 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 06d2ffef7..f5db285e1 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 a0d858a67..8ff3c6d31 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 ece29c5d8da2a8477ec2a95c8d0307a55ab21b76 Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Tue, 10 Mar 2015 19:34:07 -0500 Subject: [PATCH 29/40] Nil check block --- rpc/responses.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rpc/responses.go b/rpc/responses.go index f5db285e1..20b13f6e2 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 30/40] 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 a8c365b22..510939cd7 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 ab711e78f..2f361526a 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 31/40] 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 f552b3ef3..335f6cabd 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 2f361526a..ff41e501f 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 20b13f6e2..f41ce7b96 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 32/40] 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 ff41e501f..26a61b1a1 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 33/40] Get Uncles --- rpc/api.go | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/rpc/api.go b/rpc/api.go index 335f6cabd..90d09d281 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 34/40] 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 21a0522e8..61091f3c0 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 35/40] 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 c9f620142..10d9c8fa4 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 2c7d37942..6372aaa42 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 36/40] 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 ea9d06841..7ac8a1bd2 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 9b67de149..b1c66d8c9 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 37/40] 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 6f3945472..6e4a54844 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 38/40] 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 90d09d281..337611114 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 26a61b1a1..d4d807060 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 ec5400f3f..027624500 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 39/40] 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 f552b3ef3..f214c212b 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 e73cd70c9..891a1e072 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 40/40] 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 b1c66d8c9..7659e3d50 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 fb974883a..8bbe80bd8 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 891a1e072..d8dd66aec 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)