-
Notifications
You must be signed in to change notification settings - Fork 130
[PIE-7] Ignore transactions from the network while behind chain head #1228
Conversation
this.pendingTransactions = pendingTransactions; | ||
this.protocolSchedule = protocolSchedule; | ||
this.protocolContext = protocolContext; | ||
this.transactionBatchAddedListener = transactionBatchAddedListener; | ||
this.synchronizer = synchronizer; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
886a07f
to
08228ff
Compare
ethereum/eth/build.gradle
Outdated
@@ -55,4 +55,7 @@ dependencies { | |||
testImplementation 'org.mockito:mockito-core' | |||
|
|||
jmhImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts') | |||
|
|||
testImplementation project(path: ':config', configuration: 'testSupportArtifacts') |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
16d2a60
to
3ea92bb
Compare
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()); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
PR description
Builds on PR #
1251
, i.e. "migrate TransactionPool (& affiliated test) from 'core' to 'eth'".Adding
SyncState
inTransactionPoolFactory.createTransactionPool()
, that way we can leave extraneous elements of the system alone.Utilizing
isInSync()
ofSyncState
, i.e.:Fixed Issue(s)