-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Improved scheme for Conflicts attribute storing and pricing #2913
Changes from 3 commits
d6d3d47
002e7bf
cba97f1
ffa7e8d
fff3129
330cbea
024a82a
51a51a1
be19957
25b7d1b
2350393
c6105c0
32d1341
9845609
fc455f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,13 +47,21 @@ internal override ContractTask OnPersist(ApplicationEngine engine) | |
engine.Snapshot.Add(CreateStorageKey(Prefix_Block).Add(engine.PersistingBlock.Hash), new StorageItem(Trim(engine.PersistingBlock).ToArray())); | ||
foreach (TransactionState tx in transactions) | ||
{ | ||
// Remove possible previously saved malicious conflict records for the transaction (if any). | ||
foreach (var (key, _) in engine.Snapshot.Find(CreateStorageKey(Prefix_Transaction).Add(tx.Transaction.Hash).ToArray())) | ||
engine.Snapshot.Delete(key); | ||
|
||
engine.Snapshot.Add(CreateStorageKey(Prefix_Transaction).Add(tx.Transaction.Hash), new StorageItem(tx)); | ||
|
||
// Store transaction's conflicits. | ||
var conflictingSigners = tx.Transaction.Signers.Select(s => s.Account); | ||
foreach (var attr in tx.Transaction.GetAttributes<Conflicts>()) | ||
{ | ||
var conflictRecord = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Transaction).Add(attr.Hash), | ||
() => new StorageItem(new TransactionState { ConflictingSigners = Array.Empty<UInt160>() })).GetInteroperable<TransactionState>(); | ||
conflictRecord.ConflictingSigners = conflictRecord.ConflictingSigners.Concat(conflictingSigners).Distinct().ToArray(); | ||
engine.Snapshot.Add(CreateStorageKey(Prefix_Transaction).Add(attr.Hash), new StorageItem(new TransactionState() { BlockIndex = engine.PersistingBlock.Index })); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to hack a private net by the code from #2907, when multi transactions want to cancel the same tx(the same conflict attribute), it will throw There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's my poor knowledge of NeoC# codebase, the intention here was to replace the existing conflict record (if it's in the DB), but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @superboyiii, could you, please, run the same test? I've modified the code so that now it's possible to rewrite existing malicious conflict record with the proper transaction. |
||
foreach (var signer in conflictingSigners) | ||
{ | ||
engine.Snapshot.Add(CreateStorageKey(Prefix_Transaction).Add(attr.Hash).Add(signer), new StorageItem(new TransactionState() { BlockIndex = engine.PersistingBlock.Index })); | ||
} | ||
} | ||
} | ||
engine.SetState(transactions); | ||
|
@@ -145,11 +153,24 @@ public bool ContainsTransaction(DataCache snapshot, UInt256 hash) | |
/// <param name="snapshot">The snapshot used to read data.</param> | ||
/// <param name="hash">The hash of the conflicting transaction.</param> | ||
/// <param name="signers">The list of signer accounts of the conflicting transaction.</param> | ||
/// <param name="maxTraceableBlocks">MaxTraceableBlocks protocol setting.</param> | ||
/// <returns><see langword="true"/> if the blockchain contains the hash of the conflicting transaction; otherwise, <see langword="false"/>.</returns> | ||
public bool ContainsConflictHash(DataCache snapshot, UInt256 hash, IEnumerable<UInt160> signers) | ||
public bool ContainsConflictHash(DataCache snapshot, UInt256 hash, IEnumerable<UInt160> signers, uint maxTraceableBlocks) | ||
{ | ||
var state = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable<TransactionState>(); | ||
return state is not null && state.Transaction is null && (signers is null || state.ConflictingSigners.Intersect(signers).Any()); | ||
// Check the dummy stub firstly to define whether there's exist at least one conflict record. | ||
var stub = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable<TransactionState>(); | ||
if (stub is null || stub.Transaction is not null || !IsTraceableBlock(snapshot, stub.BlockIndex, maxTraceableBlocks)) | ||
return false; | ||
|
||
// At least one conflict record is found, then need to check signers intersection. | ||
foreach (var signer in signers) | ||
{ | ||
var state = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash).Add(signer))?.GetInteroperable<TransactionState>(); | ||
if (state is not null && IsTraceableBlock(snapshot, state.BlockIndex, maxTraceableBlocks)) | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/// <summary> | ||
|
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.
The discussion started by @roman-khimov and ported from AnnaShaleva@deb1470#r127677374:
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's true and everything will work without this line. This garbage collection may be safely run in a separate routine (or as a separate task), we don't need to include it into
OnPersist
, but I'm not sure what's the right place for it.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'd say that's a job for
RemoveUntraceableBlocks
option (C# node doesn't have it, but it can be implemented). This storage space is paid for at the same time with the new setting, so keeping these entries is OK.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 that we should have a maximum allowed signers too
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.
Having maximum allowed signers will bring us to the same problem that was described in #2907 (comment). So any kind of such restriction leads to this possible attack when malicious conflicts prevent some real (and valid) conflict from entering the chain.
That's why we build this new solution in such way so that all conflicts data are properly paid. And it's even harder to spam the DB with Conflicts-related data than via contract storage.
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.
it's not necessary to care about those "dust" records and they don't really hurt.
correct attr fee introduced by @shargon is able to avoid the abuse and restrict the storage cost.
an uncontrollable O(n) procedure here can be avoided
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 agree, let's then keep these garbage records. Implemented in a separate commit, see the 024a82a.