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

[Neo Core Fix] Check params at Calling Contract #3438

Open
wants to merge 9 commits into
base: HF_Echidna
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 5 additions & 8 deletions src/Neo/SmartContract/ApplicationEngine.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ protected internal Signer[] GetCurrentSigners()
private static bool CheckItemType(StackItem item, ContractParameterType type)
{
StackItemType aType = item.Type;
if (aType == StackItemType.Any) return true;
Copy link
Member

Choose a reason for hiding this comment

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

I don't think it's correct for VM-compatible SC types, because for example, if you require stackitem to be Boolean, then Any just doesn't match this requirement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see what you mean, will update.

if (aType == StackItemType.Pointer) return false;
switch (type)
{
Expand All @@ -459,7 +460,7 @@ private static bool CheckItemType(StackItem item, ContractParameterType type)
case ContractParameterType.Integer:
return aType == StackItemType.Integer;
case ContractParameterType.ByteArray:
return aType is StackItemType.Any or StackItemType.ByteString or StackItemType.Buffer;
return aType is StackItemType.ByteString or StackItemType.Buffer;
case ContractParameterType.String:
{
if (aType is StackItemType.ByteString or StackItemType.Buffer)
Expand All @@ -474,27 +475,23 @@ private static bool CheckItemType(StackItem item, ContractParameterType type)
return false;
}
case ContractParameterType.Hash160:
if (aType == StackItemType.Any) return true;
if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false;
return item.GetSpan().Length == UInt160.Length;
case ContractParameterType.Hash256:
if (aType == StackItemType.Any) return true;
if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false;
return item.GetSpan().Length == UInt256.Length;
case ContractParameterType.PublicKey:
if (aType == StackItemType.Any) return true;
if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false;
return item.GetSpan().Length == 33;
case ContractParameterType.Signature:
if (aType == StackItemType.Any) return true;
if (aType != StackItemType.ByteString && aType != StackItemType.Buffer) return false;
return item.GetSpan().Length == 64;
case ContractParameterType.Array:
return aType is StackItemType.Any or StackItemType.Array or StackItemType.Struct;
return aType is StackItemType.Array or StackItemType.Struct;
case ContractParameterType.Map:
return aType is StackItemType.Any or StackItemType.Map;
return aType is StackItemType.Map;
case ContractParameterType.InteropInterface:
return aType is StackItemType.Any or StackItemType.InteropInterface;
return aType is StackItemType.InteropInterface;
default:
return false;
}
Expand Down
4 changes: 4 additions & 0 deletions src/Neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,11 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe
state.CallingContext = currentContext;

for (int i = args.Count - 1; i >= 0; i--)
{
if (!CheckItemType(args[i], method.Parameters[i].Type))
throw new InvalidOperationException($"The type of the argument `{args[i]}` does not match the formal parameter.");
Copy link
Member

@AnnaShaleva AnnaShaleva Aug 7, 2024

Choose a reason for hiding this comment

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

It may be too harsh for mainnet contracts (even if enabled with hardfrok), this change may break some of them and even prevent them from update. What do you think about using the same approach as it was done for SC Events parameters check (#2810 (comment)): in next release we firstly enable a warning log in case of parameters mismatch, and then in the subsequent release we convert the log to an exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It may be too harsh for mainnet contracts (even if enabled with hardfrok), this change may break some of them and even prevent them from update. What do you think about using the same approach as it was done for SC Events parameters check (#2810 (comment)): in next release we firstly enable a warning log in case of parameters mismatch, and then in the subsequent release we convert the log to an exception.

We have discussed this in the last meeting, check will only happen for newly deployed or updated contracts, this pr will be updated.

context_new.EvaluationStack.Push(args[i]);
}

return context_new;
}
Expand Down
5 changes: 3 additions & 2 deletions tests/Neo.UnitTests/SmartContract/Native/UT_GasToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using System;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Threading.Tasks;

namespace Neo.UnitTests.SmartContract.Native
Expand Down Expand Up @@ -131,8 +132,8 @@ await Assert.ThrowsExceptionAsync<InvalidOperationException>(async () =>
// Bad inputs

Assert.ThrowsException<ArgumentOutOfRangeException>(() => NativeContract.GAS.Transfer(engine.Snapshot, from, to, BigInteger.MinusOne, true, persistingBlock));
Assert.ThrowsException<FormatException>(() => NativeContract.GAS.Transfer(engine.Snapshot, new byte[19], to, BigInteger.One, false, persistingBlock));
Assert.ThrowsException<FormatException>(() => NativeContract.GAS.Transfer(engine.Snapshot, from, new byte[19], BigInteger.One, false, persistingBlock));
Assert.ThrowsException<TargetInvocationException>(() => NativeContract.GAS.Transfer(engine.Snapshot, new byte[19], to, BigInteger.One, false, persistingBlock));
Assert.ThrowsException<TargetInvocationException>(() => NativeContract.GAS.Transfer(engine.Snapshot, from, new byte[19], BigInteger.One, false, persistingBlock));
}

internal static StorageKey CreateStorageKey(byte prefix, uint key)
Expand Down
5 changes: 3 additions & 2 deletions tests/Neo.UnitTests/SmartContract/Native/UT_NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System;
using System.Linq;
using System.Numerics;
using System.Reflection;
using static Neo.SmartContract.Native.NeoToken;

namespace Neo.UnitTests.SmartContract.Native
Expand Down Expand Up @@ -436,8 +437,8 @@ public void Check_Transfer()
// Bad inputs

Assert.ThrowsException<ArgumentOutOfRangeException>(() => NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.MinusOne, true, persistingBlock));
Assert.ThrowsException<FormatException>(() => NativeContract.NEO.Transfer(snapshot, new byte[19], to, BigInteger.One, false, persistingBlock));
Assert.ThrowsException<FormatException>(() => NativeContract.NEO.Transfer(snapshot, from, new byte[19], BigInteger.One, false, persistingBlock));
Assert.ThrowsException<TargetInvocationException>(() => NativeContract.NEO.Transfer(snapshot, new byte[19], to, BigInteger.One, false, persistingBlock));
Assert.ThrowsException<TargetInvocationException>(() => NativeContract.NEO.Transfer(snapshot, from, new byte[19], BigInteger.One, false, persistingBlock));

// More than balance

Expand Down
2 changes: 1 addition & 1 deletion tests/Neo.UnitTests/SmartContract/UT_InteropService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void Runtime_GetNotifications_Test()
scriptHash2 = script.ToArray().ToScriptHash();

snapshot.DeleteContract(scriptHash2);
var contract = TestUtils.GetContract(script.ToArray(), TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer));
var contract = TestUtils.GetContract(script.ToArray(), TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer));
contract.Manifest.Abi.Events = new[]
{
new ContractEventDescriptor
Expand Down