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

Fix Oracle PostPersist #2074

Merged
merged 24 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/neo/Ledger/TransactionVerificationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,48 @@ public class TransactionVerificationContext
/// </summary>
private readonly Dictionary<UInt160, BigInteger> senderFee = new Dictionary<UInt160, BigInteger>();

/// <summary>
/// Store oracle requests
/// </summary>
private readonly Dictionary<ulong, UInt256> oracleRequests = new Dictionary<ulong, UInt256>();
erikzhang marked this conversation as resolved.
Show resolved Hide resolved

public void AddTransaction(Transaction tx)
Copy link
Member Author

@shargon shargon Nov 26, 2020

Choose a reason for hiding this comment

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

Why don't remove CheckTransaction and Change Add to bool TryAdd (thread-safe)?

Copy link
Member

Choose a reason for hiding this comment

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

Because CheckTransaction is called in Transaction.Verify().

{
if (senderFee.TryGetValue(tx.Sender, out var value))
senderFee[tx.Sender] = value + tx.SystemFee + tx.NetworkFee;
else
senderFee.Add(tx.Sender, tx.SystemFee + tx.NetworkFee);

var oracle = tx.GetAttribute<OracleResponse>();
if (oracle != null) oracleRequests.TryAdd(oracle.Id, tx.Hash);
}

public bool CheckTransaction(Transaction tx, StoreView snapshot)
{
BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, tx.Sender);
senderFee.TryGetValue(tx.Sender, out var totalSenderFeeFromPool);
BigInteger fee = tx.SystemFee + tx.NetworkFee + totalSenderFeeFromPool;
return balance >= fee;
if (balance < fee) return false;

var oracle = tx.GetAttribute<OracleResponse>();
if (oracle != null &&
(!oracleRequests.TryGetValue(oracle.Id, out var hash) || hash != tx.Hash))
{
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

BTW, this can be replaced with generic "Conflicts" mechanism (#1991) that would also allow for proper transaction to have higher priority over fallback.

}

return true;
}

public void RemoveTransaction(Transaction tx)
{
if ((senderFee[tx.Sender] -= tx.SystemFee + tx.NetworkFee) == 0) senderFee.Remove(tx.Sender);

var oracle = tx.GetAttribute<OracleResponse>();
if (oracle != null && oracleRequests.TryGetValue(oracle.Id, out var hash) && hash == tx.Hash)
{
oracleRequests.Remove(oracle.Id);
}
}
}
}
10 changes: 7 additions & 3 deletions src/neo/SmartContract/Native/Oracle/OracleContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,23 @@ protected override void PostPersist(ApplicationEngine engine)
OracleResponse response = tx.GetAttribute<OracleResponse>();
if (response is null) continue;

// Check tx state
if (engine.Snapshot.Transactions.TryGet(tx.Hash)?.VMState != VMState.HALT) continue;
shargon marked this conversation as resolved.
Show resolved Hide resolved
shargon marked this conversation as resolved.
Show resolved Hide resolved

//Remove the request from storage
StorageKey key = CreateStorageKey(Prefix_Request).Add(response.Id);
OracleRequest request = engine.Snapshot.Storages[key].GetInteroperable<OracleRequest>();
OracleRequest request = engine.Snapshot.Storages.TryGet(key)?.GetInteroperable<OracleRequest>();
if (request == null) continue;
engine.Snapshot.Storages.Delete(key);

//Remove the id from IdList
key = CreateStorageKey(Prefix_IdList).Add(GetUrlHash(request.Url));
IdList list = engine.Snapshot.Storages.GetAndChange(key).GetInteroperable<IdList>();
if (!list.Remove(response.Id)) throw new InvalidOperationException();
if (!list.Remove(response.Id)) continue;
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
if (list.Count == 0) engine.Snapshot.Storages.Delete(key);

//Mint GAS for oracle nodes
nodes ??= NativeContract.Designate.GetDesignatedByRole(engine.Snapshot, Role.Oracle, engine.Snapshot.PersistingBlock.Index).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray();
nodes ??= Designate.GetDesignatedByRole(engine.Snapshot, Role.Oracle, engine.Snapshot.PersistingBlock.Index).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray();
if (nodes.Length > 0)
{
int index = (int)(response.Id % (ulong)nodes.Length);
Expand Down