Przeglądaj źródła

Merge pull request #1356 from Gustav-Simonsson/debug_develop

Debug develop
Jeffrey Wilcke 10 lat temu
rodzic
commit
9d8b512b27
7 zmienionych plików z 188 dodań i 54 usunięć
  1. 3 6
      .travis.yml
  2. 3 4
      README.md
  3. 55 33
      cmd/geth/monitorcmd.go
  4. 17 5
      eth/fetcher/fetcher.go
  5. 9 2
      p2p/peer.go
  6. 95 0
      p2p/peer_test.go
  7. 6 4
      p2p/protocol.go

+ 3 - 6
.travis.yml

@@ -2,7 +2,6 @@ language: go
 go:
   - 1.4.2
 before_install:
-  - sudo add-apt-repository ppa:beineri/opt-qt541 -y
   - sudo apt-get update -qq
   - sudo apt-get install -yqq libgmp3-dev
 install:
@@ -22,14 +21,12 @@ after_success:
   - if [ "$COVERALLS_TOKEN" ]; then goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN; fi
 env:
   global:
-    - PKG_CONFIG_PATH=/opt/qt54/lib/pkgconfig
-    - LD_LIBRARY_PATH=/opt/qt54/lib
     - secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
 
 notifications:
   webhooks:
     urls:
       - https://webhooks.gitter.im/e/e09ccdce1048c5e03445
-    on_success: change  # options: [always|never|change] default: always
-    on_failure: always  # options: [always|never|change] default: always
-    on_start: false     # default: false
+    on_success: change 
+    on_failure: always
+    on_start: false 

+ 3 - 4
README.md

@@ -44,11 +44,10 @@ Executables
 Go Ethereum comes with several wrappers/executables found in 
 [the `cmd` directory](https://github.com/ethereum/go-ethereum/tree/develop/cmd):
 
-* `mist` Official Ethereum Browser (ethereum GUI client)
 * `geth` Ethereum CLI (ethereum command line interface client)
 * `bootnode` runs a bootstrap node for the Discovery Protocol
-* `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suite: 
-  `cat file | ethtest`.
+* `ethtest` test tool which runs with the [tests](https://github.com/ethereum/tests) suite: 
+  `/path/to/test.json > ethtest --test BlockTests --stdin`.
 * `evm` is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas
   10000 -price 0 -dump`. See `-h` for a detailed description.
 * `disasm` disassembles EVM code: `echo "6001" | disasm`
@@ -57,7 +56,7 @@ Go Ethereum comes with several wrappers/executables found in
 Command line options
 ====================
 
-Both `mist` and `geth` can be configured via command line options, environment variables and config files.
+`geth` can be configured via command line options, environment variables and config files.
 
 To get the options available:
 

+ 55 - 33
cmd/geth/monitorcmd.go

@@ -103,33 +103,17 @@ func monitor(ctx *cli.Context) {
 	footer.Height = 3
 
 	charts := make([]*termui.LineChart, len(monitored))
+	units := make([]int, len(monitored))
 	data := make([][]float64, len(monitored))
-	for i := 0; i < len(data); i++ {
-		data[i] = make([]float64, 512)
-	}
-	for i, metric := range monitored {
-		charts[i] = termui.NewLineChart()
-		if runtime.GOOS == "windows" {
-			charts[i].Mode = "dot"
-		}
-		charts[i].Data = make([]float64, 512)
-		charts[i].DataLabels = []string{""}
-		charts[i].Height = (termui.TermHeight() - footer.Height) / rows
-		charts[i].AxesColor = termui.ColorWhite
-		charts[i].PaddingBottom = -2
-
-		charts[i].Border.Label = metric
-		charts[i].Border.LabelFgColor = charts[i].Border.FgColor | termui.AttrBold
-		charts[i].Border.FgColor = charts[i].Border.BgColor
-
+	for i := 0; i < len(monitored); i++ {
+		charts[i] = createChart((termui.TermHeight() - footer.Height) / rows)
 		row := termui.Body.Rows[i%rows]
 		row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i]))
 	}
 	termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer)))
-	termui.Body.Align()
-	termui.Render(termui.Body)
 
-	refreshCharts(xeth, monitored, data, charts, ctx, footer)
+	refreshCharts(xeth, monitored, data, units, charts, ctx, footer)
+	termui.Body.Align()
 	termui.Render(termui.Body)
 
 	// Watch for various system events, and periodically refresh the charts
@@ -149,7 +133,9 @@ func monitor(ctx *cli.Context) {
 				termui.Render(termui.Body)
 			}
 		case <-refresh:
-			refreshCharts(xeth, monitored, data, charts, ctx, footer)
+			if refreshCharts(xeth, monitored, data, units, charts, ctx, footer) {
+				termui.Body.Align()
+			}
 			termui.Render(termui.Body)
 		}
 	}
@@ -246,45 +232,63 @@ func fetchMetric(metrics map[string]interface{}, metric string) float64 {
 
 // refreshCharts retrieves a next batch of metrics, and inserts all the new
 // values into the active datasets and charts
-func refreshCharts(xeth *rpc.Xeth, metrics []string, data [][]float64, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) {
+func refreshCharts(xeth *rpc.Xeth, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) {
 	values, err := retrieveMetrics(xeth)
 	for i, metric := range metrics {
-		data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...)
-		updateChart(metric, data[i], charts[i], err)
+		if len(data) < 512 {
+			data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...)
+		} else {
+			data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...)
+		}
+		if updateChart(metric, data[i], &units[i], charts[i], err) {
+			realign = true
+		}
 	}
 	updateFooter(ctx, err, footer)
+	return
 }
 
 // updateChart inserts a dataset into a line chart, scaling appropriately as to
 // not display weird labels, also updating the chart label accordingly.
-func updateChart(metric string, data []float64, chart *termui.LineChart, err error) {
+func updateChart(metric string, data []float64, base *int, chart *termui.LineChart, err error) (realign bool) {
 	dataUnits := []string{"", "K", "M", "G", "T", "E"}
 	timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"}
 	colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed}
 
 	// Extract only part of the data that's actually visible
-	data = data[:chart.Width*2]
-
+	if chart.Width*2 < len(data) {
+		data = data[:chart.Width*2]
+	}
 	// Find the maximum value and scale under 1K
-	high := data[0]
-	for _, value := range data[1:] {
-		high = math.Max(high, value)
+	high := 0.0
+	if len(data) > 0 {
+		high = data[0]
+		for _, value := range data[1:] {
+			high = math.Max(high, value)
+		}
 	}
 	unit, scale := 0, 1.0
 	for high >= 1000 {
 		high, unit, scale = high/1000, unit+1, scale*1000
 	}
+	// If the unit changes, re-create the chart (hack to set max height...)
+	if unit != *base {
+		realign, *base, *chart = true, unit, *createChart(chart.Height)
+	}
 	// Update the chart's data points with the scaled values
+	if cap(chart.Data) < len(data) {
+		chart.Data = make([]float64, len(data))
+	}
+	chart.Data = chart.Data[:len(data)]
 	for i, value := range data {
 		chart.Data[i] = value / scale
 	}
 	// Update the chart's label with the scale units
-	chart.Border.Label = metric
-
 	units := dataUnits
 	if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") {
 		units = timeUnits
 	}
+	chart.Border.Label = metric
 	if len(units[unit]) > 0 {
 		chart.Border.Label += " [" + units[unit] + "]"
 	}
@@ -292,6 +296,24 @@ func updateChart(metric string, data []float64, chart *termui.LineChart, err err
 	if err != nil {
 		chart.LineColor = termui.ColorRed | termui.AttrBold
 	}
+	return
+}
+
+// createChart creates an empty line chart with the default configs.
+func createChart(height int) *termui.LineChart {
+	chart := termui.NewLineChart()
+	if runtime.GOOS == "windows" {
+		chart.Mode = "dot"
+	}
+	chart.DataLabels = []string{""}
+	chart.Height = height
+	chart.AxesColor = termui.ColorWhite
+	chart.PaddingBottom = -2
+
+	chart.Border.LabelFgColor = chart.Border.FgColor | termui.AttrBold
+	chart.Border.FgColor = chart.Border.BgColor
+
+	return chart
 }
 
 // updateFooter updates the footer contents based on any encountered errors.

+ 17 - 5
eth/fetcher/fetcher.go

@@ -7,6 +7,8 @@ import (
 	"math/rand"
 	"time"
 
+	"github.com/ethereum/go-ethereum/core"
+
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/logger"
@@ -104,6 +106,7 @@ type Fetcher struct {
 	broadcastMeter metrics.Meter // Counter for metering the inbound propagations
 	broadcastTimer metrics.Timer // Counter and timer for metering the block forwarding
 	discardMeter   metrics.Meter // Counter for metering the discarded blocks
+	futureMeter    metrics.Meter // Counter for metering future blocks
 }
 
 // New creates a block fetcher to retrieve blocks based on hash announcements.
@@ -131,6 +134,7 @@ func New(getBlock blockRetrievalFn, validateBlock blockValidatorFn, broadcastBlo
 		broadcastMeter: metrics.GetOrRegisterMeter("eth/sync/RemoteBroadcasts", metrics.DefaultRegistry),
 		broadcastTimer: metrics.GetOrRegisterTimer("eth/sync/LocalBroadcasts", metrics.DefaultRegistry),
 		discardMeter:   metrics.GetOrRegisterMeter("eth/sync/DiscardedBlocks", metrics.DefaultRegistry),
+		futureMeter:    metrics.GetOrRegisterMeter("eth/sync/FutureBlocks", metrics.DefaultRegistry),
 	}
 }
 
@@ -323,7 +327,7 @@ func (f *Fetcher) loop() {
 				hash := block.Hash()
 
 				// Filter explicitly requested blocks from hash announcements
-				if _, ok := f.fetching[hash]; ok {
+				if f.fetching[hash] != nil && f.queued[hash] == nil {
 					// Discard if already imported by other means
 					if f.getBlock(hash) == nil {
 						explicit = append(explicit, block)
@@ -416,14 +420,22 @@ func (f *Fetcher) insert(peer string, block *types.Block) {
 			return
 		}
 		// Quickly validate the header and propagate the block if it passes
-		if err := f.validateBlock(block, parent); err != nil {
+		switch err := f.validateBlock(block, parent); err {
+		case nil:
+			// All ok, quickly propagate to our peers
+			f.broadcastTimer.UpdateSince(block.ReceivedAt)
+			go f.broadcastBlock(block, true)
+
+		case core.BlockFutureErr:
+			f.futureMeter.Mark(1)
+			// Weird future block, don't fail, but neither propagate
+
+		default:
+			// Something went very wrong, drop the peer
 			glog.V(logger.Debug).Infof("Peer %s: block #%d [%x] verification failed: %v", peer, block.NumberU64(), hash[:4], err)
 			f.dropPeer(peer)
 			return
 		}
-		f.broadcastTimer.UpdateSince(block.ReceivedAt)
-		go f.broadcastBlock(block, true)
-
 		// Run the actual import and log any issues
 		if _, err := f.insertChain(types.Blocks{block}); err != nil {
 			glog.V(logger.Warn).Infof("Peer %s: block #%d [%x] import failed: %v", peer, block.NumberU64(), hash[:4], err)

+ 9 - 2
p2p/peer.go

@@ -249,15 +249,22 @@ func countMatchingProtocols(protocols []Protocol, caps []Cap) int {
 
 // matchProtocols creates structures for matching named subprotocols.
 func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW {
-	sort.Sort(capsByName(caps))
+	sort.Sort(capsByNameAndVersion(caps))
 	offset := baseProtocolLength
 	result := make(map[string]*protoRW)
+
 outer:
 	for _, cap := range caps {
 		for _, proto := range protocols {
-			if proto.Name == cap.Name && proto.Version == cap.Version && result[cap.Name] == nil {
+			if proto.Name == cap.Name && proto.Version == cap.Version {
+				// If an old protocol version matched, revert it
+				if old := result[cap.Name]; old != nil {
+					offset -= old.Length
+				}
+				// Assign the new match
 				result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw}
 				offset += proto.Length
+
 				continue outer
 			}
 		}

+ 95 - 0
p2p/peer_test.go

@@ -196,3 +196,98 @@ func TestNewPeer(t *testing.T) {
 
 	p.Disconnect(DiscAlreadyConnected) // Should not hang
 }
+
+func TestMatchProtocols(t *testing.T) {
+	tests := []struct {
+		Remote []Cap
+		Local  []Protocol
+		Match  map[string]protoRW
+	}{
+		{
+			// No remote capabilities
+			Local: []Protocol{{Name: "a"}},
+		},
+		{
+			// No local protocols
+			Remote: []Cap{{Name: "a"}},
+		},
+		{
+			// No mutual protocols
+			Remote: []Cap{{Name: "a"}},
+			Local:  []Protocol{{Name: "b"}},
+		},
+		{
+			// Some matches, some differences
+			Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}},
+			Local:  []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}},
+			Match:  map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}},
+		},
+		{
+			// Various alphabetical ordering
+			Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}},
+			Local:  []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}},
+			Match:  map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}},
+		},
+		{
+			// No mutual versions
+			Remote: []Cap{{Version: 1}},
+			Local:  []Protocol{{Version: 2}},
+		},
+		{
+			// Multiple versions, single common
+			Remote: []Cap{{Version: 1}, {Version: 2}},
+			Local:  []Protocol{{Version: 2}, {Version: 3}},
+			Match:  map[string]protoRW{"": {Protocol: Protocol{Version: 2}}},
+		},
+		{
+			// Multiple versions, multiple common
+			Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}},
+			Local:  []Protocol{{Version: 2}, {Version: 3}},
+			Match:  map[string]protoRW{"": {Protocol: Protocol{Version: 3}}},
+		},
+		{
+			// Various version orderings
+			Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}},
+			Local:  []Protocol{{Version: 2}, {Version: 3}, {Version: 1}},
+			Match:  map[string]protoRW{"": {Protocol: Protocol{Version: 3}}},
+		},
+		{
+			// Versions overriding sub-protocol lengths
+			Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}},
+			Local:  []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}},
+			Match:  map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}},
+		},
+	}
+
+	for i, tt := range tests {
+		result := matchProtocols(tt.Local, tt.Remote, nil)
+		if len(result) != len(tt.Match) {
+			t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match))
+			continue
+		}
+		// Make sure all negotiated protocols are needed and correct
+		for name, proto := range result {
+			match, ok := tt.Match[name]
+			if !ok {
+				t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name)
+				continue
+			}
+			if proto.Name != match.Name {
+				t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name)
+			}
+			if proto.Version != match.Version {
+				t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version)
+			}
+			if proto.offset-baseProtocolLength != match.offset {
+				t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset)
+			}
+		}
+		// Make sure no protocols missed negotiation
+		for name, _ := range tt.Match {
+			if _, ok := result[name]; !ok {
+				t.Errorf("test %d, proto '%s': not negotiated, should have", i, name)
+				continue
+			}
+		}
+	}
+}

+ 6 - 4
p2p/protocol.go

@@ -43,8 +43,10 @@ func (cap Cap) String() string {
 	return fmt.Sprintf("%s/%d", cap.Name, cap.Version)
 }
 
-type capsByName []Cap
+type capsByNameAndVersion []Cap
 
-func (cs capsByName) Len() int           { return len(cs) }
-func (cs capsByName) Less(i, j int) bool { return cs[i].Name < cs[j].Name }
-func (cs capsByName) Swap(i, j int)      { cs[i], cs[j] = cs[j], cs[i] }
+func (cs capsByNameAndVersion) Len() int      { return len(cs) }
+func (cs capsByNameAndVersion) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
+func (cs capsByNameAndVersion) Less(i, j int) bool {
+	return cs[i].Name < cs[j].Name || (cs[i].Name == cs[j].Name && cs[i].Version < cs[j].Version)
+}