downloader_test.go 10.9 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
	return nil
}

func (dl *downloadTester) getBlocks(id string) func([]common.Hash) error {
	return func(hashes []common.Hash) error {
109 110 111 112 113
		blocks := make([]*types.Block, 0, len(hashes))
		for _, hash := range hashes {
			if block, ok := dl.blocks[hash]; ok {
				blocks = append(blocks, block)
			}
114
		}
115
		go dl.downloader.DeliverBlocks(id, blocks)
116 117 118 119 120 121 122 123

		return nil
	}
}

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

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

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

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

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

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

145
	tester.newPeer("peer1", big.NewInt(10000), hashes[0])
146 147 148
	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 已提交
149
	tester.activePeerId = "peer1"
150

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

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

func TestMissing(t *testing.T) {
163
	targetBlocks := 1000
164 165 166 167 168 169 170 171 172 173
	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 已提交
174
	err := tester.sync("peer1", hashes[0])
175 176
	if err != nil {
		t.Error("download error", err)
177 178
	}

179
	inqueue := len(tester.downloader.queue.blockCache)
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
	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 已提交
199
	err := tester.sync("peer1", hashes[0])
200 201 202
	if err != nil {
		t.Error("download error", err)
	}
203
	bs := tester.downloader.TakeBlocks()
204 205
	if len(bs) != targetBlocks {
		t.Error("retrieved block mismatch: have %v, want %v", len(bs), targetBlocks)
206
	}
207
}
208

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

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

220
	err = tester.downloader.DeliverBlocks("bad peer 001", blocks)
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 251
	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")
	}
}

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

256
	targetBlocks := 16 * blockCacheLimit
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
	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() {
275
		for running := true; running; {
276 277
			select {
			case <-done:
278
				running = false
279
			default:
280
				time.Sleep(time.Millisecond)
281
			}
282
			// Take a batch of blocks and accumulate
283
			took = append(took, tester.downloader.TakeBlocks()...)
284
		}
285
		done <- struct{}{}
286 287
	}()

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

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

// 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)
	}
317 318 319
	bs := tester.downloader.TakeBlocks()
	if len(bs) != 1 {
		t.Fatalf("retrieved block mismatch: have %v, want %v", len(bs), 1)
320
	}
321 322
	if tester.hasBlock(bs[0].ParentHash()) {
		t.Fatalf("tester knows about the unknown hash")
323 324 325 326 327 328 329 330 331
	}
	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)
	}
332
	bs = tester.downloader.TakeBlocks()
333
	if len(bs) != 1 {
334
		t.Fatalf("retrieved block mismatch: have %v, want %v", len(bs), 1)
335
	}
336 337 338
	if !tester.hasBlock(bs[0].ParentHash()) {
		t.Fatalf("tester doesn't know about the origin hash")
	}
339
}
340 341 342 343 344

// 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
345
	hashes := createHashes(0, blockCacheLimit)
346
	blocks := createBlocksFromHashes(hashes)
347
	forged := hashes[:len(hashes)-1]
348 349

	// Try and sync with the malicious node
350 351
	tester := newTester(t, forged, blocks)
	tester.newPeer("attack", big.NewInt(10000), forged[0])
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366

	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")
		}
	}
367 368 369 370 371 372
	// Ensure that a valid chain can still pass sync
	tester.hashes = hashes
	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)
	}
373
}
374 375 376 377 378

// Tests that if a malicious peers returns a non-existent block hash, it should
// eventually time out and the sync reattempted.
func TestNonExistingBlockAttack(t *testing.T) {
	// Create a valid chain, but forge the last link
379
	hashes := createHashes(0, blockCacheLimit)
380
	blocks := createBlocksFromHashes(hashes)
381
	origin := hashes[len(hashes)/2]
382 383 384 385 386 387 388 389 390

	hashes[len(hashes)/2] = 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 != errPeersUnavailable {
		t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errPeersUnavailable)
	}
391 392 393 394 395 396
	// Ensure that a valid chain can still pass sync
	hashes[len(hashes)/2] = origin
	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)
	}
397
}