Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

[PIE-7] Ignore transactions from the network while behind chain head #1228

Merged
merged 5 commits into from
Apr 15, 2019

Conversation

smatthewenglish
Copy link
Contributor

@smatthewenglish smatthewenglish commented Apr 6, 2019

PR description

  • Builds on PR #1251, i.e. "migrate TransactionPool (& affiliated test) from 'core' to 'eth'".

  • Adding SyncState in TransactionPoolFactory.createTransactionPool(), that way we can leave extraneous elements of the system alone.

  • Utilizing isInSync() of SyncState, i.e.:

  public boolean isInSync() {
    return syncTarget
        .map(
            t -> t.estimatedTargetHeight() - blockchain.getChainHeadBlockNumber() <= SYNC_TOLERANCE)
        .orElse(true);
  }

Fixed Issue(s)

this.pendingTransactions = pendingTransactions;
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
this.transactionBatchAddedListener = transactionBatchAddedListener;
this.synchronizer = synchronizer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just pass around a shared SyncStatus object instead of the whole synchronizer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a constructive review comment. Obviously we can do that -- do you think we ought to do that? If that's what you're suggesting, why do you feel so? Why would that be better than the current paradigm?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was the implied subtext. Let me try the direct approach.

I don't think the entire synchronizer object should be passed around when all we need is the sync state. Change it and pass the SyncState object instead unless there is a technical reason we cannot.

Copy link
Contributor

@shemnon shemnon Apr 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to do this right we need to change how we are handling the SyncStatus object. Currently it is an Optional and not returned when we are in sync.

I propose we make it always return SyncStatus, not an Optional. We also add a field that maps the three current states the sync can be in: NOT_STARTED, SYNCING, and UP_TO_DATE. NOT_STARTED corresponds to a highest block of zero, and up to date corresponds to current block equal to highest block. We can also add a method to tell us how many blocks behind we are and use that in the logic where we would drop inbound transactions.

This object is shared between the synchronizer and the transaction pool. Synchronizer writes to it and tx pool reads.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to share SyncState the same way the AbstractMiningCoordinator does. SyncStatus is not what you want - it's only for the JSON-RPC side of things (we should, separate to this work, push it to be just in the JSON-RPC realm and not in Synchronizer at all).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, SyncState. We will still need methods to tell us how far out of sync we are. We may also want a means to tell if we are out of sync because we've never started a sync. In that case I don't think we should drop transactions because we may be only a little behind, or a whole lot.

@@ -169,6 +169,7 @@ public void subscriptionToMinerNodeMustReceivePublishEvent() {
}

@Test
@Ignore
Copy link
Contributor

@shemnon shemnon Apr 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these tests are showing a genuine failure. The bug says "too far behind" not "behind in any respect." These messages are getting dropped because the full node in a process runner is about 3-4 blocks behind. We don't see this in the thread runner because there is no process and JVM startup overhead and the full node doesn't start behind the miner.

@@ -78,6 +81,12 @@ public TransactionPool(
}

public void addRemoteTransactions(final Collection<Transaction> transactions) {

if (synchronizer.getSyncStatus().isPresent()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too aggressive. If we are behind by as much as 1 block we are going to drop transactions. This can happen from the p2p network's normal operation. We need a threshold, stored in the SynchronizerConfiguration, such as 100. If we are syncing and more than that threshold behind then we can drop results. But if we are not syncing or are within the threshold we should hold onto it.

this.pendingTransactions = pendingTransactions;
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
this.transactionBatchAddedListener = transactionBatchAddedListener;
this.synchronizer = synchronizer;
Copy link
Contributor

@shemnon shemnon Apr 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to do this right we need to change how we are handling the SyncStatus object. Currently it is an Optional and not returned when we are in sync.

I propose we make it always return SyncStatus, not an Optional. We also add a field that maps the three current states the sync can be in: NOT_STARTED, SYNCING, and UP_TO_DATE. NOT_STARTED corresponds to a highest block of zero, and up to date corresponds to current block equal to highest block. We can also add a method to tell us how many blocks behind we are and use that in the logic where we would drop inbound transactions.

This object is shared between the synchronizer and the transaction pool. Synchronizer writes to it and tx pool reads.

@smatthewenglish smatthewenglish force-pushed the p7 branch 10 times, most recently from 886a07f to 08228ff Compare April 10, 2019 07:49
@smatthewenglish smatthewenglish requested a review from shemnon April 10, 2019 07:58
@@ -55,4 +55,7 @@ dependencies {
testImplementation 'org.mockito:mockito-core'

jmhImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')

testImplementation project(path: ':config', configuration: 'testSupportArtifacts')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alphabetize this with the rest of the testImplementations. At about line 46

@@ -80,6 +91,9 @@ public TransactionPool(
public void addRemoteTransactions(final Collection<Transaction> transactions) {
final Set<Transaction> addedTransactions = new HashSet<>();
for (final Transaction transaction : sortByNonce(transactions)) {
if (!syncState.isInSync()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like isInSync has most of the sync tolerance I was looking for already built in. The acceptance criteria listed 100 blocks and the default is 5. We cannot just change the default for isInSync as it is is also used by mining and 5 is their threshold.

Can you update the isInSync to have a version that takes an argument and for this call use 100 (and make the no-args version call the args version with that constant)? Either as a local constant or one of the new unstable options via SynchnizerConfiguration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good -- I made it a local constant 'cause I don't know how SynchnizerConfiguration works

@smatthewenglish smatthewenglish force-pushed the p7 branch 6 times, most recently from 16d2a60 to 3ea92bb Compare April 11, 2019 11:00
@shemnon
Copy link
Contributor

shemnon commented Apr 11, 2019

I'll give it another pass once #1251 is merged into master, it looks like both PRs are in this PR.

@@ -35,13 +37,16 @@ public static TransactionPool createTransactionPool(
final PeerTransactionTracker transactionTracker = new PeerTransactionTracker();
final TransactionsMessageSender transactionsMessageSender =
new TransactionsMessageSender(transactionTracker);
final MutableBlockchain blockchain = protocolContext.getBlockchain();
final SyncState syncState = new SyncState(blockchain, ethContext.getEthPeers());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be using the shared SyncState that the controllers use. So add this as a param to createTransactionPool and have the callers pass their instance. They all should have one by the point they make this call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah -- that makes sense. branch has been updated accordingly

@smatthewenglish smatthewenglish merged commit 42ecfb5 into PegaSysEng:master Apr 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants