diff --git a/eth/handler.go b/eth/handler.go index d2355a876..e18fa6124 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -687,6 +687,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err := msg.Decode(&request); err != nil { return errResp(ErrDecode, "%v: %v", msg, err) } + if hash := types.CalcUncleHash(request.Block.Uncles()); hash != request.Block.UncleHash() { + log.Warn("Propagated block has invalid uncles", "have", hash, "exp", request.Block.UncleHash()) + break // TODO(karalabe): return error eventually, but wait a few releases + } + if hash := types.DeriveSha(request.Block.Transactions()); hash != request.Block.TxHash() { + log.Warn("Propagated block has invalid body", "have", hash, "exp", request.Block.TxHash()) + break // TODO(karalabe): return error eventually, but wait a few releases + } if err := request.sanityCheck(); err != nil { return err } diff --git a/eth/handler_test.go b/eth/handler_test.go index 05ddca3ef..893bfffa4 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -634,3 +634,65 @@ outer: t.Errorf("block broadcast to %d peers, expected %d", receivedCount, broadcastExpected) } } + +// Tests that a propagated malformed block (uncles or transactions don't match +// with the hashes in the header) gets discarded and not broadcast forward. +func TestBroadcastMalformedBlock(t *testing.T) { + // Create a live node to test propagation with + var ( + engine = ethash.NewFaker() + db = rawdb.NewMemoryDatabase() + config = ¶ms.ChainConfig{} + gspec = &core.Genesis{Config: config} + genesis = gspec.MustCommit(db) + ) + blockchain, err := core.NewBlockChain(db, nil, config, engine, vm.Config{}, nil) + if err != nil { + t.Fatalf("failed to create new blockchain: %v", err) + } + pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), engine, blockchain, db, 1, nil) + if err != nil { + t.Fatalf("failed to start test protocol manager: %v", err) + } + pm.Start(2) + defer pm.Stop() + + // Create two peers, one to send the malformed block with and one to check + // propagation + source, _ := newTestPeer("source", eth63, pm, true) + defer source.close() + + sink, _ := newTestPeer("sink", eth63, pm, true) + defer sink.close() + + // Create various combinations of malformed blocks + chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {}) + + malformedUncles := chain[0].Header() + malformedUncles.UncleHash[0]++ + malformedTransactions := chain[0].Header() + malformedTransactions.TxHash[0]++ + malformedEverything := chain[0].Header() + malformedEverything.UncleHash[0]++ + malformedEverything.TxHash[0]++ + + // Keep listening to broadcasts and notify if any arrives + notify := make(chan struct{}) + go func() { + if _, err := sink.app.ReadMsg(); err == nil { + notify <- struct{}{} + } + }() + // Try to broadcast all malformations and ensure they all get discarded + for _, header := range []*types.Header{malformedUncles, malformedTransactions, malformedEverything} { + block := types.NewBlockWithHeader(header).WithBody(chain[0].Transactions(), chain[0].Uncles()) + if err := p2p.Send(source.app, NewBlockMsg, []interface{}{block, big.NewInt(131136)}); err != nil { + t.Fatalf("failed to broadcast block: %v", err) + } + select { + case <-notify: + t.Fatalf("malformed block forwarded") + case <-time.After(100 * time.Millisecond): + } + } +}