/* * Copyright (c) [2016] [ <ether.camp> ] * This file is part of the ethereumJ library. * * The ethereumJ 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 ethereumJ 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 ethereumJ library. If not, see <http://www.gnu.org/licenses/>. */ package org.ethereum.sync; import org.ethereum.core.Block; import org.ethereum.core.BlockHeaderWrapper; import org.ethereum.util.ByteArrayMap; import org.ethereum.util.FastByteComparisons; import org.ethereum.util.MinMaxMap; import java.util.*; /** * Created by Anton Nashatyrev on 27.10.2016. */ public class SyncQueueReverseImpl implements SyncQueueIfc { byte[] curHeaderHash; // List<BlockHeaderWrapper> headers = new ArrayList<>(); MinMaxMap<BlockHeaderWrapper> headers = new MinMaxMap<>(); long minValidated = -1; ByteArrayMap<Block> blocks = new ByteArrayMap<>(); boolean headersOnly; public SyncQueueReverseImpl(byte[] startHash) { this.curHeaderHash = startHash; } public SyncQueueReverseImpl(byte[] startHash, boolean headersOnly) { this.curHeaderHash = startHash; this.headersOnly = headersOnly; } @Override public synchronized List<HeadersRequest> requestHeaders(int maxSize, int maxRequests, int maxTotalHeaders) { List<HeadersRequest> ret = new ArrayList<>(); if (minValidated < 0) { ret.add(new SyncQueueImpl.HeadersRequestImpl(curHeaderHash, maxSize, true, maxSize - 1)); } else if (minValidated == 0) { // genesis reached return null; } else { if (minValidated - headers.getMin() < maxSize * maxSize && minValidated > maxSize) { ret.add(new SyncQueueImpl.HeadersRequestImpl( headers.get(headers.getMin()).getHash(), maxSize, true, maxSize - 1)); maxRequests--; } Set<Map.Entry<Long, BlockHeaderWrapper>> entries = headers.descendingMap().subMap(minValidated, true, headers.getMin(), true).entrySet(); Iterator<Map.Entry<Long, BlockHeaderWrapper>> it = entries.iterator(); BlockHeaderWrapper prevEntry = it.next().getValue(); while(maxRequests > 0 && it.hasNext()) { BlockHeaderWrapper entry = it.next().getValue(); if (prevEntry.getNumber() - entry.getNumber() > 1) { ret.add(new SyncQueueImpl.HeadersRequestImpl(prevEntry.getHash(), maxSize, true)); maxRequests--; } prevEntry = entry; } if (maxRequests > 0) { ret.add(new SyncQueueImpl.HeadersRequestImpl(prevEntry.getHash(), maxSize, true)); } } return ret; } @Override public synchronized List<BlockHeaderWrapper> addHeaders(Collection<BlockHeaderWrapper> newHeaders) { if (minValidated < 0) { // need to fetch initial header for (BlockHeaderWrapper header : newHeaders) { if (FastByteComparisons.equal(curHeaderHash, header.getHash())) { minValidated = header.getNumber(); headers.put(header.getNumber(), header); } } } // start header not found or we are already done if (minValidated <= 0) return Collections.emptyList(); for (BlockHeaderWrapper header : newHeaders) { if (header.getNumber() < minValidated) { headers.put(header.getNumber(), header); } } if (minValidated == -1) minValidated = headers.getMax(); for (; minValidated >= headers.getMin() ; minValidated--) { BlockHeaderWrapper header = headers.get(minValidated); BlockHeaderWrapper parent = headers.get(minValidated - 1); if (parent == null) { // Some peers doesn't return 0 block header if (minValidated == 1) minValidated = 0; break; } if (!FastByteComparisons.equal(header.getHeader().getParentHash(), parent.getHash())) { // chain is broken here (unlikely) - refetch the rest headers.clearAllBefore(header.getNumber()); break; } } if (headersOnly) { List<BlockHeaderWrapper> ret = new ArrayList<>(); for (long i = headers.getMax(); i > minValidated; i--) { ret.add(headers.remove(i)); } return ret; } else { return null; } } @Override public synchronized BlocksRequest requestBlocks(int maxSize) { List<BlockHeaderWrapper> reqHeaders = new ArrayList<>(); for (BlockHeaderWrapper header : headers.descendingMap().values()) { if (maxSize == 0) break; if (blocks.get(header.getHash()) == null) { reqHeaders.add(header); maxSize--; } } return new SyncQueueImpl.BlocksRequestImpl(reqHeaders); } @Override public synchronized List<Block> addBlocks(Collection<Block> newBlocks) { for (Block block : newBlocks) { blocks.put(block.getHash(), block); } List<Block> ret = new ArrayList<>(); for (long i = headers.getMax(); i > minValidated; i--) { Block block = blocks.get(headers.get(i).getHash()); if (block == null) break; ret.add(block); blocks.remove(headers.get(i).getHash()); headers.remove(i); } return ret; } @Override public synchronized int getHeadersCount() { return headers.size(); } }