aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/filter.go4
-rw-r--r--event/filter/eth_filter.go11
-rw-r--r--rpc/api.go735
-rw-r--r--rpc/api_test.go64
-rw-r--r--rpc/args.go26
-rw-r--r--rpc/args_test.go32
-rw-r--r--rpc/http.go16
-rw-r--r--rpc/messages_test.go41
-rw-r--r--rpc/responses.go26
-rw-r--r--rpc/util.go86
-rw-r--r--xeth/xeth.go324
11 files changed, 635 insertions, 730 deletions
diff --git a/core/filter.go b/core/filter.go
index f1627636f..b5d9deb7a 100644
--- a/core/filter.go
+++ b/core/filter.go
@@ -34,7 +34,7 @@ type Filter struct {
topics [][]common.Hash
BlockCallback func(*types.Block, state.Logs)
- PendingCallback func(*types.Block, state.Logs)
+ PendingCallback func(*types.Transaction)
LogsCallback func(state.Logs)
}
@@ -46,7 +46,7 @@ func NewFilter(eth Backend) *Filter {
// SetOptions copies the filter options to the filter it self. The reason for this "silly" copy
// is simply because named arguments in this case is extremely nice and readable.
-func (self *Filter) SetOptions(options FilterOptions) {
+func (self *Filter) SetOptions(options *FilterOptions) {
self.earliest = options.Earliest
self.latest = options.Latest
self.skip = options.Skip
diff --git a/event/filter/eth_filter.go b/event/filter/eth_filter.go
index cb75d7e1a..1abd48389 100644
--- a/event/filter/eth_filter.go
+++ b/event/filter/eth_filter.go
@@ -48,7 +48,9 @@ func (self *FilterManager) InstallFilter(filter *core.Filter) (id int) {
func (self *FilterManager) UninstallFilter(id int) {
self.filterMu.Lock()
defer self.filterMu.Unlock()
- delete(self.filters, id)
+ if _, ok := self.filters[id]; ok {
+ delete(self.filters, id)
+ }
}
// GetFilter retrieves a filter installed using InstallFilter.
@@ -62,8 +64,9 @@ func (self *FilterManager) GetFilter(id int) *core.Filter {
func (self *FilterManager) filterLoop() {
// Subscribe to events
events := self.eventMux.Subscribe(
- core.PendingBlockEvent{},
+ //core.PendingBlockEvent{},
core.ChainEvent{},
+ core.TxPreEvent{},
state.Logs(nil))
out:
@@ -82,11 +85,11 @@ out:
}
self.filterMu.RUnlock()
- case core.PendingBlockEvent:
+ case core.TxPreEvent:
self.filterMu.RLock()
for _, filter := range self.filters {
if filter.PendingCallback != nil {
- filter.PendingCallback(event.Block, event.Logs)
+ filter.PendingCallback(event.Tx)
}
}
self.filterMu.RUnlock()
diff --git a/rpc/api.go b/rpc/api.go
index 39a6b8f5b..659bc373d 100644
--- a/rpc/api.go
+++ b/rpc/api.go
@@ -2,491 +2,49 @@ package rpc
import (
"encoding/json"
- "fmt"
"math/big"
"path"
- "strings"
"sync"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/event/filter"
- "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/xeth"
)
-var (
- defaultGasPrice = big.NewInt(150000000000)
- defaultGas = big.NewInt(500000)
- filterTickerTime = 5 * time.Minute
-)
-
type EthereumApi struct {
eth *xeth.XEth
xethMu sync.RWMutex
- mux *event.TypeMux
-
- quit chan struct{}
- filterManager *filter.FilterManager
-
- logMut sync.RWMutex
- logs map[int]*logFilter
-
- messagesMut sync.RWMutex
- messages map[int]*whisperFilter
- // Register keeps a list of accounts and transaction data
- regmut sync.Mutex
- register map[string][]*NewTxArgs
-
- db common.Database
+ db common.Database
}
func NewEthereumApi(eth *xeth.XEth, dataDir string) *EthereumApi {
+ // What about when dataDir is empty?
db, _ := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps"))
api := &EthereumApi{
- eth: eth,
- mux: eth.Backend().EventMux(),
- quit: make(chan struct{}),
- filterManager: filter.NewFilterManager(eth.Backend().EventMux()),
- logs: make(map[int]*logFilter),
- messages: make(map[int]*whisperFilter),
- db: db,
+ eth: eth,
+ db: db,
}
- go api.filterManager.Start()
- go api.start()
return api
}
-func (self *EthereumApi) xethWithStateNum(num int64) *xeth.XEth {
- chain := self.xeth().Backend().ChainManager()
- var block *types.Block
-
- if num < 0 {
- num = chain.CurrentBlock().Number().Int64() + num + 1
- }
- block = chain.GetBlockByNumber(uint64(num))
-
- 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(2 * time.Second)
-done:
- for {
- select {
- case <-timer.C:
- self.logMut.Lock()
- self.messagesMut.Lock()
- for id, filter := range self.logs {
- if time.Since(filter.timeout) > filterTickerTime {
- self.filterManager.UninstallFilter(id)
- delete(self.logs, id)
- }
- }
-
- for id, filter := range self.messages {
- if time.Since(filter.timeout) > filterTickerTime {
- self.xeth().Whisper().Unwatch(id)
- delete(self.messages, id)
- }
- }
- self.messagesMut.Unlock()
- self.logMut.Unlock()
- case <-self.quit:
- break done
- }
- }
-}
-
-func (self *EthereumApi) stop() {
- close(self.quit)
-}
-
-// func (self *EthereumApi) Register(args string, reply *interface{}) error {
-// self.regmut.Lock()
-// defer self.regmut.Unlock()
-
-// 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()
-
-// delete(self.register, args)
-
-// return nil
-// }
-
-// func (self *EthereumApi) WatchTx(args string, reply *interface{}) error {
-// self.regmut.Lock()
-// defer self.regmut.Unlock()
-
-// txs := self.register[args]
-// self.register[args] = nil
-
-// *reply = txs
-// return nil
-// }
-
-func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error {
- var id int
- filter := core.NewFilter(self.xeth().Backend())
- filter.SetOptions(toFilterOptions(args))
- filter.LogsCallback = func(logs state.Logs) {
- self.logMut.Lock()
- defer self.logMut.Unlock()
-
- self.logs[id].add(logs...)
- }
- id = self.filterManager.InstallFilter(filter)
- self.logs[id] = &logFilter{timeout: time.Now()}
-
- *reply = common.ToHex(big.NewInt(int64(id)).Bytes())
-
- return nil
-}
-
-func (self *EthereumApi) UninstallFilter(id int, reply *interface{}) error {
- if _, ok := self.logs[id]; ok {
- delete(self.logs, id)
- }
-
- self.filterManager.UninstallFilter(id)
- *reply = true
- return nil
-}
-
-func (self *EthereumApi) NewFilterString(args *FilterStringArgs, reply *interface{}) error {
- var id int
- filter := core.NewFilter(self.xeth().Backend())
-
- callback := func(block *types.Block, logs state.Logs) {
- self.logMut.Lock()
- defer self.logMut.Unlock()
-
- for _, log := range logs {
- self.logs[id].add(log)
- }
- self.logs[id].add(&state.StateLog{})
- }
-
- switch args.Word {
- case "pending":
- filter.PendingCallback = callback
- case "latest":
- filter.BlockCallback = callback
- default:
- return NewValidationError("Word", "Must be `latest` or `pending`")
- }
-
- id = self.filterManager.InstallFilter(filter)
- self.logs[id] = &logFilter{timeout: time.Now()}
- *reply = common.ToHex(big.NewInt(int64(id)).Bytes())
-
- return nil
-}
-
-func (self *EthereumApi) FilterChanged(id int, reply *interface{}) error {
- self.logMut.Lock()
- defer self.logMut.Unlock()
-
- if self.logs[id] != nil {
- *reply = toLogs(self.logs[id].get())
- }
-
- return nil
-}
-
-func (self *EthereumApi) Logs(id int, reply *interface{}) error {
- self.logMut.Lock()
- defer self.logMut.Unlock()
-
- filter := self.filterManager.GetFilter(id)
- if filter != nil {
- *reply = toLogs(filter.Find())
- }
-
- return nil
-}
-
-func (self *EthereumApi) AllLogs(args *FilterOptions, reply *interface{}) error {
- filter := core.NewFilter(self.xeth().Backend())
- filter.SetOptions(toFilterOptions(args))
-
- *reply = toLogs(filter.Find())
-
- return nil
-}
-
-func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) (err error) {
- // TODO if no_private_key then
- //if _, exists := p.register[args.From]; exists {
- // p.register[args.From] = append(p.register[args.From], args)
- //} else {
- /*
- account := accounts.Get(common.FromHex(args.From))
- if account != nil {
- if account.Unlocked() {
- if !unlockAccount(account) {
- return
- }
- }
-
- result, _ := account.Transact(common.FromHex(args.To), common.FromHex(args.Value), common.FromHex(args.Gas), common.FromHex(args.GasPrice), common.FromHex(args.Data))
- if len(result) > 0 {
- *reply = common.ToHex(result)
- }
- } else if _, exists := p.register[args.From]; exists {
- p.register[ags.From] = append(p.register[args.From], args)
- }
- */
-
- if err := args.requirements(); err != nil {
- return err
- }
-
- // TODO: align default values to have the same type, e.g. not depend on
- // common.Value conversions later on
- if args.Gas.Cmp(big.NewInt(0)) == 0 {
- args.Gas = defaultGas
- }
-
- if args.GasPrice.Cmp(big.NewInt(0)) == 0 {
- args.GasPrice = defaultGasPrice
- }
-
- *reply, err = p.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
- if err != nil {
- fmt.Println("err:", err)
- return err
- }
-
- return nil
-}
-
-func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error {
- 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
- }
-
- *reply = result
- return nil
-}
-
-func (p *EthereumApi) GetBalance(args *GetBalanceArgs, reply *interface{}) error {
- if err := args.requirements(); err != nil {
- return err
- }
- state := p.getStateWithNum(args.BlockNumber).SafeGet(args.Address)
- *reply = common.ToHex(state.Balance().Bytes())
- return nil
-}
-
-func (p *EthereumApi) GetStorage(args *GetStorageArgs, reply *interface{}) error {
- if err := args.requirements(); err != nil {
- return err
- }
- *reply = p.getStateWithNum(args.BlockNumber).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.getStateWithNum(args.BlockNumber).SafeGet(args.Address)
-
- value := state.StorageString(args.Key)
- var hx string
- if strings.Index(args.Key, "0x") == 0 {
- hx = string([]byte(args.Key)[2:])
- } else {
- // Convert the incoming string (which is a bigint) into hex
- i, _ := new(big.Int).SetString(args.Key, 10)
- hx = common.Bytes2Hex(i.Bytes())
- }
- rpclogger.Debugf("GetStateAt(%s, %s)\n", args.Address, hx)
- *reply = map[string]string{args.Key: value.Str()}
- return nil
-}
-
-func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) error {
- err := args.requirements()
- if err != nil {
- return err
- }
- *reply = p.xethWithStateNum(args.BlockNumber).TxCountAt(args.Address)
- return nil
-}
-
-func (p *EthereumApi) GetData(args *GetDataArgs, reply *interface{}) error {
- if err := args.requirements(); err != nil {
- return err
- }
- *reply = p.xethWithStateNum(args.BlockNumber).CodeAt(args.Address)
- return nil
-}
-
-func (p *EthereumApi) GetCompilers(reply *interface{}) error {
- c := []string{""}
- *reply = c
- return nil
-}
-
-func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error {
- if err := args.requirements(); err != nil {
- return err
- }
-
- p.db.Put([]byte(args.Database+args.Key), []byte(args.Value))
- *reply = true
- return nil
-}
-
-func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error {
- if err := args.requirements(); err != nil {
- return err
- }
-
- res, _ := p.db.Get([]byte(args.Database + args.Key))
- *reply = string(res)
- return nil
-}
-
-func (p *EthereumApi) NewWhisperIdentity(reply *interface{}) error {
- *reply = p.xeth().Whisper().NewIdentity()
- return nil
-}
-
-// func (p *EthereumApi) RemoveWhisperIdentity(args *WhisperIdentityArgs, reply *interface{}) error {
-// *reply = p.xeth().Whisper().RemoveIdentity(args.Identity)
-// return nil
-// }
-
-func (p *EthereumApi) NewWhisperFilter(args *WhisperFilterArgs, reply *interface{}) error {
- var id int
- 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(opts)
- p.messages[id] = &whisperFilter{timeout: time.Now()}
- *reply = common.ToHex(big.NewInt(int64(id)).Bytes())
- return nil
-}
-
-func (p *EthereumApi) UninstallWhisperFilter(id int, reply *interface{}) error {
- delete(p.messages, id)
- *reply = true
- return nil
-}
-
-func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error {
- self.messagesMut.Lock()
- defer self.messagesMut.Unlock()
-
- if self.messages[id] != nil {
- *reply = self.messages[id].get()
- }
-
- return nil
-}
-
-func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error {
- err := p.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
- if err != nil {
- return err
- }
-
- *reply = true
- return nil
-}
-
-func (p *EthereumApi) HasWhisperIdentity(args string, reply *interface{}) error {
- *reply = p.xeth().Whisper().HasIdentity(args)
- return nil
-}
-
-func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error {
- *reply = p.xeth().Whisper().Messages(id)
- return nil
-}
-
-func (p *EthereumApi) GetTransactionByHash(hash string, reply *interface{}) error {
- tx := p.xeth().EthTransactionByHash(hash)
- if tx != nil {
- *reply = NewTransactionRes(tx)
- }
- 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 (self *EthereumApi) xeth() *xeth.XEth {
+ self.xethMu.RLock()
+ defer self.xethMu.RUnlock()
-func (p *EthereumApi) GetBlockUncleCountByHash(blockhash string) (int64, error) {
- block := p.xeth().EthBlockByHash(blockhash)
- br := NewBlockRes(block)
- return int64(len(br.Uncles)), nil
+ return self.eth
}
-func (p *EthereumApi) GetBlockUncleCountByNumber(blocknum int64) (int64, error) {
- block := p.xeth().EthBlockByNumber(blocknum)
- br := NewBlockRes(block)
- return int64(len(br.Uncles)), nil
+func (self *EthereumApi) xethAtStateNum(num int64) *xeth.XEth {
+ return self.xeth().AtStateNum(num)
}
func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
// Spec at https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC
rpclogger.Debugf("%s %s", req.Method, req.Params)
+
switch req.Method {
case "web3_sha3":
args := new(Sha3Args)
@@ -501,7 +59,8 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
case "net_listening":
*reply = p.xeth().IsListening()
case "net_peerCount":
- *reply = common.ToHex(big.NewInt(int64(p.xeth().PeerCount())).Bytes())
+ v := p.xeth().PeerCount()
+ *reply = common.ToHex(big.NewInt(int64(v)).Bytes())
case "eth_coinbase":
// TODO handling of empty coinbase due to lack of accounts
res := p.xeth().Coinbase()
@@ -513,97 +72,131 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
case "eth_mining":
*reply = p.xeth().IsMining()
case "eth_gasPrice":
- *reply = common.ToHex(defaultGasPrice.Bytes())
+ v := p.xeth().DefaultGas()
+ *reply = common.ToHex(v.Bytes())
case "eth_accounts":
*reply = p.xeth().Accounts()
case "eth_blockNumber":
- *reply = common.ToHex(p.xeth().Backend().ChainManager().CurrentBlock().Number().Bytes())
+ v := p.xeth().Backend().ChainManager().CurrentBlock().Number()
+ *reply = common.ToHex(v.Bytes())
case "eth_getBalance":
args := new(GetBalanceArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.GetBalance(args, reply)
+
+ if err := args.requirements(); err != nil {
+ return err
+ }
+
+ v := p.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance()
+ *reply = common.ToHex(v.Bytes())
case "eth_getStorage", "eth_storageAt":
args := new(GetStorageArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.GetStorage(args, reply)
+
+ if err := args.requirements(); err != nil {
+ return err
+ }
+
+ *reply = p.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage()
case "eth_getStorageAt":
args := new(GetStorageAtArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.GetStorageAt(args, reply)
+ if err := args.requirements(); err != nil {
+ return err
+ }
+
+ state := p.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address)
+ value := state.StorageString(args.Key)
+
+ *reply = common.Bytes2Hex(value.Bytes())
case "eth_getTransactionCount":
args := new(GetTxCountArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.GetTxCountAt(args, reply)
+
+ err := args.requirements()
+ if err != nil {
+ return err
+ }
+
+ *reply = p.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address)
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 = common.ToHex(big.NewInt(v).Bytes())
+ block := NewBlockRes(p.xeth().EthBlockByHash(args.BlockHash))
+ *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).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 = common.ToHex(big.NewInt(v).Bytes())
+ block := NewBlockRes(p.xeth().EthBlockByNumber(args.BlockNumber))
+ *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).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 = common.ToHex(big.NewInt(v).Bytes())
+ block := p.xeth().EthBlockByHash(args.BlockHash)
+ br := NewBlockRes(block)
+ *reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes())
case "eth_getUncleCountByBlockNumber":
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 = common.ToHex(big.NewInt(v).Bytes())
+ block := p.xeth().EthBlockByNumber(args.BlockNumber)
+ br := NewBlockRes(block)
+ *reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes())
case "eth_getData", "eth_getCode":
args := new(GetDataArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.GetData(args, reply)
+ if err := args.requirements(); err != nil {
+ return err
+ }
+ *reply = p.xethAtStateNum(args.BlockNumber).CodeAt(args.Address)
case "eth_sendTransaction", "eth_transact":
args := new(NewTxArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.Transact(args, reply)
+
+ if err := args.requirements(); err != nil {
+ return err
+ }
+
+ v, err := p.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
+ if err != nil {
+ return err
+ }
+ *reply = v
case "eth_call":
args := new(NewTxArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.Call(args, reply)
+
+ v, err := p.xethAtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
+ if err != nil {
+ return err
+ }
+
+ *reply = v
case "eth_flush":
return NewNotImplementedError(req.Method)
case "eth_getBlockByHash":
@@ -612,52 +205,55 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
return err
}
- v, err := p.GetBlockByHash(args.BlockHash, args.Transactions)
- if err != nil {
- return err
- }
- *reply = v
+ block := p.xeth().EthBlockByHash(args.BlockHash)
+ br := NewBlockRes(block)
+ br.fullTx = args.IncludeTxs
+
+ *reply = br
case "eth_getBlockByNumber":
args := new(GetBlockByNumberArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- v, err := p.GetBlockByNumber(args.BlockNumber, args.Transactions)
- if err != nil {
- return err
- }
- *reply = v
+ block := p.xeth().EthBlockByNumber(args.BlockNumber)
+ br := NewBlockRes(block)
+ br.fullTx = args.IncludeTxs
+
+ *reply = br
case "eth_getTransactionByHash":
// HashIndexArgs used, but only the "Hash" part we need.
args := new(HashIndexArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
}
- return p.GetTransactionByHash(args.Hash, reply)
+ tx := p.xeth().EthTransactionByHash(args.Hash)
+ if tx != nil {
+ *reply = NewTransactionRes(tx)
+ }
case "eth_getTransactionByBlockHashAndIndex":
args := new(HashIndexArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- v, err := p.GetBlockByHash(args.Hash, true)
- if err != nil {
- return err
- }
- if args.Index > int64(len(v.Transactions)) || args.Index < 0 {
+ block := p.xeth().EthBlockByHash(args.Hash)
+ br := NewBlockRes(block)
+ br.fullTx = true
+
+ if args.Index > int64(len(br.Transactions)) || args.Index < 0 {
return NewValidationError("Index", "does not exist")
}
- *reply = v.Transactions[args.Index]
+ *reply = br.Transactions[args.Index]
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
- }
+ block := p.xeth().EthBlockByNumber(args.BlockNumber)
+ v := NewBlockRes(block)
+ v.fullTx = true
+
if args.Index > int64(len(v.Transactions)) || args.Index < 0 {
return NewValidationError("Index", "does not exist")
}
@@ -668,18 +264,15 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
return err
}
- v, err := p.GetBlockByHash(args.Hash, false)
- if err != nil {
- return err
- }
- if args.Index > int64(len(v.Uncles)) || args.Index < 0 {
+ br := NewBlockRes(p.xeth().EthBlockByHash(args.Hash))
+
+ if args.Index > int64(len(br.Uncles)) || args.Index < 0 {
return NewValidationError("Index", "does not exist")
}
- uncle, err := p.GetBlockByHash(v.Uncles[args.Index].Hex(), false)
- if err != nil {
- return err
- }
+ uhash := br.Uncles[args.Index].Hex()
+ uncle := NewBlockRes(p.xeth().EthBlockByHash(uhash))
+
*reply = uncle
case "eth_getUncleByBlockNumberAndIndex":
args := new(BlockNumIndexArgs)
@@ -687,59 +280,68 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
return err
}
- v, err := p.GetBlockByNumber(args.BlockNumber, true)
- if err != nil {
- return err
- }
+ block := p.xeth().EthBlockByNumber(args.BlockNumber)
+ v := NewBlockRes(block)
+ v.fullTx = true
+
if args.Index > int64(len(v.Uncles)) || args.Index < 0 {
return NewValidationError("Index", "does not exist")
}
- uncle, err := p.GetBlockByHash(v.Uncles[args.Index].Hex(), false)
- if err != nil {
- return err
- }
+ uhash := v.Uncles[args.Index].Hex()
+ uncle := NewBlockRes(p.xeth().EthBlockByHash(uhash))
+
*reply = uncle
case "eth_getCompilers":
- return p.GetCompilers(reply)
+ c := []string{""}
+ *reply = c
case "eth_compileSolidity", "eth_compileLLL", "eth_compileSerpent":
return NewNotImplementedError(req.Method)
case "eth_newFilter":
- args := new(FilterOptions)
+ args := new(BlockFilterArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.NewFilter(args, reply)
+
+ opts := toFilterOptions(args)
+ id := p.xeth().RegisterFilter(opts)
+ *reply = common.ToHex(big.NewInt(int64(id)).Bytes())
case "eth_newBlockFilter":
args := new(FilterStringArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.NewFilterString(args, reply)
+ if err := args.requirements(); err != nil {
+ return err
+ }
+
+ id := p.xeth().NewFilterString(args.Word)
+ *reply = common.ToHex(big.NewInt(int64(id)).Bytes())
case "eth_uninstallFilter":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.UninstallFilter(args.Id, reply)
+ *reply = p.xeth().UninstallFilter(args.Id)
case "eth_getFilterChanges":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.FilterChanged(args.Id, reply)
+ *reply = NewLogsRes(p.xeth().FilterChanged(args.Id))
case "eth_getFilterLogs":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.Logs(args.Id, reply)
+ *reply = NewLogsRes(p.xeth().Logs(args.Id))
case "eth_getLogs":
- args := new(FilterOptions)
+ args := new(BlockFilterArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.AllLogs(args, reply)
+ opts := toFilterOptions(args)
+ *reply = NewLogsRes(p.xeth().AllLogs(opts))
case "eth_getWork", "eth_submitWork":
return NewNotImplementedError(req.Method)
case "db_putString":
@@ -747,13 +349,25 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.DbPut(args, reply)
+
+ if err := args.requirements(); err != nil {
+ return err
+ }
+
+ p.db.Put([]byte(args.Database+args.Key), []byte(args.Value))
+ *reply = true
case "db_getString":
args := new(DbArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.DbGet(args, reply)
+
+ if err := args.requirements(); err != nil {
+ return err
+ }
+
+ res, _ := p.db.Get([]byte(args.Database + args.Key))
+ *reply = string(res)
case "db_putHex", "db_getHex":
return NewNotImplementedError(req.Method)
case "shh_post":
@@ -761,21 +375,27 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.WhisperPost(args, reply)
+
+ err := p.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
+ if err != nil {
+ return err
+ }
+
+ *reply = true
case "shh_newIdentity":
- return p.NewWhisperIdentity(reply)
+ *reply = p.xeth().Whisper().NewIdentity()
// case "shh_removeIdentity":
// args := new(WhisperIdentityArgs)
// if err := json.Unmarshal(req.Params, &args); err != nil {
// return err
// }
- // return p.RemoveWhisperIdentity(args, reply)
+ // *reply = p.xeth().Whisper().RemoveIdentity(args.Identity)
case "shh_hasIdentity":
args := new(WhisperIdentityArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.HasWhisperIdentity(args.Identity, reply)
+ *reply = p.xeth().Whisper().HasIdentity(args.Identity)
case "shh_newGroup", "shh_addToGroup":
return NewNotImplementedError(req.Method)
case "shh_newFilter":
@@ -783,43 +403,49 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.NewWhisperFilter(args, reply)
+ opts := new(xeth.Options)
+ opts.From = args.From
+ opts.To = args.To
+ opts.Topics = args.Topics
+ id := p.xeth().NewWhisperFilter(opts)
+ *reply = common.ToHex(big.NewInt(int64(id)).Bytes())
case "shh_uninstallFilter":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.UninstallWhisperFilter(args.Id, reply)
+ *reply = p.xeth().UninstallWhisperFilter(args.Id)
case "shh_getFilterChanges":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.MessagesChanged(args.Id, reply)
+ *reply = p.xeth().MessagesChanged(args.Id)
case "shh_getMessages":
args := new(FilterIdArgs)
if err := json.Unmarshal(req.Params, &args); err != nil {
return err
}
- return p.WhisperMessages(args.Id, reply)
+ *reply = p.xeth().Whisper().Messages(args.Id)
// case "eth_register":
- // args, err := req.ToRegisterArgs()
- // if err != nil {
+ // // Placeholder for actual type
+ // args := new(HashIndexArgs)
+ // if err := json.Unmarshal(req.Params, &args); err != nil {
// return err
// }
- // return p.Register(args, reply)
+ // *reply = p.xeth().Register(args.Hash)
// case "eth_unregister":
- // args, err := req.ToRegisterArgs()
- // if err != nil {
+ // args := new(HashIndexArgs)
+ // if err := json.Unmarshal(req.Params, &args); err != nil {
// return err
// }
- // return p.Unregister(args, reply)
+ // *reply = p.xeth().Unregister(args.Hash)
// case "eth_watchTx":
- // args, err := req.ToWatchTxArgs()
- // if err != nil {
+ // args := new(HashIndexArgs)
+ // if err := json.Unmarshal(req.Params, &args); err != nil {
// return err
// }
- // return p.WatchTx(args, reply)
+ // *reply = p.xeth().PullWatchTx(args.Hash)
default:
return NewNotImplementedError(req.Method)
}
@@ -828,14 +454,7 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error
return nil
}
-func (self *EthereumApi) xeth() *xeth.XEth {
- self.xethMu.RLock()
- defer self.xethMu.RUnlock()
-
- return self.eth
-}
-
-func toFilterOptions(options *FilterOptions) core.FilterOptions {
+func toFilterOptions(options *BlockFilterArgs) *core.FilterOptions {
var opts core.FilterOptions
// Convert optional address slice/string to byte slice
@@ -867,5 +486,5 @@ func toFilterOptions(options *FilterOptions) core.FilterOptions {
}
opts.Topics = topics
- return opts
+ return &opts
}
diff --git a/rpc/api_test.go b/rpc/api_test.go
index ec03822c5..727ade007 100644
--- a/rpc/api_test.go
+++ b/rpc/api_test.go
@@ -2,9 +2,9 @@ package rpc
import (
"encoding/json"
- "sync"
+ // "sync"
"testing"
- "time"
+ // "time"
)
func TestWeb3Sha3(t *testing.T) {
@@ -24,33 +24,33 @@ func TestWeb3Sha3(t *testing.T) {
}
}
-func TestFilterClose(t *testing.T) {
- t.Skip()
- api := &EthereumApi{
- logs: make(map[int]*logFilter),
- messages: make(map[int]*whisperFilter),
- quit: make(chan struct{}),
- }
-
- filterTickerTime = 1
- api.logs[0] = &logFilter{}
- api.messages[0] = &whisperFilter{}
- var wg sync.WaitGroup
- wg.Add(1)
- go api.start()
- go func() {
- select {
- case <-time.After(500 * time.Millisecond):
- api.stop()
- wg.Done()
- }
- }()
- wg.Wait()
- if len(api.logs) != 0 {
- t.Error("expected logs to be empty")
- }
-
- if len(api.messages) != 0 {
- t.Error("expected messages to be empty")
- }
-}
+// func TestFilterClose(t *testing.T) {
+// t.Skip()
+// api := &EthereumApi{
+// logs: make(map[int]*logFilter),
+// messages: make(map[int]*whisperFilter),
+// quit: make(chan struct{}),
+// }
+
+// filterTickerTime = 1
+// api.logs[0] = &logFilter{}
+// api.messages[0] = &whisperFilter{}
+// var wg sync.WaitGroup
+// wg.Add(1)
+// go api.start()
+// go func() {
+// select {
+// case <-time.After(500 * time.Millisecond):
+// api.stop()
+// wg.Done()
+// }
+// }()
+// wg.Wait()
+// if len(api.logs) != 0 {
+// t.Error("expected logs to be empty")
+// }
+
+// if len(api.messages) != 0 {
+// t.Error("expected messages to be empty")
+// }
+// }
diff --git a/rpc/args.go b/rpc/args.go
index ab1c40585..e50c9b1f5 100644
--- a/rpc/args.go
+++ b/rpc/args.go
@@ -35,8 +35,8 @@ func blockAge(raw interface{}, number *int64) (err error) {
}
type GetBlockByHashArgs struct {
- BlockHash string
- Transactions bool
+ BlockHash string
+ IncludeTxs bool
}
func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
@@ -57,15 +57,15 @@ func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
args.BlockHash = argstr
if len(obj) > 1 {
- args.Transactions = obj[1].(bool)
+ args.IncludeTxs = obj[1].(bool)
}
return nil
}
type GetBlockByNumberArgs struct {
- BlockNumber int64
- Transactions bool
+ BlockNumber int64
+ IncludeTxs bool
}
func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
@@ -86,7 +86,7 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
}
if len(obj) > 1 {
- args.Transactions = obj[1].(bool)
+ args.IncludeTxs = obj[1].(bool)
}
return nil
@@ -433,7 +433,7 @@ func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) {
return nil
}
-type FilterOptions struct {
+type BlockFilterArgs struct {
Earliest int64
Latest int64
Address interface{}
@@ -442,7 +442,7 @@ type FilterOptions struct {
Max int
}
-func (args *FilterOptions) UnmarshalJSON(b []byte) (err error) {
+func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) {
var obj []struct {
FromBlock interface{} `json:"fromBlock"`
ToBlock interface{} `json:"toBlock"`
@@ -609,6 +609,16 @@ func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) {
return nil
}
+func (args *FilterStringArgs) requirements() error {
+ switch args.Word {
+ case "latest", "pending":
+ break
+ default:
+ return NewValidationError("Word", "Must be `latest` or `pending`")
+ }
+ return nil
+}
+
type FilterIdArgs struct {
Id int
}
diff --git a/rpc/args_test.go b/rpc/args_test.go
index 0d8dc4085..cfdd278b8 100644
--- a/rpc/args_test.go
+++ b/rpc/args_test.go
@@ -82,7 +82,7 @@ func TestGetBlockByHashArgs(t *testing.T) {
input := `["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true]`
expected := new(GetBlockByHashArgs)
expected.BlockHash = "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
- expected.Transactions = true
+ expected.IncludeTxs = true
args := new(GetBlockByHashArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
@@ -93,8 +93,8 @@ func TestGetBlockByHashArgs(t *testing.T) {
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)
+ if args.IncludeTxs != expected.IncludeTxs {
+ t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs)
}
}
@@ -112,7 +112,7 @@ func TestGetBlockByNumberArgs(t *testing.T) {
input := `["0x1b4", false]`
expected := new(GetBlockByNumberArgs)
expected.BlockNumber = 436
- expected.Transactions = false
+ expected.IncludeTxs = false
args := new(GetBlockByNumberArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
@@ -123,8 +123,8 @@ func TestGetBlockByNumberArgs(t *testing.T) {
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)
+ if args.IncludeTxs != expected.IncludeTxs {
+ t.Errorf("IncludeTxs should be %v but is %v", expected.IncludeTxs, args.IncludeTxs)
}
}
@@ -388,7 +388,7 @@ func TestGetDataEmptyArgs(t *testing.T) {
}
}
-func TestFilterOptions(t *testing.T) {
+func TestBlockFilterArgs(t *testing.T) {
input := `[{
"fromBlock": "0x1",
"toBlock": "0x2",
@@ -396,7 +396,7 @@ func TestFilterOptions(t *testing.T) {
"offset": "0x0",
"address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8",
"topics": ["0x12341234"]}]`
- expected := new(FilterOptions)
+ expected := new(BlockFilterArgs)
expected.Earliest = 1
expected.Latest = 2
expected.Max = 3
@@ -404,7 +404,7 @@ func TestFilterOptions(t *testing.T) {
expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"
// expected.Topics = []string{"0x12341234"}
- args := new(FilterOptions)
+ args := new(BlockFilterArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
@@ -434,16 +434,16 @@ func TestFilterOptions(t *testing.T) {
// }
}
-func TestFilterOptionsWords(t *testing.T) {
+func TestBlockFilterArgsWords(t *testing.T) {
input := `[{
"fromBlock": "latest",
"toBlock": "pending"
}]`
- expected := new(FilterOptions)
+ expected := new(BlockFilterArgs)
expected.Earliest = 0
expected.Latest = -1
- args := new(FilterOptions)
+ args := new(BlockFilterArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
t.Error(err)
}
@@ -457,13 +457,13 @@ func TestFilterOptionsWords(t *testing.T) {
}
}
-func TestFilterOptionsNums(t *testing.T) {
+func TestBlockFilterArgsNums(t *testing.T) {
input := `[{
"fromBlock": 2,
"toBlock": 3
}]`
- args := new(FilterOptions)
+ args := new(BlockFilterArgs)
err := json.Unmarshal([]byte(input), &args)
switch err.(type) {
case *DecodeParamError:
@@ -474,10 +474,10 @@ func TestFilterOptionsNums(t *testing.T) {
}
-func TestFilterOptionsEmptyArgs(t *testing.T) {
+func TestBlockFilterArgsEmptyArgs(t *testing.T) {
input := `[]`
- args := new(FilterOptions)
+ args := new(BlockFilterArgs)
err := json.Unmarshal([]byte(input), &args)
if err == nil {
t.Error("Expected error but didn't get one")
diff --git a/rpc/http.go b/rpc/http.go
index 5f2445e6c..3dfb67781 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -10,7 +10,7 @@ import (
"github.com/ethereum/go-ethereum/xeth"
)
-var rpchttplogger = logger.NewLogger("RPC-HTTP")
+var rpclogger = logger.NewLogger("RPC")
const (
jsonrpcver = "2.0"
@@ -28,7 +28,7 @@ func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler {
// Limit request size to resist DoS
if req.ContentLength > maxSizeReqLength {
jsonerr := &RpcErrorObject{-32700, "Request too large"}
- Send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
+ send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
return
}
@@ -37,14 +37,14 @@ func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
jsonerr := &RpcErrorObject{-32700, "Could not read request body"}
- Send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
+ send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
}
// Try to parse the request as a single
var reqSingle RpcRequest
if err := json.Unmarshal(body, &reqSingle); err == nil {
response := RpcResponse(api, &reqSingle)
- Send(w, &response)
+ send(w, &response)
return
}
@@ -57,13 +57,13 @@ func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler {
response := RpcResponse(api, &request)
resBatch[i] = response
}
- Send(w, resBatch)
+ send(w, resBatch)
return
}
// Not a batch or single request, error
jsonerr := &RpcErrorObject{-32600, "Could not decode request"}
- Send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
+ send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
})
}
@@ -84,11 +84,11 @@ func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} {
response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
}
- rpchttplogger.DebugDetailf("Generated response: %T %s", response, response)
+ rpclogger.DebugDetailf("Generated response: %T %s", response, response)
return &response
}
-func Send(writer io.Writer, v interface{}) (n int, err error) {
+func send(writer io.Writer, v interface{}) (n int, err error) {
var payload []byte
payload, err = json.MarshalIndent(v, "", "\t")
if err != nil {
diff --git a/rpc/messages_test.go b/rpc/messages_test.go
new file mode 100644
index 000000000..5274c91e4
--- /dev/null
+++ b/rpc/messages_test.go
@@ -0,0 +1,41 @@
+package rpc
+
+import (
+ "testing"
+)
+
+func TestInsufficientParamsError(t *testing.T) {
+ err := NewInsufficientParamsError(0, 1)
+ expected := "insufficient params, want 1 have 0"
+
+ if err.Error() != expected {
+ t.Error(err.Error())
+ }
+}
+
+func TestNotImplementedError(t *testing.T) {
+ err := NewNotImplementedError("foo")
+ expected := "foo method not implemented"
+
+ if err.Error() != expected {
+ t.Error(err.Error())
+ }
+}
+
+func TestDecodeParamError(t *testing.T) {
+ err := NewDecodeParamError("foo")
+ expected := "could not decode, foo"
+
+ if err.Error() != expected {
+ t.Error(err.Error())
+ }
+}
+
+func TestValidationError(t *testing.T) {
+ err := NewValidationError("foo", "should be `bar`")
+ expected := "foo not valid, should be `bar`"
+
+ if err.Error() != expected {
+ t.Error(err.Error())
+ }
+}
diff --git a/rpc/responses.go b/rpc/responses.go
index 5e197b729..ce64f1581 100644
--- a/rpc/responses.go
+++ b/rpc/responses.go
@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/state"
)
type BlockRes struct {
@@ -215,3 +216,28 @@ type FilterWhisperRes struct {
Payload string `json:"payload"`
WorkProved string `json:"workProved"`
}
+
+type LogRes struct {
+ Address string `json:"address"`
+ Topic []string `json:"topic"`
+ Data string `json:"data"`
+ Number uint64 `json:"number"`
+}
+
+func NewLogsRes(logs state.Logs) (ls []LogRes) {
+ ls = make([]LogRes, len(logs))
+
+ for i, log := range logs {
+ var l LogRes
+ l.Topic = make([]string, len(log.Topics()))
+ l.Address = log.Address().Hex()
+ l.Data = common.ToHex(log.Data())
+ l.Number = log.Number()
+ for j, topic := range log.Topics() {
+ l.Topic[j] = topic.Hex()
+ }
+ ls[i] = l
+ }
+
+ return
+}
diff --git a/rpc/util.go b/rpc/util.go
deleted file mode 100644
index 724c8b503..000000000
--- a/rpc/util.go
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- This file is part of go-ethereum
-
- go-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- go-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
-*/
-package rpc
-
-import (
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/state"
- "github.com/ethereum/go-ethereum/xeth"
-)
-
-var rpclogger = logger.NewLogger("RPC")
-
-type Log struct {
- Address string `json:"address"`
- Topic []string `json:"topic"`
- Data string `json:"data"`
- Number uint64 `json:"number"`
-}
-
-func toLogs(logs state.Logs) (ls []Log) {
- ls = make([]Log, len(logs))
-
- for i, log := range logs {
- var l Log
- l.Topic = make([]string, len(log.Topics()))
- l.Address = log.Address().Hex()
- l.Data = common.ToHex(log.Data())
- l.Number = log.Number()
- for j, topic := range log.Topics() {
- l.Topic[j] = topic.Hex()
- }
- ls[i] = l
- }
-
- return
-}
-
-type whisperFilter struct {
- messages []xeth.WhisperMessage
- timeout time.Time
- id int
-}
-
-func (w *whisperFilter) add(msgs ...xeth.WhisperMessage) {
- w.messages = append(w.messages, msgs...)
-}
-func (w *whisperFilter) get() []xeth.WhisperMessage {
- w.timeout = time.Now()
- tmp := w.messages
- w.messages = nil
- return tmp
-}
-
-type logFilter struct {
- logs state.Logs
- timeout time.Time
- id int
-}
-
-func (l *logFilter) add(logs ...state.Log) {
- l.logs = append(l.logs, logs...)
-}
-
-func (l *logFilter) get() state.Logs {
- l.timeout = time.Now()
- tmp := l.logs
- l.logs = nil
- return tmp
-}
diff --git a/xeth/xeth.go b/xeth/xeth.go
index 686ed4432..2c324be26 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -6,6 +6,8 @@ import (
"encoding/json"
"fmt"
"math/big"
+ "sync"
+ "time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
@@ -13,13 +15,19 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/event/filter"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/whisper"
)
-var pipelogger = logger.NewLogger("XETH")
+var (
+ pipelogger = logger.NewLogger("XETH")
+ filterTickerTime = 5 * time.Minute
+ defaultGasPrice = big.NewInt(10000000000000) //150000000000
+ defaultGas = big.NewInt(90000) //500000
+)
// to resolve the import cycle
type Backend interface {
@@ -62,6 +70,13 @@ type Frontend interface {
ConfirmTransaction(tx *types.Transaction) bool
}
+// dummyFrontend is a non-interactive frontend that allows all
+// transactions but cannot not unlock any keys.
+type dummyFrontend struct{}
+
+func (dummyFrontend) UnlockAccount([]byte) bool { return false }
+func (dummyFrontend) ConfirmTransaction(*types.Transaction) bool { return true }
+
type XEth struct {
eth Backend
blockProcessor *core.BlockProcessor
@@ -71,14 +86,19 @@ type XEth struct {
whisper *Whisper
frontend Frontend
-}
-// dummyFrontend is a non-interactive frontend that allows all
-// transactions but cannot not unlock any keys.
-type dummyFrontend struct{}
+ quit chan struct{}
+ filterManager *filter.FilterManager
-func (dummyFrontend) UnlockAccount([]byte) bool { return false }
-func (dummyFrontend) ConfirmTransaction(*types.Transaction) bool { return true }
+ logMut sync.RWMutex
+ logs map[int]*logFilter
+
+ messagesMut sync.RWMutex
+ messages map[int]*whisperFilter
+
+ // regmut sync.Mutex
+ // register map[string][]*interface{} // TODO improve return type
+}
// New creates an XEth that uses the given frontend.
// If a nil Frontend is provided, a default frontend which
@@ -90,15 +110,76 @@ func New(eth Backend, frontend Frontend) *XEth {
chainManager: eth.ChainManager(),
accountManager: eth.AccountManager(),
whisper: NewWhisper(eth.Whisper()),
+ quit: make(chan struct{}),
+ filterManager: filter.NewFilterManager(eth.EventMux()),
frontend: frontend,
+ logs: make(map[int]*logFilter),
+ messages: make(map[int]*whisperFilter),
}
if frontend == nil {
xeth.frontend = dummyFrontend{}
}
xeth.state = NewState(xeth, xeth.chainManager.TransState())
+ go xeth.start()
+ go xeth.filterManager.Start()
+
return xeth
}
+func (self *XEth) start() {
+ timer := time.NewTicker(2 * time.Second)
+done:
+ for {
+ select {
+ case <-timer.C:
+ self.logMut.Lock()
+ self.messagesMut.Lock()
+ for id, filter := range self.logs {
+ if time.Since(filter.timeout) > filterTickerTime {
+ self.filterManager.UninstallFilter(id)
+ delete(self.logs, id)
+ }
+ }
+
+ for id, filter := range self.messages {
+ if time.Since(filter.timeout) > filterTickerTime {
+ self.Whisper().Unwatch(id)
+ delete(self.messages, id)
+ }
+ }
+ self.messagesMut.Unlock()
+ self.logMut.Unlock()
+ case <-self.quit:
+ break done
+ }
+ }
+}
+
+func (self *XEth) stop() {
+ close(self.quit)
+}
+
+func (self *XEth) DefaultGas() *big.Int { return defaultGas }
+func (self *XEth) DefaultGasPrice() *big.Int { return defaultGasPrice }
+
+func (self *XEth) AtStateNum(num int64) *XEth {
+ chain := self.Backend().ChainManager()
+ var block *types.Block
+
+ if num < 0 {
+ num = chain.CurrentBlock().Number().Int64() + num + 1
+ }
+ block = chain.GetBlockByNumber(uint64(num))
+
+ var st *state.StateDB
+ if block != nil {
+ st = state.New(block.Root(), self.Backend().StateDb())
+ } else {
+ st = chain.State()
+ }
+ return self.WithState(st)
+}
+
func (self *XEth) Backend() Backend { return self.eth }
func (self *XEth) WithState(statedb *state.StateDB) *XEth {
xeth := &XEth{
@@ -241,6 +322,157 @@ func (self *XEth) SecretToAddress(key string) string {
return common.ToHex(pair.Address())
}
+func (self *XEth) RegisterFilter(args *core.FilterOptions) int {
+ var id int
+ filter := core.NewFilter(self.Backend())
+ filter.SetOptions(args)
+ filter.LogsCallback = func(logs state.Logs) {
+ self.logMut.Lock()
+ defer self.logMut.Unlock()
+
+ self.logs[id].add(logs...)
+ }
+ id = self.filterManager.InstallFilter(filter)
+ self.logs[id] = &logFilter{timeout: time.Now()}
+
+ return id
+}
+
+func (self *XEth) UninstallFilter(id int) bool {
+ if _, ok := self.logs[id]; ok {
+ delete(self.logs, id)
+ self.filterManager.UninstallFilter(id)
+ return true
+ }
+
+ return false
+}
+
+func (self *XEth) NewFilterString(word string) int {
+ var id int
+ filter := core.NewFilter(self.Backend())
+
+ switch word {
+ case "pending":
+ filter.PendingCallback = func(tx *types.Transaction) {
+ self.logMut.Lock()
+ defer self.logMut.Unlock()
+
+ self.logs[id].add(&state.StateLog{})
+ }
+ case "latest":
+ filter.BlockCallback = func(block *types.Block, logs state.Logs) {
+ self.logMut.Lock()
+ defer self.logMut.Unlock()
+
+ for _, log := range logs {
+ self.logs[id].add(log)
+ }
+ self.logs[id].add(&state.StateLog{})
+ }
+ }
+
+ id = self.filterManager.InstallFilter(filter)
+ self.logs[id] = &logFilter{timeout: time.Now()}
+
+ return id
+}
+
+func (self *XEth) FilterChanged(id int) state.Logs {
+ self.logMut.Lock()
+ defer self.logMut.Unlock()
+
+ if self.logs[id] != nil {
+ return self.logs[id].get()
+ }
+
+ return nil
+}
+
+func (self *XEth) Logs(id int) state.Logs {
+ self.logMut.Lock()
+ defer self.logMut.Unlock()
+
+ filter := self.filterManager.GetFilter(id)
+ if filter != nil {
+ return filter.Find()
+ }
+
+ return nil
+}
+
+func (self *XEth) AllLogs(args *core.FilterOptions) state.Logs {
+ filter := core.NewFilter(self.Backend())
+ filter.SetOptions(args)
+
+ return filter.Find()
+}
+
+func (p *XEth) NewWhisperFilter(opts *Options) int {
+ var id int
+ opts.Fn = func(msg WhisperMessage) {
+ p.messagesMut.Lock()
+ defer p.messagesMut.Unlock()
+ p.messages[id].add(msg) // = append(p.messages[id], msg)
+ }
+ id = p.Whisper().Watch(opts)
+ p.messages[id] = &whisperFilter{timeout: time.Now()}
+ return id
+}
+
+func (p *XEth) UninstallWhisperFilter(id int) bool {
+ if _, ok := p.messages[id]; ok {
+ delete(p.messages, id)
+ return true
+ }
+
+ return false
+}
+
+func (self *XEth) MessagesChanged(id int) []WhisperMessage {
+ self.messagesMut.Lock()
+ defer self.messagesMut.Unlock()
+
+ if self.messages[id] != nil {
+ return self.messages[id].get()
+ }
+
+ return nil
+}
+
+// func (self *XEth) Register(args string) bool {
+// self.regmut.Lock()
+// defer self.regmut.Unlock()
+
+// if _, ok := self.register[args]; ok {
+// self.register[args] = nil // register with empty
+// }
+// return true
+// }
+
+// func (self *XEth) Unregister(args string) bool {
+// self.regmut.Lock()
+// defer self.regmut.Unlock()
+
+// if _, ok := self.register[args]; ok {
+// delete(self.register, args)
+// return true
+// }
+
+// return false
+// }
+
+// // TODO improve return type
+// func (self *XEth) PullWatchTx(args string) []*interface{} {
+// self.regmut.Lock()
+// defer self.regmut.Unlock()
+
+// txs := self.register[args]
+// self.register[args] = nil
+
+// return txs
+// }
+
type KeyVal struct {
Key string `json:"key"`
Value string `json:"value"`
@@ -298,11 +530,6 @@ func (self *XEth) PushTx(encodedTx string) (string, error) {
return tx.Hash().Hex(), nil
}
-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{
@@ -333,12 +560,44 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt
from = common.HexToAddress(fromStr)
to = common.HexToAddress(toStr)
value = common.NewValue(valueStr)
- gas = common.NewValue(gasStr)
- price = common.NewValue(gasPriceStr)
+ gas = common.Big(gasStr)
+ price = common.Big(gasPriceStr)
data []byte
contractCreation bool
)
+ // TODO if no_private_key then
+ //if _, exists := p.register[args.From]; exists {
+ // p.register[args.From] = append(p.register[args.From], args)
+ //} else {
+ /*
+ account := accounts.Get(common.FromHex(args.From))
+ if account != nil {
+ if account.Unlocked() {
+ if !unlockAccount(account) {
+ return
+ }
+ }
+
+ result, _ := account.Transact(common.FromHex(args.To), common.FromHex(args.Value), common.FromHex(args.Gas), common.FromHex(args.GasPrice), common.FromHex(args.Data))
+ if len(result) > 0 {
+ *reply = common.ToHex(result)
+ }
+ } else if _, exists := p.register[args.From]; exists {
+ p.register[ags.From] = append(p.register[args.From], args)
+ }
+ */
+
+ // TODO: align default values to have the same type, e.g. not depend on
+ // common.Value conversions later on
+ if gas.Cmp(big.NewInt(0)) == 0 {
+ gas = defaultGas
+ }
+
+ if price.Cmp(big.NewInt(0)) == 0 {
+ price = defaultGasPrice
+ }
+
data = common.FromHex(codeStr)
if len(toStr) == 0 {
contractCreation = true
@@ -346,9 +605,9 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt
var tx *types.Transaction
if contractCreation {
- tx = types.NewContractCreationTx(value.BigInt(), gas.BigInt(), price.BigInt(), data)
+ tx = types.NewContractCreationTx(value.BigInt(), gas, price, data)
} else {
- tx = types.NewTransactionMessage(to, value.BigInt(), gas.BigInt(), price.BigInt(), data)
+ tx = types.NewTransactionMessage(to, value.BigInt(), gas, price, data)
}
state := self.chainManager.TxState()
@@ -407,3 +666,36 @@ func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
func (m callmsg) Gas() *big.Int { return m.gas }
func (m callmsg) Value() *big.Int { return m.value }
func (m callmsg) Data() []byte { return m.data }
+
+type whisperFilter struct {
+ messages []WhisperMessage
+ timeout time.Time
+ id int
+}
+
+func (w *whisperFilter) add(msgs ...WhisperMessage) {
+ w.messages = append(w.messages, msgs...)
+}
+func (w *whisperFilter) get() []WhisperMessage {
+ w.timeout = time.Now()
+ tmp := w.messages
+ w.messages = nil
+ return tmp
+}
+
+type logFilter struct {
+ logs state.Logs
+ timeout time.Time
+ id int
+}
+
+func (l *logFilter) add(logs ...state.Log) {
+ l.logs = append(l.logs, logs...)
+}
+
+func (l *logFilter) get() state.Logs {
+ l.timeout = time.Now()
+ tmp := l.logs
+ l.logs = nil
+ return tmp
+}