downloader_test.go 9.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
package downloader

import (
	"encoding/binary"
	"math/big"
	"testing"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
O
obscuren 已提交
11
	"github.com/ethereum/go-ethereum/event"
12 13
)

14 15 16 17
var (
	knownHash   = common.Hash{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	unknownHash = common.Hash{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}
)
18

19
func createHashes(start, amount int) (hashes []common.Hash) {
20 21 22 23 24 25 26 27 28 29
	hashes = make([]common.Hash, amount+1)
	hashes[len(hashes)-1] = knownHash

	for i := range hashes[:len(hashes)-1] {
		binary.BigEndian.PutUint64(hashes[i][:8], uint64(i+2))
	}

	return
}

30 31 32 33
func createBlock(i int, prevHash, hash common.Hash) *types.Block {
	header := &types.Header{Number: big.NewInt(int64(i))}
	block := types.NewBlockWithHeader(header)
	block.HeaderHash = hash
34
	block.ParentHeaderHash = prevHash
35 36 37
	return block
}

38 39
func createBlocksFromHashes(hashes []common.Hash) map[common.Hash]*types.Block {
	blocks := make(map[common.Hash]*types.Block)
40

41
	for i, hash := range hashes {
42
		blocks[hash] = createBlock(len(hashes)-i, knownHash, hash)
43 44 45 46 47 48
	}

	return blocks
}

type downloadTester struct {
49 50 51 52 53 54
	downloader *Downloader

	hashes []common.Hash                // Chain of hashes simulating
	blocks map[common.Hash]*types.Block // Blocks associated with the hashes
	chain  []common.Hash                // Block-chain being constructed

O
obscuren 已提交
55 56 57 58
	t            *testing.T
	pcount       int
	done         chan bool
	activePeerId string
59 60 61
}

func newTester(t *testing.T, hashes []common.Hash, blocks map[common.Hash]*types.Block) *downloadTester {
62 63 64 65 66 67 68 69 70
	tester := &downloadTester{
		t: t,

		hashes: hashes,
		blocks: blocks,
		chain:  []common.Hash{knownHash},

		done: make(chan bool),
	}
O
obscuren 已提交
71 72
	var mux event.TypeMux
	downloader := New(&mux, tester.hasBlock, tester.getBlock)
73 74 75 76 77
	tester.downloader = downloader

	return tester
}

O
obscuren 已提交
78 79
func (dl *downloadTester) sync(peerId string, hash common.Hash) error {
	dl.activePeerId = peerId
80
	return dl.downloader.Synchronise(peerId, hash)
O
obscuren 已提交
81 82
}

83 84 85 86 87 88
func (dl *downloadTester) insertBlocks(blocks types.Blocks) {
	for _, block := range blocks {
		dl.chain = append(dl.chain, block.Hash())
	}
}

89
func (dl *downloadTester) hasBlock(hash common.Hash) bool {
90 91 92 93
	for _, h := range dl.chain {
		if h == hash {
			return true
		}
94 95 96 97
	}
	return false
}

98 99
func (dl *downloadTester) getBlock(hash common.Hash) *types.Block {
	return dl.blocks[knownHash]
100 101 102
}

func (dl *downloadTester) getHashes(hash common.Hash) error {
103
	dl.downloader.DeliverHashes(dl.activePeerId, dl.hashes)
104 105 106 107 108 109 110 111 112 113
	return nil
}

func (dl *downloadTester) getBlocks(id string) func([]common.Hash) error {
	return func(hashes []common.Hash) error {
		blocks := make([]*types.Block, len(hashes))
		for i, hash := range hashes {
			blocks[i] = dl.blocks[hash]
		}

114
		go dl.downloader.DeliverBlocks(id, blocks)
115 116 117 118 119 120 121 122

		return nil
	}
}

func (dl *downloadTester) newPeer(id string, td *big.Int, hash common.Hash) {
	dl.pcount++

O
obscuren 已提交
123
	dl.downloader.RegisterPeer(id, hash, dl.getHashes, dl.getBlocks(id))
124 125 126 127 128 129
}

func (dl *downloadTester) badBlocksPeer(id string, td *big.Int, hash common.Hash) {
	dl.pcount++

	// This bad peer never returns any blocks
O
obscuren 已提交
130
	dl.downloader.RegisterPeer(id, hash, dl.getHashes, func([]common.Hash) error {
131 132 133 134 135
		return nil
	})
}

func TestDownload(t *testing.T) {
136
	minDesiredPeerCount = 4
O
obscuren 已提交
137
	blockTtl = 1 * time.Second
138

139 140
	targetBlocks := 1000
	hashes := createHashes(0, targetBlocks)
141 142 143
	blocks := createBlocksFromHashes(hashes)
	tester := newTester(t, hashes, blocks)

144
	tester.newPeer("peer1", big.NewInt(10000), hashes[0])
145 146 147
	tester.newPeer("peer2", big.NewInt(0), common.Hash{})
	tester.badBlocksPeer("peer3", big.NewInt(0), common.Hash{})
	tester.badBlocksPeer("peer4", big.NewInt(0), common.Hash{})
O
obscuren 已提交
148
	tester.activePeerId = "peer1"
149

O
obscuren 已提交
150
	err := tester.sync("peer1", hashes[0])
151 152 153 154
	if err != nil {
		t.Error("download error", err)
	}

155
	inqueue := len(tester.downloader.queue.blockCache)
156 157
	if inqueue != targetBlocks {
		t.Error("expected", targetBlocks, "have", inqueue)
158 159
	}
}
160 161

func TestMissing(t *testing.T) {
162
	targetBlocks := 1000
163 164 165 166 167 168 169 170 171 172
	hashes := createHashes(0, 1000)
	extraHashes := createHashes(1001, 1003)
	blocks := createBlocksFromHashes(append(extraHashes, hashes...))
	tester := newTester(t, hashes, blocks)

	tester.newPeer("peer1", big.NewInt(10000), hashes[len(hashes)-1])

	hashes = append(extraHashes, hashes[:len(hashes)-1]...)
	tester.newPeer("peer2", big.NewInt(0), common.Hash{})

O
obscuren 已提交
173
	err := tester.sync("peer1", hashes[0])
174 175
	if err != nil {
		t.Error("download error", err)
176 177
	}

178
	inqueue := len(tester.downloader.queue.blockCache)
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
	if inqueue != targetBlocks {
		t.Error("expected", targetBlocks, "have", inqueue)
	}
}

func TestTaking(t *testing.T) {
	minDesiredPeerCount = 4
	blockTtl = 1 * time.Second

	targetBlocks := 1000
	hashes := createHashes(0, targetBlocks)
	blocks := createBlocksFromHashes(hashes)
	tester := newTester(t, hashes, blocks)

	tester.newPeer("peer1", big.NewInt(10000), hashes[0])
	tester.newPeer("peer2", big.NewInt(0), common.Hash{})
	tester.badBlocksPeer("peer3", big.NewInt(0), common.Hash{})
	tester.badBlocksPeer("peer4", big.NewInt(0), common.Hash{})

O
obscuren 已提交
198
	err := tester.sync("peer1", hashes[0])
199 200 201
	if err != nil {
		t.Error("download error", err)
	}
202
	bs := tester.downloader.TakeBlocks()
203 204
	if len(bs) != targetBlocks {
		t.Error("retrieved block mismatch: have %v, want %v", len(bs), targetBlocks)
205
	}
206
}
207

208 209 210 211 212 213
func TestInactiveDownloader(t *testing.T) {
	targetBlocks := 1000
	hashes := createHashes(0, targetBlocks)
	blocks := createBlocksFromHashSet(createHashSet(hashes))
	tester := newTester(t, hashes, nil)

214
	err := tester.downloader.DeliverHashes("bad peer 001", hashes)
215 216 217 218
	if err != errNoSyncActive {
		t.Error("expected no sync error, got", err)
	}

219
	err = tester.downloader.DeliverBlocks("bad peer 001", blocks)
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
	if err != errNoSyncActive {
		t.Error("expected no sync error, got", err)
	}
}

func TestCancel(t *testing.T) {
	minDesiredPeerCount = 4
	blockTtl = 1 * time.Second

	targetBlocks := 1000
	hashes := createHashes(0, targetBlocks)
	blocks := createBlocksFromHashes(hashes)
	tester := newTester(t, hashes, blocks)

	tester.newPeer("peer1", big.NewInt(10000), hashes[0])

	err := tester.sync("peer1", hashes[0])
	if err != nil {
		t.Error("download error", err)
	}

	if !tester.downloader.Cancel() {
		t.Error("cancel operation unsuccessfull")
	}

	hashSize, blockSize := tester.downloader.queue.Size()
	if hashSize > 0 || blockSize > 0 {
		t.Error("block (", blockSize, ") or hash (", hashSize, ") not 0")
	}
}

251 252 253 254
func TestThrottling(t *testing.T) {
	minDesiredPeerCount = 4
	blockTtl = 1 * time.Second

255
	targetBlocks := 16 * blockCacheLimit
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
	hashes := createHashes(0, targetBlocks)
	blocks := createBlocksFromHashes(hashes)
	tester := newTester(t, hashes, blocks)

	tester.newPeer("peer1", big.NewInt(10000), hashes[0])
	tester.newPeer("peer2", big.NewInt(0), common.Hash{})
	tester.badBlocksPeer("peer3", big.NewInt(0), common.Hash{})
	tester.badBlocksPeer("peer4", big.NewInt(0), common.Hash{})

	// Concurrently download and take the blocks
	errc := make(chan error, 1)
	go func() {
		errc <- tester.sync("peer1", hashes[0])
	}()

	done := make(chan struct{})
	took := []*types.Block{}
	go func() {
274
		for running := true; running; {
275 276
			select {
			case <-done:
277
				running = false
278
			default:
279
				time.Sleep(time.Millisecond)
280
			}
281
			// Take a batch of blocks and accumulate
282
			took = append(took, tester.downloader.TakeBlocks()...)
283
		}
284
		done <- struct{}{}
285 286
	}()

287
	// Synchronise the two threads and verify
288 289 290 291 292
	err := <-errc
	done <- struct{}{}
	<-done

	if err != nil {
293
		t.Fatalf("failed to synchronise blocks: %v", err)
294 295 296 297 298
	}
	if len(took) != targetBlocks {
		t.Fatalf("downloaded block mismatch: have %v, want %v", len(took), targetBlocks)
	}
}
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

// Tests that if a peer returns an invalid chain with a block pointing to a non-
// existing parent, it is correctly detected and handled.
func TestNonExistingParentAttack(t *testing.T) {
	// Forge a single-link chain with a forged header
	hashes := createHashes(0, 1)
	blocks := createBlocksFromHashes(hashes)

	forged := blocks[hashes[0]]
	forged.ParentHeaderHash = unknownHash

	// Try and sync with the malicious node and check that it fails
	tester := newTester(t, hashes, blocks)
	tester.newPeer("attack", big.NewInt(10000), hashes[0])
	if err := tester.sync("attack", hashes[0]); err != nil {
		t.Fatalf("failed to synchronise blocks: %v", err)
	}
316 317 318
	bs := tester.downloader.TakeBlocks()
	if len(bs) != 1 {
		t.Fatalf("retrieved block mismatch: have %v, want %v", len(bs), 1)
319
	}
320 321
	if tester.hasBlock(bs[0].ParentHash()) {
		t.Fatalf("tester knows about the unknown hash")
322 323 324 325 326 327 328 329 330
	}
	tester.downloader.Cancel()

	// Reconstruct a valid chain, and try to synchronize with it
	forged.ParentHeaderHash = knownHash
	tester.newPeer("valid", big.NewInt(20000), hashes[0])
	if err := tester.sync("valid", hashes[0]); err != nil {
		t.Fatalf("failed to synchronise blocks: %v", err)
	}
331
	bs = tester.downloader.TakeBlocks()
332
	if len(bs) != 1 {
333
		t.Fatalf("retrieved block mismatch: have %v, want %v", len(bs), 1)
334
	}
335 336 337
	if !tester.hasBlock(bs[0].ParentHash()) {
		t.Fatalf("tester doesn't know about the origin hash")
	}
338
}
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367

// Tests that if a malicious peers keeps sending us repeating hashes, we don't
// loop indefinitely.
func TestRepeatingHashAttack(t *testing.T) {
	// Create a valid chain, but drop the last link
	hashes := createHashes(1000, 1)
	blocks := createBlocksFromHashes(hashes)

	hashes = hashes[:len(hashes)-1]

	// Try and sync with the malicious node
	tester := newTester(t, hashes, blocks)
	tester.newPeer("attack", big.NewInt(10000), hashes[0])

	errc := make(chan error)
	go func() {
		errc <- tester.sync("attack", hashes[0])
	}()

	// Make sure that syncing returns and does so with a failure
	select {
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("synchronisation blocked")
	case err := <-errc:
		if err == nil {
			t.Fatalf("synchronisation succeeded")
		}
	}
}