Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set max persisted conflicts signers #2908

Closed
wants to merge 18 commits into from
Closed

Set max persisted conflicts signers #2908

wants to merge 18 commits into from

Conversation

shargon
Copy link
Member

@shargon shargon commented Sep 15, 2023

Partially fix of #2907

First step, ledger fix
Second step, memory pool fix ( @AnnaShaleva thanks for it)

src/Neo/Network/P2P/Payloads/Conflicts.cs Outdated Show resolved Hide resolved
src/Neo/Network/P2P/Payloads/Conflicts.cs Show resolved Hide resolved
src/Neo/Network/P2P/Payloads/Conflicts.cs Outdated Show resolved Hide resolved
@shargon shargon changed the title Set max conflicts attributes Set max conflicts signers Sep 15, 2023
Comment on lines 44 to 60
// Check already stored signers

var conflictingSigners = tx.Signers.Select(s => s.Account);

foreach (var attr in tx.GetAttributes<Conflicts>())
{
var conflictRecord = snapshot.TryGet(
new KeyBuilder(NativeContract.Ledger.Id, LedgerContract.Prefix_Transaction).Add(attr.Hash))?
.GetInteroperable<TransactionState>();
if (conflictRecord == null) continue;

if (conflictRecord.ConflictingSigners.Concat(conflictingSigners).Distinct().Count() > 100)
{
return false;
}
}

Copy link
Member

Choose a reason for hiding this comment

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

The direction is right, this will fix the problem (if, of course, the true source of the problem is the conflicting signers list overflow). We need to test this patch against the nice scenario provided by @vang1ong7ang in #2907 (comment).

src/Neo/Network/P2P/Payloads/Conflicts.cs Outdated Show resolved Hide resolved
src/Neo/Network/P2P/Payloads/Conflicts.cs Outdated Show resolved Hide resolved
src/Neo/Network/P2P/Payloads/Conflicts.cs Outdated Show resolved Hide resolved
@dusmart
Copy link

dusmart commented Sep 15, 2023

By the way, I think this PR's idea will solve this golang-C# inconsistency indeed if golang follows. #2907 (comment)

While I'm not sure if it can prevent the entire issue of #2907 if no mempool change is made.

@vang1ong7ang
Copy link
Contributor

I think @AnnaShaleva knows what happened and works on the right direction

@vang1ong7ang
Copy link
Contributor

vang1ong7ang commented Sep 15, 2023

Adding over-tight restrictions doesn't completely solve the problem, it just makes the functionality worse and worse.

@AnnaShaleva
Copy link
Member

By the way, I think this PR will solve this golang-C# inconsistency indeed if golang follows. #2907 (comment)

First of all, I'll port this MaxConflictingSigners constraint to our node ASAP.

And actually, this problem with getrawtransaction is a bit deeper than it seems, because getrawtransaction itself must not be bothered by any conflicting staff. These conflict records just presented as "dummy" transactions inside the native Ledger and expected to be unretrievable by getrawtransaction. And we have this code:

private Transaction GetTransactionForContract(ApplicationEngine engine, UInt256 hash)
{
TransactionState state = GetTransactionState(engine.Snapshot, hash);
if (state is null || !IsTraceableBlock(engine.Snapshot, state.BlockIndex, engine.ProtocolSettings.MaxTraceableBlocks)) return null;
return state.Transaction;
}

Which uses GetTransactionState that retrieves all (both proper and dummy) transactions:
public TransactionState GetTransactionState(DataCache snapshot, UInt256 hash)
{
var state = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable<TransactionState>();
if (state?.Transaction is null) return null;
return state;
}

And likely throws the exception on deserialization (right before the Transaction field check).

@AnnaShaleva
Copy link
Member

Adding tighter restrictions doesn't completely solve the problem, it just makes the functionality worse and worse.

@vang1ong7ang, why do you think so?

@vang1ong7ang
Copy link
Contributor

Adding tighter restrictions doesn't completely solve the problem, it just makes the functionality worse and worse.

@vang1ong7ang, why do you think so?

max attr per tx / max signers per tx / max conflict per tx / ... are not something critical, reducing them only increase a little bit the cost of the hacker.

@shargon shargon changed the title Set max conflicts signers Set max persisted conflicts signers Sep 15, 2023
@shargon
Copy link
Member Author

shargon commented Sep 15, 2023

Ready to merge @roman-khimov @Liaojinghui could you review it?
We will open a new one for fix the memory pool issue

@shargon shargon requested a review from AnnaShaleva September 15, 2023 16:04
Copy link
Member

@vncoelho vncoelho left a comment

Choose a reason for hiding this comment

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

Let's wait for the other PR, test both together and merge both of them solving the issue.

On Monday I can try to prepare a temporary network similar to testnet that we can all test.

@dusmart
Copy link

dusmart commented Sep 15, 2023

Ready to merge @roman-khimov @Liaojinghui could you review it?
We will open a new one for fix the memory pool issue

To be responsible, only collecting approvals is not enough for the merge. Build your 7-node private testnet and follow the instructions at #2907, only if you can prove to others that it can not break the testnet then is the merge deemed good.

I think that’s why Vang provides the details. It’s for good purposes like this.

Ignore me if you developers have your own ways. These words comes because I feel unfair for Vang when you want him to build a private environment while you won’t.

AnnaShaleva added a commit to AnnaShaleva/neo that referenced this pull request Sep 17, 2023
…ict record

Instead of restricting the maximum number of conflicting signers per conflict
record we can restrict the number of transactions that make their contribution
to this conflict record. The constraint nature is the same, it doesn't allow to
spam the chain with a large set of transactions with the same Conflicts attribute.
However, this approach has several benefits over restricting the number of
signers for these conflicting transactions:
1. It significantly simplifies and accelerates a newly-pooled transactions verification.
   We don't need to perform this `Concat(signers).Distinct().Count()` operation anymore
   on new transaction addition.
2. It has a space for improvement which is not presented in this commit, but have to be
   implemented if the current solution will be accepted by the core developers. A space
   for improvement is the following: during Conflicts attribute verification we don't
   actually need to retrieve the whole list of conflicting signers for the conflict
   record. What we need instead is to check the `ConflictingTransactionsCount` field only.
   Thus, we may safely omit the `ConflictingSigners` deserialisation from stack item.
3. It allows to easily port the same constraint to the mempool (see the further commit)
   which, in turn, roughly restrict the overall possible number of transactions per
   conflict record in Ledger contract up to 2*MaxAllowedConflictingTransactions. It means,
   that the maximum number of signers per conflict record in the worst case equals to
   2*MaxAllowedConflictingTransactions*(MaxTransactionAttributes-1)=480.
At the same time, the overhead the current commit brings in the `TransactionState` is
quite insignificant (it's only a single extra Integer field).

Note 1: this PR changes the native Ledger scheme, thus, requires the DB resync on
upgrade. States will differs from the current 3.6.0 T5.

Note 2: this PR (as far as neo-project#2908) doesn't help with those transactions that already
were included into blocks 2690038-2690040 of the current T5 due to the
neo-project#2907 (comment). We need to have a
separate discussion on these "malicious" blocks and decide how to handle them.
However, this PR doesn't ever prevent the node from the proper processing of these
blocks on resync, the blocks will be processed successfully (with HALT state, the same
as they were processed by the 3.6.0 release).

Signed-off-by: Anna Shaleva <[email protected]>
AnnaShaleva added a commit to AnnaShaleva/neo that referenced this pull request Sep 17, 2023
Instead of restricting the maximum number of conflicting signers per conflict
record we can restrict the number of transactions that make their contribution
to this conflict record. The constraint nature is the same, it doesn't allow to
spam the chain with a large set of transactions with the same Conflicts attribute.
However, this approach has several benefits over restricting the number of
signers for these conflicting transactions:
1. It significantly simplifies and accelerates a newly-pooled transactions verification.
   We don't need to perform this `Concat(signers).Distinct().Count()` operation anymore
   on new transaction addition.
2. It has a space for improvement which is not presented in this commit, but have to be
   implemented if the current solution will be accepted by the core developers. A space
   for improvement is the following: during Conflicts attribute verification we don't
   actually need to retrieve the whole list of conflicting signers for the conflict
   record. What we need instead is to check the `ConflictingTransactionsCount` field only.
   Thus, we may safely omit the `ConflictingSigners` deserialisation from stack item.
3. It allows to easily port the same constraint to the mempool (see the further commit)
   which, in turn, roughly restrict the overall possible number of transactions per
   conflict record in Ledger contract up to 2*MaxAllowedConflictingTransactions. It means,
   that the maximum number of signers per conflict record in the worst case equals to
   2*MaxAllowedConflictingTransactions*(MaxTransactionAttributes-1)=480.
At the same time, the overhead the current commit brings in the `TransactionState` is
quite insignificant (it's only a single extra Integer field).

Note 1: this PR changes the native Ledger scheme, thus, requires the DB resync on
upgrade. States will differ from the current 3.6.0 T5.

Note 2: this PR (as far as neo-project#2908) doesn't help with those transactions that already
were included into blocks 2690038-2690040 of the current T5 due to the
neo-project#2907 (comment). We need to have a
separate discussion on these "malicious" blocks and decide how to handle them.
However, this PR doesn't ever prevent the node from the proper processing of these
blocks on resync, the blocks will be processed successfully (with HALT state, the same
as they were processed by the 3.6.0 release).

Signed-off-by: Anna Shaleva <[email protected]>
@AnnaShaleva
Copy link
Member

I've created the second part of this PR, please, check out the #2911. See also #2907 (comment) comment.

…lict record (#2911)

* Revert "Sort and limit size"

This reverts commit 2a69047.

As it was said in #2909 (comment),
we can't save only a part of on-chained conflicting signers. Instead, we
need to construct new blocks in such way so that there were no
conflicting signers limit overflow. It's not hard to implement, see the
further commits.

Signed-off-by: Anna Shaleva <[email protected]>

* Ledger: restrict max number of conflicting txs per conflict record

Instead of restricting the maximum number of conflicting signers per conflict
record we can restrict the number of transactions that make their contribution
to this conflict record. The constraint nature is the same, it doesn't allow to
spam the chain with a large set of transactions with the same Conflicts attribute.
However, this approach has several benefits over restricting the number of
signers for these conflicting transactions:
1. It significantly simplifies and accelerates a newly-pooled transactions verification.
   We don't need to perform this `Concat(signers).Distinct().Count()` operation anymore
   on new transaction addition.
2. It has a space for improvement which is not presented in this commit, but have to be
   implemented if the current solution will be accepted by the core developers. A space
   for improvement is the following: during Conflicts attribute verification we don't
   actually need to retrieve the whole list of conflicting signers for the conflict
   record. What we need instead is to check the `ConflictingTransactionsCount` field only.
   Thus, we may safely omit the `ConflictingSigners` deserialisation from stack item.
3. It allows to easily port the same constraint to the mempool (see the further commit)
   which, in turn, roughly restrict the overall possible number of transactions per
   conflict record in Ledger contract up to 2*MaxAllowedConflictingTransactions. It means,
   that the maximum number of signers per conflict record in the worst case equals to
   2*MaxAllowedConflictingTransactions*(MaxTransactionAttributes-1)=480.
At the same time, the overhead the current commit brings in the `TransactionState` is
quite insignificant (it's only a single extra Integer field).

Note 1: this PR changes the native Ledger scheme, thus, requires the DB resync on
upgrade. States will differ from the current 3.6.0 T5.

Note 2: this PR (as far as #2908) doesn't help with those transactions that already
were included into blocks 2690038-2690040 of the current T5 due to the
#2907 (comment). We need to have a
separate discussion on these "malicious" blocks and decide how to handle them.
However, this PR doesn't ever prevent the node from the proper processing of these
blocks on resync, the blocks will be processed successfully (with HALT state, the same
as they were processed by the 3.6.0 release).

Signed-off-by: Anna Shaleva <[email protected]>

* MemoryPool: restrict max number of conflicting txs per conflict record

Port the Ledger conflicting tx restriction logic to the MemoryPool: the maximum
number of mempooled transactions that has the same Conflicts attribute (i.e.
those that conflict with the same transaction) is restricted by the
LedgerContract.MaxAllowedConflictingTransactions constant. This restriction,
combined with the existing Ledger's storage restriction, roughly restricts the
overall possible number of transactions per conflict record in Ledger contract
up to 2*MaxAllowedConflictingTransactions. It means, that the maximum number
of signers per conflict record in the worst case equals to
2*MaxAllowedConflictingTransactions*(MaxTransactionAttributes-1)=480.

Signed-off-by: Anna Shaleva <[email protected]>

---------

Signed-off-by: Anna Shaleva <[email protected]>
@shargon
Copy link
Member Author

shargon commented Sep 19, 2023

Closed in favor of #2913

@shargon shargon closed this Sep 19, 2023
@shargon shargon deleted the max-conflicts branch September 19, 2023 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants