diff --git a/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs b/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs index df358fdc4..cac84a8d4 100644 --- a/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs +++ b/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs @@ -127,13 +127,26 @@ private void OnPrepareRequestReceived(ExtensiblePayload payload, PrepareRequest { if (mempoolVerified.TryGetValue(hash, out Transaction tx)) { + if (NativeContract.Ledger.ContainsConflictHash(context.Snapshot, hash, tx.Signers.Select(s => s.Account))) + { + Log($"Invalid request: transaction has on-chain conflict", LogLevel.Warning); + return; + } + if (!AddTransaction(tx, false)) return; } else { if (neoSystem.MemPool.TryGetValue(hash, out tx)) + { + if (NativeContract.Ledger.ContainsConflictHash(context.Snapshot, hash, tx.Signers.Select(s => s.Account))) + { + Log($"Invalid request: transaction has on-chain conflict", LogLevel.Warning); + return; + } unverified.Add(tx); + } } } foreach (Transaction tx in unverified) diff --git a/src/DBFTPlugin/Consensus/ConsensusService.cs b/src/DBFTPlugin/Consensus/ConsensusService.cs index e50ab42d2..dc8f66ff1 100644 --- a/src/DBFTPlugin/Consensus/ConsensusService.cs +++ b/src/DBFTPlugin/Consensus/ConsensusService.cs @@ -257,7 +257,38 @@ private bool AddTransaction(Transaction tx, bool verify) { if (verify) { - VerifyResult result = tx.Verify(neoSystem.Settings, context.Snapshot, context.VerificationContext); + // At this step we're sure that there's no on-chain transaction that conflicts with + // the provided tx because of the previous Blockchain's OnReceive check. Thus, we only + // need to check that current context doesn't contain conflicting transactions. + VerifyResult result; + + // Firstly, check whether tx has Conlicts attribute with the hash of one of the context's transactions. + foreach (var h in tx.GetAttributes().Select(attr => attr.Hash)) + { + if (context.TransactionHashes.Contains(h)) + { + result = VerifyResult.HasConflicts; + Log($"Rejected tx: {tx.Hash}, {result}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); + RequestChangeView(ChangeViewReason.TxInvalid); + return false; + } + } + // After that, check whether context's transactions have Conflicts attribute with tx's hash. + foreach (var pooledTx in context.Transactions.Values) + { + if (pooledTx.GetAttributes().Select(attr => attr.Hash).Contains(tx.Hash)) + { + result = VerifyResult.HasConflicts; + Log($"Rejected tx: {tx.Hash}, {result}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); + RequestChangeView(ChangeViewReason.TxInvalid); + return false; + } + } + + // We've ensured that there's no conlicting transactions in the context, thus, can safely provide an empty conflicting list + // for futher verification. + var conflictingTxs = new List(); + result = tx.Verify(neoSystem.Settings, context.Snapshot, context.VerificationContext, conflictingTxs); if (result != VerifyResult.Succeed) { Log($"Rejected tx: {tx.Hash}, {result}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);