diff options
-rw-r--r-- | CONTRIBUTING.md | 9 | ||||
-rw-r--r-- | core/transaction_pool.go | 30 | ||||
-rw-r--r-- | eth/api.go | 74 | ||||
-rw-r--r-- | miner/api.go | 5 | ||||
-rw-r--r-- | p2p/discover/udp.go | 9 | ||||
-rw-r--r-- | p2p/discover/udp_notwindows.go | 26 | ||||
-rw-r--r-- | p2p/discover/udp_test.go | 56 | ||||
-rw-r--r-- | p2p/discover/udp_windows.go | 40 | ||||
-rw-r--r-- | rpc/javascript.go | 10 |
9 files changed, 250 insertions, 9 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 918a2c154..829bf5d43 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,12 @@ +## Can I have feature X + +Before you do a feature request please check and make sure that it isn't possible +through some other means. The JavaScript enabled console is a powerful feature +in the right hands. Please check our [Bitchin' tricks](https://github.com/ethereum/go-ethereum/wiki/bitchin-tricks) wiki page for more info +and help. + +## Contributing + If you'd like to contribute to go-ethereum please fork, fix, commit and send a pull request. Commits who do not comply with the coding standards are ignored (use gofmt!). If you send pull requests make absolute sure that you diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 54c0ddebb..456de3cb6 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -169,6 +169,36 @@ func (pool *TxPool) Stats() (pending int, queued int) { return } +// Content retrieves the data content of the transaction pool, returning all the +// pending as well as queued transactions, grouped by account and nonce. +func (pool *TxPool) Content() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { + pool.mu.RLock() + defer pool.mu.RUnlock() + + // Retrieve all the pending transactions and sort by account and by nonce + pending := make(map[common.Address]map[uint64][]*types.Transaction) + for _, tx := range pool.pending { + account, _ := tx.From() + + owned, ok := pending[account] + if !ok { + owned = make(map[uint64][]*types.Transaction) + pending[account] = owned + } + owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) + } + // Retrieve all the queued transactions and sort by account and by nonce + queued := make(map[common.Address]map[uint64][]*types.Transaction) + for account, txs := range pool.queue { + owned := make(map[uint64][]*types.Transaction) + for _, tx := range txs { + owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) + } + queued[account] = owned + } + return pending, queued +} + // SetLocal marks a transaction as local, skipping gas price // check against local miner minimum in the future func (pool *TxPool) SetLocal(tx *types.Transaction) { diff --git a/eth/api.go b/eth/api.go index 08b103a4c..0f55c60a9 100644 --- a/eth/api.go +++ b/eth/api.go @@ -227,6 +227,39 @@ func NewPublicTxPoolAPI(e *Ethereum) *PublicTxPoolAPI { return &PublicTxPoolAPI{e} } +// Content returns the transactions contained within the transaction pool. +func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string][]*RPCTransaction { + content := map[string]map[string]map[string][]*RPCTransaction{ + "pending": make(map[string]map[string][]*RPCTransaction), + "queued": make(map[string]map[string][]*RPCTransaction), + } + pending, queue := s.e.TxPool().Content() + + // Flatten the pending transactions + for account, batches := range pending { + dump := make(map[string][]*RPCTransaction) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) + } + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, batches := range queue { + dump := make(map[string][]*RPCTransaction) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) + } + } + content["queued"][account.Hex()] = dump + } + return content +} + // Status returns the number of pending and queued transaction in the pool. func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber { pending, queue := s.e.TxPool().Stats() @@ -236,6 +269,47 @@ func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber { } } +// Inspect retrieves the content of the transaction pool and flattens it into an +// easily inspectable list. +func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string { + content := map[string]map[string]map[string][]string{ + "pending": make(map[string]map[string][]string), + "queued": make(map[string]map[string][]string), + } + pending, queue := s.e.TxPool().Content() + + // Define a formatter to flatten a transaction into a string + var format = func(tx *types.Transaction) string { + if to := tx.To(); to != nil { + return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) + } + return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) + } + // Flatten the pending transactions + for account, batches := range pending { + dump := make(map[string][]string) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], format(tx)) + } + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, batches := range queue { + dump := make(map[string][]string) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], format(tx)) + } + } + content["queued"][account.Hex()] = dump + } + return content +} + // PublicAccountAPI provides an API to access accounts managed by this node. // It offers only methods that can retrieve accounts. type PublicAccountAPI struct { diff --git a/miner/api.go b/miner/api.go index 42678b5ad..fae2203f5 100644 --- a/miner/api.go +++ b/miner/api.go @@ -33,7 +33,10 @@ type PublicMinerAPI struct { // NewPublicMinerAPI create a new PublicMinerAPI instance. func NewPublicMinerAPI(miner *Miner) *PublicMinerAPI { - return &PublicMinerAPI{miner, NewRemoteAgent()} + agent := NewRemoteAgent() + miner.Register(agent) + + return &PublicMinerAPI{miner, agent} } // Mining returns an indication if this node is currently mining. diff --git a/p2p/discover/udp.go b/p2p/discover/udp.go index 72b2a45e5..cec9046a3 100644 --- a/p2p/discover/udp.go +++ b/p2p/discover/udp.go @@ -453,8 +453,11 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte, return packet, nil } -type tempError interface { - Temporary() bool +func isTemporaryError(err error) bool { + tempErr, ok := err.(interface { + Temporary() bool + }) + return ok && tempErr.Temporary() || isPacketTooBig(err) } // readLoop runs in its own goroutine. it handles incoming UDP packets. @@ -466,7 +469,7 @@ func (t *udp) readLoop() { buf := make([]byte, 1280) for { nbytes, from, err := t.conn.ReadFromUDP(buf) - if tempErr, ok := err.(tempError); ok && tempErr.Temporary() { + if isTemporaryError(err) { // Ignore temporary read errors. glog.V(logger.Debug).Infof("Temporary read error: %v", err) continue diff --git a/p2p/discover/udp_notwindows.go b/p2p/discover/udp_notwindows.go new file mode 100644 index 000000000..e9de83aa9 --- /dev/null +++ b/p2p/discover/udp_notwindows.go @@ -0,0 +1,26 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +//+build !windows + +package discover + +// reports whether err indicates that a UDP packet didn't +// fit the receive buffer. There is no such error on +// non-Windows platforms. +func isPacketTooBig(err error) bool { + return false +} diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go index 8ed12b8ec..ec28867cc 100644 --- a/p2p/discover/udp_test.go +++ b/p2p/discover/udp_test.go @@ -23,10 +23,8 @@ import ( "errors" "fmt" "io" - logpkg "log" "math/rand" "net" - "os" "path/filepath" "reflect" "runtime" @@ -34,12 +32,62 @@ import ( "testing" "time" + "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" ) func init() { - logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel)) + spew.Config.DisableMethods = true +} + +// This test checks that isPacketTooBig correctly identifies +// errors that result from receiving a UDP packet larger +// than the supplied receive buffer. +func TestIsPacketTooBig(t *testing.T) { + listener, err := net.ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer listener.Close() + sender, err := net.Dial("udp", listener.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + defer sender.Close() + + sendN := 1800 + recvN := 300 + for i := 0; i < 20; i++ { + go func() { + buf := make([]byte, sendN) + for i := range buf { + buf[i] = byte(i) + } + sender.Write(buf) + }() + + buf := make([]byte, recvN) + listener.SetDeadline(time.Now().Add(1 * time.Second)) + n, _, err := listener.ReadFrom(buf) + if err != nil { + if nerr, ok := err.(net.Error); ok && nerr.Timeout() { + continue + } + if !isPacketTooBig(err) { + t.Fatal("unexpected read error:", spew.Sdump(err)) + } + continue + } + if n != recvN { + t.Fatalf("short read: %d, want %d", n, recvN) + } + for i := range buf { + if buf[i] != byte(i) { + t.Fatalf("error in pattern") + break + } + } + } } // shared test variables diff --git a/p2p/discover/udp_windows.go b/p2p/discover/udp_windows.go new file mode 100644 index 000000000..66bbf9597 --- /dev/null +++ b/p2p/discover/udp_windows.go @@ -0,0 +1,40 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +//+build windows + +package discover + +import ( + "net" + "os" + "syscall" +) + +const _WSAEMSGSIZE = syscall.Errno(10040) + +// reports whether err indicates that a UDP packet didn't +// fit the receive buffer. On Windows, WSARecvFrom returns +// code WSAEMSGSIZE and no data if this happens. +func isPacketTooBig(err error) bool { + if opErr, ok := err.(*net.OpError); ok { + if scErr, ok := opErr.Err.(*os.SyscallError); ok { + return scErr.Err == _WSAEMSGSIZE + } + return opErr.Err == _WSAEMSGSIZE + } + return false +} diff --git a/rpc/javascript.go b/rpc/javascript.go index 9e3b2218b..c145163e5 100644 --- a/rpc/javascript.go +++ b/rpc/javascript.go @@ -71,8 +71,16 @@ web3._extend({ properties: [ new web3._extend.Property({ + name: 'content', + getter: 'txpool_content' + }), + new web3._extend.Property({ + name: 'inspect', + getter: 'txpool_inspect' + }), + new web3._extend.Property({ name: 'status', - getter: 'txpool_status' + getter: 'txpool_status', outputFormatter: function(status) { status.pending = web3._extend.utils.toDecimal(status.pending); status.queued = web3._extend.utils.toDecimal(status.queued); |