|
|
|
@ -15,138 +15,191 @@ |
|
|
|
|
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.blockheaders; |
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
|
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; |
|
|
|
|
import static org.mockito.ArgumentMatchers.any; |
|
|
|
|
import static org.mockito.Mockito.doAnswer; |
|
|
|
|
import static org.mockito.Mockito.times; |
|
|
|
|
import static org.mockito.Mockito.verify; |
|
|
|
|
import static org.mockito.Mockito.verifyZeroInteractions; |
|
|
|
|
import static org.mockito.Mockito.when; |
|
|
|
|
|
|
|
|
|
import org.hyperledger.besu.crypto.SECP256K1.KeyPair; |
|
|
|
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResult; |
|
|
|
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; |
|
|
|
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.JsonRpcResult; |
|
|
|
|
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; |
|
|
|
|
import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; |
|
|
|
|
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; |
|
|
|
|
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; |
|
|
|
|
import org.hyperledger.besu.ethereum.chain.BlockAddedEvent; |
|
|
|
|
import org.hyperledger.besu.ethereum.chain.BlockchainStorage; |
|
|
|
|
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain; |
|
|
|
|
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.Block; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockBody; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockHeader; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.Difficulty; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.Hash; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.TransactionTestFixture; |
|
|
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.Collections; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockDataGenerator; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.TransactionReceipt; |
|
|
|
|
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; |
|
|
|
|
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; |
|
|
|
|
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; |
|
|
|
|
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; |
|
|
|
|
|
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Optional; |
|
|
|
|
import java.util.function.Consumer; |
|
|
|
|
|
|
|
|
|
import com.google.common.collect.Lists; |
|
|
|
|
import org.junit.Before; |
|
|
|
|
import org.junit.Test; |
|
|
|
|
import org.junit.runner.RunWith; |
|
|
|
|
import org.mockito.ArgumentCaptor; |
|
|
|
|
import org.mockito.Captor; |
|
|
|
|
import org.mockito.Mock; |
|
|
|
|
import org.mockito.Mockito; |
|
|
|
|
import org.mockito.Spy; |
|
|
|
|
import org.mockito.junit.MockitoJUnitRunner; |
|
|
|
|
|
|
|
|
|
@RunWith(MockitoJUnitRunner.class) |
|
|
|
|
public class NewBlockHeadersSubscriptionServiceTest { |
|
|
|
|
|
|
|
|
|
private NewBlockHeadersSubscriptionService newBlockHeadersSubscriptionService; |
|
|
|
|
|
|
|
|
|
@Captor ArgumentCaptor<Long> subscriptionIdCaptor; |
|
|
|
|
@Captor ArgumentCaptor<JsonRpcResult> responseCaptor; |
|
|
|
|
|
|
|
|
|
@Mock private SubscriptionManager subscriptionManager; |
|
|
|
|
@Mock private BlockchainQueries blockchainQueries; |
|
|
|
|
|
|
|
|
|
private final BlockHeaderTestFixture blockHeaderTestFixture = new BlockHeaderTestFixture(); |
|
|
|
|
private final TransactionTestFixture txTestFixture = new TransactionTestFixture(); |
|
|
|
|
private final BlockHeader blockHeader = blockHeaderTestFixture.buildHeader(); |
|
|
|
|
private final BlockResultFactory blockResultFactory = new BlockResultFactory(); |
|
|
|
|
private final BlockDataGenerator gen = new BlockDataGenerator(); |
|
|
|
|
private final BlockchainStorage blockchainStorage = |
|
|
|
|
new KeyValueStoragePrefixedKeyBlockchainStorage( |
|
|
|
|
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()); |
|
|
|
|
private final Block genesisBlock = gen.genesisBlock(); |
|
|
|
|
private final MutableBlockchain blockchain = |
|
|
|
|
DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, new NoOpMetricsSystem()); |
|
|
|
|
|
|
|
|
|
@Spy |
|
|
|
|
private final SubscriptionManager subscriptionManagerSpy = |
|
|
|
|
new SubscriptionManager(new NoOpMetricsSystem()); |
|
|
|
|
|
|
|
|
|
@Spy |
|
|
|
|
private final BlockchainQueries blockchainQueriesSpy = |
|
|
|
|
Mockito.spy(new BlockchainQueries(blockchain, createInMemoryWorldStateArchive())); |
|
|
|
|
|
|
|
|
|
@Before |
|
|
|
|
public void before() { |
|
|
|
|
newBlockHeadersSubscriptionService = |
|
|
|
|
new NewBlockHeadersSubscriptionService(subscriptionManager, blockchainQueries); |
|
|
|
|
final NewBlockHeadersSubscriptionService newBlockHeadersSubscriptionService = |
|
|
|
|
new NewBlockHeadersSubscriptionService(subscriptionManagerSpy, blockchainQueriesSpy); |
|
|
|
|
blockchain.observeBlockAdded(newBlockHeadersSubscriptionService); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldSendMessageWhenBlockAddedOnCanonicalChain() { |
|
|
|
|
final NewBlockHeadersSubscription subscription = createSubscription(false); |
|
|
|
|
mockSubscriptionManagerNotifyMethod(subscription); |
|
|
|
|
final BlockResult expectedNewBlock = expectedBlockWithTransactions(Collections.emptyList()); |
|
|
|
|
|
|
|
|
|
simulateAddingBlockOnCanonicalChain(); |
|
|
|
|
final Block testBlock = appendBlockWithParent(blockchain, blockchain.getChainHeadBlock()); |
|
|
|
|
final BlockResult expectedNewBlock = |
|
|
|
|
blockResultFactory.transactionHash( |
|
|
|
|
blockchainQueriesSpy.blockByHashWithTxHashes(testBlock.getHash()).orElse(null)); |
|
|
|
|
|
|
|
|
|
verify(subscriptionManager) |
|
|
|
|
verify(subscriptionManagerSpy) |
|
|
|
|
.sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); |
|
|
|
|
assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); |
|
|
|
|
assertThat(responseCaptor.getValue()) |
|
|
|
|
.isEqualToComparingFieldByFieldRecursively(expectedNewBlock); |
|
|
|
|
assertThat(responseCaptor.getValue()).usingRecursiveComparison().isEqualTo(expectedNewBlock); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldNotSendMessageWhenBlockAddedIsNotOnCanonicalChain() { |
|
|
|
|
simulateAddingBlockOnNonCanonicalChain(); |
|
|
|
|
final NewBlockHeadersSubscription subscription = createSubscription(false); |
|
|
|
|
mockSubscriptionManagerNotifyMethod(subscription); |
|
|
|
|
|
|
|
|
|
final Block canonicalBlock = appendBlockWithParent(blockchain, genesisBlock); |
|
|
|
|
final BlockOptions options = |
|
|
|
|
new BlockOptions() |
|
|
|
|
.setBlockNumber(genesisBlock.getHeader().getNumber() + 1) |
|
|
|
|
.setParentHash(genesisBlock.getHash()) |
|
|
|
|
.setDifficulty(genesisBlock.getHeader().getDifficulty().divide(100L)); |
|
|
|
|
appendBlockWithParent(blockchain, options); |
|
|
|
|
final BlockResult expectedNewBlock = |
|
|
|
|
blockResultFactory.transactionHash( |
|
|
|
|
blockchainQueriesSpy.blockByHashWithTxHashes(canonicalBlock.getHash()).orElse(null)); |
|
|
|
|
|
|
|
|
|
verify(subscriptionManagerSpy, times(1)).notifySubscribersOnWorkerThread(any(), any(), any()); |
|
|
|
|
verify(subscriptionManagerSpy, times(1)) |
|
|
|
|
.sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); |
|
|
|
|
assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); |
|
|
|
|
List<JsonRpcResult> capturedNewBlocks = responseCaptor.getAllValues(); |
|
|
|
|
assertThat(capturedNewBlocks.size()).isEqualTo(1); |
|
|
|
|
assertThat(capturedNewBlocks.get(0)).usingRecursiveComparison().isEqualTo(expectedNewBlock); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldSendMessagesWhenReorgBlockAdded() { |
|
|
|
|
final NewBlockHeadersSubscription subscription = createSubscription(false); |
|
|
|
|
mockSubscriptionManagerNotifyMethod(subscription); |
|
|
|
|
|
|
|
|
|
verifyZeroInteractions(subscriptionManager); |
|
|
|
|
final Block canonicalBlock = appendBlockWithParent(blockchain, genesisBlock); |
|
|
|
|
final BlockOptions options = |
|
|
|
|
new BlockOptions() |
|
|
|
|
.setBlockNumber(genesisBlock.getHeader().getNumber() + 1) |
|
|
|
|
.setParentHash(genesisBlock.getHash()) |
|
|
|
|
.setDifficulty(genesisBlock.getHeader().getDifficulty().divide(100L)); |
|
|
|
|
final Block forkBlock = appendBlockWithParent(blockchain, options); |
|
|
|
|
options.setDifficulty(forkBlock.getHeader().getDifficulty().divide(100L)); |
|
|
|
|
appendBlockWithParent(blockchain, options); |
|
|
|
|
options.setDifficulty(blockchain.getChainHeadBlock().getHeader().getDifficulty().multiply(2L)); |
|
|
|
|
final Block forkBlock2 = appendBlockWithParent(blockchain, options); |
|
|
|
|
final BlockResult expectedNewBlock = |
|
|
|
|
blockResultFactory.transactionHash( |
|
|
|
|
blockchainQueriesSpy.blockByHashWithTxHashes(canonicalBlock.getHash()).orElse(null)); |
|
|
|
|
final BlockResult expectedNewBlock1 = |
|
|
|
|
blockResultFactory.transactionHash( |
|
|
|
|
blockchainQueriesSpy.blockByHashWithTxHashes(forkBlock2.getHash()).orElse(null)); |
|
|
|
|
|
|
|
|
|
verify(subscriptionManagerSpy, times(2)).notifySubscribersOnWorkerThread(any(), any(), any()); |
|
|
|
|
verify(subscriptionManagerSpy, times(2)) |
|
|
|
|
.sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); |
|
|
|
|
assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); |
|
|
|
|
List<JsonRpcResult> capturedNewBlocks = responseCaptor.getAllValues(); |
|
|
|
|
assertThat(capturedNewBlocks.size()).isEqualTo(2); |
|
|
|
|
assertThat(capturedNewBlocks.get(0)).usingRecursiveComparison().isEqualTo(expectedNewBlock); |
|
|
|
|
assertThat(capturedNewBlocks.get(1)).usingRecursiveComparison().isEqualTo(expectedNewBlock1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldReturnTxHashesWhenIncludeTransactionsFalse() { |
|
|
|
|
final NewBlockHeadersSubscription subscription = createSubscription(false); |
|
|
|
|
mockSubscriptionManagerNotifyMethod(subscription); |
|
|
|
|
final List<Hash> txHashList = transactionsWithHashOnly(); |
|
|
|
|
final BlockResult expectedNewBlock = expectedBlockWithTransactions(txHashList); |
|
|
|
|
|
|
|
|
|
simulateAddingBlockOnCanonicalChain(); |
|
|
|
|
final Block testBlock = appendBlockWithParent(blockchain, blockchain.getChainHeadBlock()); |
|
|
|
|
final BlockResult expectedNewBlock = |
|
|
|
|
blockResultFactory.transactionHash( |
|
|
|
|
blockchainQueriesSpy.blockByHashWithTxHashes(testBlock.getHash()).orElse(null)); |
|
|
|
|
|
|
|
|
|
verify(subscriptionManager) |
|
|
|
|
verify(subscriptionManagerSpy) |
|
|
|
|
.sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); |
|
|
|
|
assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); |
|
|
|
|
final Object actualBlock = responseCaptor.getValue(); |
|
|
|
|
assertThat(actualBlock).isInstanceOf(BlockResult.class); |
|
|
|
|
assertThat(((BlockResult) actualBlock).getTransactions()).hasSize(txHashList.size()); |
|
|
|
|
assertThat(actualBlock).isEqualToComparingFieldByFieldRecursively(expectedNewBlock); |
|
|
|
|
assertThat(((BlockResult) actualBlock).getTransactions()) |
|
|
|
|
.hasSize(expectedNewBlock.getTransactions().size()); |
|
|
|
|
assertThat(actualBlock).usingRecursiveComparison().isEqualTo(expectedNewBlock); |
|
|
|
|
|
|
|
|
|
verify(blockchainQueries, times(1)).blockByHashWithTxHashes(any()); |
|
|
|
|
verify(blockchainQueries, times(0)).blockByHash(any()); |
|
|
|
|
verify(blockchainQueriesSpy, times(2)).blockByHashWithTxHashes(any()); |
|
|
|
|
verify(blockchainQueriesSpy, times(0)).blockByHash(any()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldReturnCompleteTxWhenParameterTrue() { |
|
|
|
|
final NewBlockHeadersSubscription subscription = createSubscription(true); |
|
|
|
|
mockSubscriptionManagerNotifyMethod(subscription); |
|
|
|
|
final List<TransactionWithMetadata> txHashList = transactionsWithMetadata(); |
|
|
|
|
final BlockWithMetadata<TransactionWithMetadata, Hash> testBlockWithMetadata = |
|
|
|
|
new BlockWithMetadata<>( |
|
|
|
|
blockHeader, txHashList, Collections.emptyList(), blockHeader.getDifficulty(), 0); |
|
|
|
|
final BlockResult expectedNewBlock = |
|
|
|
|
blockResultFactory.transactionComplete(testBlockWithMetadata); |
|
|
|
|
when(blockchainQueries.blockByHash(testBlockWithMetadata.getHeader().getHash())) |
|
|
|
|
.thenReturn(Optional.of(testBlockWithMetadata)); |
|
|
|
|
|
|
|
|
|
simulateAddingBlockOnCanonicalChain(); |
|
|
|
|
final Block testBlock = appendBlockWithParent(blockchain, blockchain.getChainHeadBlock()); |
|
|
|
|
final BlockResult expectedNewBlock = |
|
|
|
|
blockResultFactory.transactionComplete( |
|
|
|
|
blockchainQueriesSpy.blockByHash(testBlock.getHeader().getHash()).orElse(null)); |
|
|
|
|
|
|
|
|
|
verify(subscriptionManager) |
|
|
|
|
verify(subscriptionManagerSpy) |
|
|
|
|
.sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); |
|
|
|
|
|
|
|
|
|
assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); |
|
|
|
|
|
|
|
|
|
final Object actualBlock = responseCaptor.getValue(); |
|
|
|
|
assertThat(actualBlock).isInstanceOf(BlockResult.class); |
|
|
|
|
assertThat(((BlockResult) actualBlock).getTransactions()).hasSize(txHashList.size()); |
|
|
|
|
assertThat(actualBlock).isEqualToComparingFieldByFieldRecursively(expectedNewBlock); |
|
|
|
|
assertThat(((BlockResult) actualBlock).getTransactions()) |
|
|
|
|
.hasSize(testBlock.getBody().getTransactions().size()); |
|
|
|
|
assertThat(actualBlock).usingRecursiveComparison().isEqualTo(expectedNewBlock); |
|
|
|
|
|
|
|
|
|
verify(subscriptionManager, times(1)).sendMessage(any(), any()); |
|
|
|
|
verify(blockchainQueries, times(0)).blockByHashWithTxHashes(any()); |
|
|
|
|
verify(blockchainQueries, times(1)).blockByHash(any()); |
|
|
|
|
verify(subscriptionManagerSpy, times(1)).sendMessage(any(), any()); |
|
|
|
|
verify(blockchainQueriesSpy, times(0)).blockByHashWithTxHashes(any()); |
|
|
|
|
verify(blockchainQueriesSpy, times(2)).blockByHash(any()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@ -157,9 +210,9 @@ public class NewBlockHeadersSubscriptionServiceTest { |
|
|
|
|
final NewBlockHeadersSubscription subscription4 = createSubscription(false); |
|
|
|
|
mockSubscriptionManagerNotifyMethod(subscription1, subscription2, subscription3, subscription4); |
|
|
|
|
|
|
|
|
|
simulateAddingBlockOnCanonicalChain(); |
|
|
|
|
appendBlockWithParent(blockchain, blockchain.getChainHeadBlock()); |
|
|
|
|
|
|
|
|
|
verify(subscriptionManager, times(4)) |
|
|
|
|
verify(subscriptionManagerSpy, times(4)) |
|
|
|
|
.sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); |
|
|
|
|
assertThat(subscriptionIdCaptor.getAllValues()) |
|
|
|
|
.containsExactly( |
|
|
|
@ -168,18 +221,8 @@ public class NewBlockHeadersSubscriptionServiceTest { |
|
|
|
|
subscription3.getSubscriptionId(), |
|
|
|
|
subscription4.getSubscriptionId()); |
|
|
|
|
|
|
|
|
|
verify(blockchainQueries, times(1)).blockByHashWithTxHashes(any()); |
|
|
|
|
verify(blockchainQueries, times(1)).blockByHash(any()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private BlockResult expectedBlockWithTransactions(final List<Hash> objects) { |
|
|
|
|
final BlockWithMetadata<Hash, Hash> testBlockWithMetadata = |
|
|
|
|
new BlockWithMetadata<>(blockHeader, objects, Collections.emptyList(), Difficulty.ONE, 1); |
|
|
|
|
final BlockResult expectedNewBlock = blockResultFactory.transactionHash(testBlockWithMetadata); |
|
|
|
|
|
|
|
|
|
when(blockchainQueries.blockByHashWithTxHashes(testBlockWithMetadata.getHeader().getHash())) |
|
|
|
|
.thenReturn(Optional.of(testBlockWithMetadata)); |
|
|
|
|
return expectedNewBlock; |
|
|
|
|
verify(blockchainQueriesSpy, times(1)).blockByHashWithTxHashes(any()); |
|
|
|
|
verify(blockchainQueriesSpy, times(1)).blockByHash(any()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void mockSubscriptionManagerNotifyMethod( |
|
|
|
@ -190,46 +233,29 @@ public class NewBlockHeadersSubscriptionServiceTest { |
|
|
|
|
consumer.accept(List.of(subscriptions)); |
|
|
|
|
return null; |
|
|
|
|
}) |
|
|
|
|
.when(subscriptionManager) |
|
|
|
|
.when(subscriptionManagerSpy) |
|
|
|
|
.notifySubscribersOnWorkerThread(any(), any(), any()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void simulateAddingBlockOnCanonicalChain() { |
|
|
|
|
final BlockBody blockBody = new BlockBody(Collections.emptyList(), Collections.emptyList()); |
|
|
|
|
final Block testBlock = new Block(blockHeader, blockBody); |
|
|
|
|
newBlockHeadersSubscriptionService.onBlockAdded( |
|
|
|
|
BlockAddedEvent.createForHeadAdvancement(testBlock, Collections.emptyList()), |
|
|
|
|
blockchainQueries.getBlockchain()); |
|
|
|
|
verify(blockchainQueries, times(1)).getBlockchain(); |
|
|
|
|
private NewBlockHeadersSubscription createSubscription(final boolean includeTransactions) { |
|
|
|
|
return new NewBlockHeadersSubscription(1L, "conn", includeTransactions); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void simulateAddingBlockOnNonCanonicalChain() { |
|
|
|
|
final BlockBody blockBody = new BlockBody(Collections.emptyList(), Collections.emptyList()); |
|
|
|
|
final Block testBlock = new Block(blockHeader, blockBody); |
|
|
|
|
newBlockHeadersSubscriptionService.onBlockAdded( |
|
|
|
|
BlockAddedEvent.createForFork(testBlock), blockchainQueries.getBlockchain()); |
|
|
|
|
verify(blockchainQueries, times(1)).getBlockchain(); |
|
|
|
|
} |
|
|
|
|
private Block appendBlockWithParent(final MutableBlockchain blockchain, final Block parent) { |
|
|
|
|
final BlockOptions options = |
|
|
|
|
new BlockOptions() |
|
|
|
|
.setBlockNumber(parent.getHeader().getNumber() + 1) |
|
|
|
|
.setParentHash(parent.getHash()); |
|
|
|
|
|
|
|
|
|
private List<TransactionWithMetadata> transactionsWithMetadata() { |
|
|
|
|
final TransactionWithMetadata t1 = |
|
|
|
|
new TransactionWithMetadata( |
|
|
|
|
txTestFixture.createTransaction(KeyPair.generate()), 0L, Hash.ZERO, 0); |
|
|
|
|
final TransactionWithMetadata t2 = |
|
|
|
|
new TransactionWithMetadata( |
|
|
|
|
txTestFixture.createTransaction(KeyPair.generate()), 1L, Hash.ZERO, 1); |
|
|
|
|
return Lists.newArrayList(t1, t2); |
|
|
|
|
return appendBlockWithParent(blockchain, options); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private List<Hash> transactionsWithHashOnly() { |
|
|
|
|
final List<Hash> hashes = new ArrayList<>(); |
|
|
|
|
for (final TransactionWithMetadata transactionWithMetadata : transactionsWithMetadata()) { |
|
|
|
|
hashes.add(transactionWithMetadata.getTransaction().getHash()); |
|
|
|
|
} |
|
|
|
|
return hashes; |
|
|
|
|
} |
|
|
|
|
private Block appendBlockWithParent( |
|
|
|
|
final MutableBlockchain blockchain, final BlockOptions options) { |
|
|
|
|
final Block newBlock = gen.block(options); |
|
|
|
|
final List<TransactionReceipt> receipts = gen.receipts(newBlock); |
|
|
|
|
blockchain.appendBlock(newBlock, receipts); |
|
|
|
|
|
|
|
|
|
private NewBlockHeadersSubscription createSubscription(final boolean includeTransactions) { |
|
|
|
|
return new NewBlockHeadersSubscription(1L, "conn", includeTransactions); |
|
|
|
|
return newBlock; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|