mirror of https://github.com/hyperledger/besu
7311: add GetReceiptsFromPeerTask (#7638)
Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>pull/7837/head
parent
f9f721c10e
commit
db29df7c8d
@ -0,0 +1,135 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.ethereum.eth.manager.peertask.task; |
||||||
|
|
||||||
|
import static java.util.Collections.emptyList; |
||||||
|
|
||||||
|
import org.hyperledger.besu.datatypes.Hash; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||||
|
import org.hyperledger.besu.ethereum.core.TransactionReceipt; |
||||||
|
import org.hyperledger.besu.ethereum.eth.EthProtocol; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.peertask.InvalidPeerTaskResponseException; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTask; |
||||||
|
import org.hyperledger.besu.ethereum.eth.messages.GetReceiptsMessage; |
||||||
|
import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.BodyValidation; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; |
||||||
|
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.function.Predicate; |
||||||
|
|
||||||
|
public class GetReceiptsFromPeerTask |
||||||
|
implements PeerTask<Map<BlockHeader, List<TransactionReceipt>>> { |
||||||
|
|
||||||
|
private final Collection<BlockHeader> blockHeaders; |
||||||
|
private final ProtocolSchedule protocolSchedule; |
||||||
|
private final Map<BlockHeader, List<TransactionReceipt>> receiptsByBlockHeader = new HashMap<>(); |
||||||
|
private final Map<Hash, List<BlockHeader>> headersByReceiptsRoot = new HashMap<>(); |
||||||
|
private final long requiredBlockchainHeight; |
||||||
|
|
||||||
|
public GetReceiptsFromPeerTask( |
||||||
|
final Collection<BlockHeader> blockHeaders, final ProtocolSchedule protocolSchedule) { |
||||||
|
this.blockHeaders = new ArrayList<>(blockHeaders); |
||||||
|
this.protocolSchedule = protocolSchedule; |
||||||
|
|
||||||
|
// pre-fill any headers with an empty receipts root into the result map
|
||||||
|
this.blockHeaders.stream() |
||||||
|
.filter(header -> header.getReceiptsRoot().equals(Hash.EMPTY_TRIE_HASH)) |
||||||
|
.forEach(header -> receiptsByBlockHeader.put(header, emptyList())); |
||||||
|
this.blockHeaders.removeAll(receiptsByBlockHeader.keySet()); |
||||||
|
|
||||||
|
// group headers by their receipts root hash to reduce total number of receipts hashes requested
|
||||||
|
// for
|
||||||
|
this.blockHeaders.forEach( |
||||||
|
header -> |
||||||
|
headersByReceiptsRoot |
||||||
|
.computeIfAbsent(header.getReceiptsRoot(), key -> new ArrayList<>()) |
||||||
|
.add(header)); |
||||||
|
|
||||||
|
// calculate the minimum required blockchain height a peer will need to be able to fulfil this
|
||||||
|
// request
|
||||||
|
requiredBlockchainHeight = |
||||||
|
this.blockHeaders.stream() |
||||||
|
.mapToLong(BlockHeader::getNumber) |
||||||
|
.max() |
||||||
|
.orElse(BlockHeader.GENESIS_BLOCK_NUMBER); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public SubProtocol getSubProtocol() { |
||||||
|
return EthProtocol.get(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public MessageData getRequestMessage() { |
||||||
|
// Since we have to match up the data by receipt root, we only need to request receipts
|
||||||
|
// for one of the headers with each unique receipt root.
|
||||||
|
final List<Hash> blockHashes = |
||||||
|
headersByReceiptsRoot.values().stream() |
||||||
|
.map(headers -> headers.getFirst().getHash()) |
||||||
|
.toList(); |
||||||
|
return GetReceiptsMessage.create(blockHashes); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Map<BlockHeader, List<TransactionReceipt>> processResponse(final MessageData messageData) |
||||||
|
throws InvalidPeerTaskResponseException { |
||||||
|
if (messageData == null) { |
||||||
|
throw new InvalidPeerTaskResponseException(); |
||||||
|
} |
||||||
|
final ReceiptsMessage receiptsMessage = ReceiptsMessage.readFrom(messageData); |
||||||
|
final List<List<TransactionReceipt>> receiptsByBlock = receiptsMessage.receipts(); |
||||||
|
// take a copy of the pre-filled receiptsByBlockHeader, to ensure idempotency of subsequent
|
||||||
|
// calls to processResponse
|
||||||
|
final Map<BlockHeader, List<TransactionReceipt>> receiptsByHeader = |
||||||
|
new HashMap<>(receiptsByBlockHeader); |
||||||
|
if (!blockHeaders.isEmpty()) { |
||||||
|
if (receiptsByBlock.isEmpty() || receiptsByBlock.size() > blockHeaders.size()) { |
||||||
|
throw new InvalidPeerTaskResponseException(); |
||||||
|
} |
||||||
|
|
||||||
|
for (final List<TransactionReceipt> receiptsInBlock : receiptsByBlock) { |
||||||
|
final List<BlockHeader> blockHeaders = |
||||||
|
headersByReceiptsRoot.get(BodyValidation.receiptsRoot(receiptsInBlock)); |
||||||
|
if (blockHeaders == null) { |
||||||
|
// Contains receipts that we didn't request, so mustn't be the response we're looking for.
|
||||||
|
throw new InvalidPeerTaskResponseException(); |
||||||
|
} |
||||||
|
blockHeaders.forEach(header -> receiptsByHeader.put(header, receiptsInBlock)); |
||||||
|
} |
||||||
|
} |
||||||
|
return receiptsByHeader; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Predicate<EthPeer> getPeerRequirementFilter() { |
||||||
|
return (ethPeer) -> |
||||||
|
ethPeer.getProtocolName().equals(getSubProtocol().getName()) |
||||||
|
&& (protocolSchedule.anyMatch((ps) -> ps.spec().isPoS()) |
||||||
|
|| ethPeer.chainState().getEstimatedHeight() >= requiredBlockchainHeight); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isSuccess(final Map<BlockHeader, List<TransactionReceipt>> result) { |
||||||
|
return !result.isEmpty(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,264 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.ethereum.eth.manager.peertask.task; |
||||||
|
|
||||||
|
import org.hyperledger.besu.datatypes.Hash; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||||
|
import org.hyperledger.besu.ethereum.core.TransactionReceipt; |
||||||
|
import org.hyperledger.besu.ethereum.eth.EthProtocol; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.ChainState; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.peertask.InvalidPeerTaskResponseException; |
||||||
|
import org.hyperledger.besu.ethereum.eth.messages.EthPV63; |
||||||
|
import org.hyperledger.besu.ethereum.eth.messages.GetReceiptsMessage; |
||||||
|
import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.BodyValidation; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.junit.jupiter.api.Assertions; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.mockito.Mockito; |
||||||
|
|
||||||
|
public class GetReceiptsFromPeerTaskTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testGetSubProtocol() { |
||||||
|
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(Collections.emptyList(), null); |
||||||
|
Assertions.assertEquals(EthProtocol.get(), task.getSubProtocol()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testGetRequestMessage() { |
||||||
|
BlockHeader blockHeader1 = mockBlockHeader(1); |
||||||
|
TransactionReceipt receiptForBlock1 = |
||||||
|
new TransactionReceipt(1, 123, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader1.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock1))); |
||||||
|
|
||||||
|
BlockHeader blockHeader2 = mockBlockHeader(2); |
||||||
|
TransactionReceipt receiptForBlock2 = |
||||||
|
new TransactionReceipt(1, 456, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader2.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock2))); |
||||||
|
|
||||||
|
BlockHeader blockHeader3 = mockBlockHeader(3); |
||||||
|
TransactionReceipt receiptForBlock3 = |
||||||
|
new TransactionReceipt(1, 789, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader3.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock3))); |
||||||
|
|
||||||
|
GetReceiptsFromPeerTask task = |
||||||
|
new GetReceiptsFromPeerTask(List.of(blockHeader1, blockHeader2, blockHeader3), null); |
||||||
|
|
||||||
|
MessageData messageData = task.getRequestMessage(); |
||||||
|
GetReceiptsMessage getReceiptsMessage = GetReceiptsMessage.readFrom(messageData); |
||||||
|
|
||||||
|
Assertions.assertEquals(EthPV63.GET_RECEIPTS, getReceiptsMessage.getCode()); |
||||||
|
Iterable<Hash> hashesInMessage = getReceiptsMessage.hashes(); |
||||||
|
List<Hash> expectedHashes = |
||||||
|
List.of( |
||||||
|
Hash.fromHexString(StringUtils.repeat("00", 31) + "11"), |
||||||
|
Hash.fromHexString(StringUtils.repeat("00", 31) + "21"), |
||||||
|
Hash.fromHexString(StringUtils.repeat("00", 31) + "31")); |
||||||
|
List<Hash> actualHashes = new ArrayList<>(); |
||||||
|
hashesInMessage.forEach(actualHashes::add); |
||||||
|
|
||||||
|
Assertions.assertEquals(3, actualHashes.size()); |
||||||
|
Assertions.assertEquals( |
||||||
|
expectedHashes.stream().sorted().toList(), actualHashes.stream().sorted().toList()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testParseResponseWithNullResponseMessage() { |
||||||
|
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(Collections.emptyList(), null); |
||||||
|
Assertions.assertThrows( |
||||||
|
InvalidPeerTaskResponseException.class, () -> task.processResponse(null)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testParseResponseForInvalidResponse() { |
||||||
|
BlockHeader blockHeader1 = mockBlockHeader(1); |
||||||
|
TransactionReceipt receiptForBlock1 = |
||||||
|
new TransactionReceipt(1, 123, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader1.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock1))); |
||||||
|
|
||||||
|
BlockHeader blockHeader2 = mockBlockHeader(2); |
||||||
|
TransactionReceipt receiptForBlock2 = |
||||||
|
new TransactionReceipt(1, 456, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader2.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock2))); |
||||||
|
|
||||||
|
BlockHeader blockHeader3 = mockBlockHeader(3); |
||||||
|
TransactionReceipt receiptForBlock3 = |
||||||
|
new TransactionReceipt(1, 789, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader3.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock3))); |
||||||
|
GetReceiptsFromPeerTask task = |
||||||
|
new GetReceiptsFromPeerTask(List.of(blockHeader1, blockHeader2, blockHeader3), null); |
||||||
|
ReceiptsMessage receiptsMessage = |
||||||
|
ReceiptsMessage.create( |
||||||
|
List.of( |
||||||
|
List.of(receiptForBlock1), |
||||||
|
List.of(receiptForBlock2), |
||||||
|
List.of(receiptForBlock3), |
||||||
|
List.of( |
||||||
|
new TransactionReceipt(1, 101112, Collections.emptyList(), Optional.empty())))); |
||||||
|
|
||||||
|
Assertions.assertThrows( |
||||||
|
InvalidPeerTaskResponseException.class, () -> task.processResponse(receiptsMessage)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testParseResponse() throws InvalidPeerTaskResponseException { |
||||||
|
BlockHeader blockHeader1 = mockBlockHeader(1); |
||||||
|
TransactionReceipt receiptForBlock1 = |
||||||
|
new TransactionReceipt(1, 123, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader1.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock1))); |
||||||
|
|
||||||
|
BlockHeader blockHeader2 = mockBlockHeader(2); |
||||||
|
TransactionReceipt receiptForBlock2 = |
||||||
|
new TransactionReceipt(1, 456, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader2.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock2))); |
||||||
|
|
||||||
|
BlockHeader blockHeader3 = mockBlockHeader(3); |
||||||
|
TransactionReceipt receiptForBlock3 = |
||||||
|
new TransactionReceipt(1, 789, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader3.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock3))); |
||||||
|
|
||||||
|
BlockHeader blockHeader4 = mockBlockHeader(4); |
||||||
|
Mockito.when(blockHeader4.getReceiptsRoot()).thenReturn(Hash.EMPTY_TRIE_HASH); |
||||||
|
|
||||||
|
GetReceiptsFromPeerTask task = |
||||||
|
new GetReceiptsFromPeerTask( |
||||||
|
List.of(blockHeader1, blockHeader2, blockHeader3, blockHeader4), null); |
||||||
|
|
||||||
|
ReceiptsMessage receiptsMessage = |
||||||
|
ReceiptsMessage.create( |
||||||
|
List.of( |
||||||
|
List.of(receiptForBlock1), List.of(receiptForBlock2), List.of(receiptForBlock3))); |
||||||
|
|
||||||
|
Map<BlockHeader, List<TransactionReceipt>> resultMap = task.processResponse(receiptsMessage); |
||||||
|
|
||||||
|
Assertions.assertEquals(4, resultMap.size()); |
||||||
|
Assertions.assertEquals(Collections.emptyList(), resultMap.get(blockHeader4)); |
||||||
|
Assertions.assertEquals(List.of(receiptForBlock1), resultMap.get(blockHeader1)); |
||||||
|
Assertions.assertEquals(List.of(receiptForBlock2), resultMap.get(blockHeader2)); |
||||||
|
Assertions.assertEquals(List.of(receiptForBlock3), resultMap.get(blockHeader3)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testParseResponseForOnlyPrefilledEmptyTrieReceiptsRoots() |
||||||
|
throws InvalidPeerTaskResponseException { |
||||||
|
BlockHeader blockHeader1 = mockBlockHeader(1); |
||||||
|
Mockito.when(blockHeader1.getReceiptsRoot()).thenReturn(Hash.EMPTY_TRIE_HASH); |
||||||
|
|
||||||
|
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(List.of(blockHeader1), null); |
||||||
|
|
||||||
|
ReceiptsMessage receiptsMessage = ReceiptsMessage.create(Collections.emptyList()); |
||||||
|
|
||||||
|
Map<BlockHeader, List<TransactionReceipt>> resultMap = task.processResponse(receiptsMessage); |
||||||
|
|
||||||
|
Assertions.assertEquals(1, resultMap.size()); |
||||||
|
Assertions.assertEquals(Collections.emptyList(), resultMap.get(blockHeader1)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testGetPeerRequirementFilter() { |
||||||
|
BlockHeader blockHeader1 = mockBlockHeader(1); |
||||||
|
TransactionReceipt receiptForBlock1 = |
||||||
|
new TransactionReceipt(1, 123, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader1.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock1))); |
||||||
|
|
||||||
|
BlockHeader blockHeader2 = mockBlockHeader(2); |
||||||
|
TransactionReceipt receiptForBlock2 = |
||||||
|
new TransactionReceipt(1, 456, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader2.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock2))); |
||||||
|
|
||||||
|
BlockHeader blockHeader3 = mockBlockHeader(3); |
||||||
|
TransactionReceipt receiptForBlock3 = |
||||||
|
new TransactionReceipt(1, 789, Collections.emptyList(), Optional.empty()); |
||||||
|
Mockito.when(blockHeader3.getReceiptsRoot()) |
||||||
|
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock3))); |
||||||
|
|
||||||
|
ProtocolSchedule protocolSchedule = Mockito.mock(ProtocolSchedule.class); |
||||||
|
Mockito.when(protocolSchedule.anyMatch(Mockito.any())).thenReturn(false); |
||||||
|
|
||||||
|
GetReceiptsFromPeerTask task = |
||||||
|
new GetReceiptsFromPeerTask( |
||||||
|
List.of(blockHeader1, blockHeader2, blockHeader3), protocolSchedule); |
||||||
|
|
||||||
|
EthPeer failForIncorrectProtocol = mockPeer("incorrectProtocol", 5); |
||||||
|
EthPeer failForShortChainHeight = mockPeer("incorrectProtocol", 1); |
||||||
|
EthPeer successfulCandidate = mockPeer(EthProtocol.NAME, 5); |
||||||
|
|
||||||
|
Assertions.assertFalse(task.getPeerRequirementFilter().test(failForIncorrectProtocol)); |
||||||
|
Assertions.assertFalse(task.getPeerRequirementFilter().test(failForShortChainHeight)); |
||||||
|
Assertions.assertTrue(task.getPeerRequirementFilter().test(successfulCandidate)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testIsSuccessForPartialSuccess() { |
||||||
|
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(Collections.emptyList(), null); |
||||||
|
|
||||||
|
Assertions.assertFalse(task.isSuccess(Collections.emptyMap())); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testIsSuccessForFullSuccess() { |
||||||
|
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(Collections.emptyList(), null); |
||||||
|
|
||||||
|
Map<BlockHeader, List<TransactionReceipt>> map = new HashMap<>(); |
||||||
|
map.put(mockBlockHeader(1), null); |
||||||
|
|
||||||
|
Assertions.assertTrue(task.isSuccess(map)); |
||||||
|
} |
||||||
|
|
||||||
|
private BlockHeader mockBlockHeader(final long blockNumber) { |
||||||
|
BlockHeader blockHeader = Mockito.mock(BlockHeader.class); |
||||||
|
Mockito.when(blockHeader.getNumber()).thenReturn(blockNumber); |
||||||
|
// second to last hex digit indicates the blockNumber, last hex digit indicates the usage of the
|
||||||
|
// hash
|
||||||
|
Mockito.when(blockHeader.getHash()) |
||||||
|
.thenReturn(Hash.fromHexString(StringUtils.repeat("00", 31) + blockNumber + "1")); |
||||||
|
|
||||||
|
return blockHeader; |
||||||
|
} |
||||||
|
|
||||||
|
private EthPeer mockPeer(final String protocol, final long chainHeight) { |
||||||
|
EthPeer ethPeer = Mockito.mock(EthPeer.class); |
||||||
|
ChainState chainState = Mockito.mock(ChainState.class); |
||||||
|
|
||||||
|
Mockito.when(ethPeer.getProtocolName()).thenReturn(protocol); |
||||||
|
Mockito.when(ethPeer.chainState()).thenReturn(chainState); |
||||||
|
Mockito.when(chainState.getEstimatedHeight()).thenReturn(chainHeight); |
||||||
|
|
||||||
|
return ethPeer; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue