aboutsummaryrefslogtreecommitdiffstats
path: root/xeth/xeth.go
diff options
context:
space:
mode:
Diffstat (limited to 'xeth/xeth.go')
-rw-r--r--xeth/xeth.go377
1 files changed, 333 insertions, 44 deletions
diff --git a/xeth/xeth.go b/xeth/xeth.go
index 115bd787a..d3c3131d4 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,20 @@ 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/miner"
"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 {
@@ -35,6 +44,7 @@ type Backend interface {
ExtraDb() common.Database
EventMux() *event.TypeMux
Whisper() *whisper.Whisper
+ Miner() *miner.Miner
IsMining() bool
StartMining() error
@@ -62,6 +72,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 +88,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 +112,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{
@@ -116,14 +199,14 @@ func (self *XEth) State() *State { return self.state }
func (self *XEth) Whisper() *Whisper { return self.whisper }
func (self *XEth) BlockByHash(strHash string) *Block {
- hash := common.FromHex(strHash)
+ hash := common.HexToHash(strHash)
block := self.chainManager.GetBlock(hash)
return NewBlock(block)
}
func (self *XEth) EthBlockByHash(strHash string) *types.Block {
- hash := common.FromHex(strHash)
+ hash := common.HexToHash(strHash)
block := self.chainManager.GetBlock(hash)
return block
@@ -241,6 +324,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"`
@@ -293,21 +527,16 @@ func (self *XEth) PushTx(encodedTx string) (string, error) {
if tx.To() == nil {
addr := core.AddressFromMessage(tx)
- return common.ToHex(addr), nil
+ return addr.Hex(), nil
}
- return common.ToHex(tx.Hash()), nil
+ 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{
- from: statedb.GetOrNewStateObject(common.FromHex(fromStr)),
- to: common.FromHex(toStr),
+ from: statedb.GetOrNewStateObject(common.HexToAddress(fromStr)),
+ to: common.HexToAddress(toStr),
gas: common.Big(gasStr),
gasPrice: common.Big(gasPriceStr),
value: common.Big(valueStr),
@@ -330,31 +559,61 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
var (
- from []byte
- to []byte
+ 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
)
- from = common.FromHex(fromStr)
+ // 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)
- to = common.FromHex(toStr)
- if len(to) == 0 {
+ if len(toStr) == 0 {
contractCreation = true
}
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()
- nonce := state.NewNonce(from) //state.GetNonce(from)
+ nonce := state.NewNonce(from)
tx.SetNonce(nonce)
if err := self.sign(tx, from, false); err != nil {
@@ -363,26 +622,23 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt
if err := self.eth.TxPool().Add(tx); err != nil {
return "", err
}
- //state.IncrementNonce(from)
if contractCreation {
addr := core.AddressFromMessage(tx)
pipelogger.Infof("Contract addr %x\n", addr)
- }
- if types.IsContractAddr(to) {
- return common.ToHex(core.AddressFromMessage(tx)), nil
+ return core.AddressFromMessage(tx).Hex(), nil
}
- return common.ToHex(tx.Hash()), nil
+ return tx.Hash().Hex(), nil
}
-func (self *XEth) sign(tx *types.Transaction, from []byte, didUnlock bool) error {
- sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash())
+func (self *XEth) sign(tx *types.Transaction, from common.Address, didUnlock bool) error {
+ sig, err := self.accountManager.Sign(accounts.Account{Address: from.Bytes()}, tx.Hash().Bytes())
if err == accounts.ErrLocked {
if didUnlock {
return fmt.Errorf("sender account still locked after successful unlock")
}
- if !self.frontend.UnlockAccount(from) {
+ if !self.frontend.UnlockAccount(from.Bytes()) {
return fmt.Errorf("could not unlock sender account")
}
// retry signing, the account should now be unlocked.
@@ -397,17 +653,50 @@ func (self *XEth) sign(tx *types.Transaction, from []byte, didUnlock bool) error
// callmsg is the message type used for call transations.
type callmsg struct {
from *state.StateObject
- to []byte
+ to common.Address
gas, gasPrice *big.Int
value *big.Int
data []byte
}
// accessor boilerplate to implement core.Message
-func (m callmsg) From() []byte { return m.from.Address() }
-func (m callmsg) Nonce() uint64 { return m.from.Nonce() }
-func (m callmsg) To() []byte { return m.to }
-func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
-func (m callmsg) Gas() *big.Int { return m.gas }
-func (m callmsg) Value() *big.Int { return m.value }
-func (m callmsg) Data() []byte { return m.data }
+func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil }
+func (m callmsg) Nonce() uint64 { return m.from.Nonce() }
+func (m callmsg) To() *common.Address { return &m.to }
+func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
+func (m callmsg) Gas() *big.Int { return m.gas }
+func (m callmsg) Value() *big.Int { return m.value }
+func (m callmsg) Data() []byte { return m.data }
+
+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
+}