diff --git a/neo b/neo index befe7f64d..0024d575e 160000 --- a/neo +++ b/neo @@ -1 +1 @@ -Subproject commit befe7f64d2ef7fa8abfb02864f63bc0bcac07875 +Subproject commit 0024d575e300ee3c72023fd11a6e624684060f3b diff --git a/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs b/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs index d3e7d0ad4..764d029cb 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/Expression/Expression.cs @@ -46,11 +46,11 @@ private void ConvertExpression(SemanticModel model, ExpressionSyntax syntax, Syn private bool TryConvertConstant(SemanticModel model, ExpressionSyntax syntax, SyntaxNode? syntaxNode) { - Optional constant = model.GetConstantValue(syntax); - if (!constant.HasValue) + var constant = model.GetConstantValue(syntax); + var value = constant.Value; + if (value == null) return false; - var value = constant.Value; ITypeSymbol? typeSymbol = GetTypeSymbol(syntaxNode, model); if (typeSymbol != null) diff --git a/src/Neo.Compiler.CSharp/MethodConvert/Helpers/SlotHelpers.cs b/src/Neo.Compiler.CSharp/MethodConvert/Helpers/SlotHelpers.cs index 5f6cf5488..afd64b7e9 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/Helpers/SlotHelpers.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/Helpers/SlotHelpers.cs @@ -308,10 +308,10 @@ private void ProcessOutArgument(SemanticModel model, IMethodSymbol methodSymbol, catch { // check if the argument is a discard - var argument = arguments[parameter.Ordinal] as ArgumentSyntax; - if (argument.Expression is not IdentifierNameSyntax { Identifier.ValueText: "_" }) - throw new CompilationException(arguments[parameter.Ordinal], DiagnosticId.SyntaxNotSupported, - $"In method {Symbol.Name}, unsupported out argument: {arguments[parameter.Ordinal]}"); + var argument = arguments[parameter.Ordinal]; + if (argument is not ArgumentSyntax syntax || syntax.Expression is not IdentifierNameSyntax { Identifier.ValueText: "_" }) + throw new CompilationException(argument, DiagnosticId.SyntaxNotSupported, + $"In method {Symbol.Name}, unsupported out argument: {argument}"); LdArgSlot(parameter); } } diff --git a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs index e36b3c5aa..f5b01bd3e 100644 --- a/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs +++ b/src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.cs @@ -93,8 +93,8 @@ private static string GetKeyFromExpression(LambdaExpression expression, params T return expression.Body switch { MethodCallExpression methodCall => GetMethodCallKey(methodCall, argumentTypes), - MemberExpression { Member: PropertyInfo property } => $"{GetShortTypeName(property.DeclaringType)}.{property.Name}.get", - MemberExpression { Member: FieldInfo field } => $"{GetShortTypeName(field.DeclaringType)}.{field.Name}", + MemberExpression { Member: PropertyInfo property } => $"{GetShortTypeName(property.DeclaringType!)}.{property.Name}.get", + MemberExpression { Member: FieldInfo field } => $"{GetShortTypeName(field.DeclaringType!)}.{field.Name}", UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression => GetUnaryExpressionKey(unaryExpression), IndexExpression indexExpression => GetIndexExpressionKey(indexExpression), _ => throw new ArgumentException("Expression must be a method call, property, field access, or special member.", nameof(expression)), @@ -104,6 +104,7 @@ private static string GetKeyFromExpression(LambdaExpression expression, params T private static string GetMethodCallKey(MethodCallExpression methodCall, Type[] argumentTypes) { var method = methodCall.Method; + var declaringType = method.DeclaringType!; // Static method if (methodCall.Object == null) return GetMethodKey(method, argumentTypes); @@ -114,12 +115,12 @@ private static string GetMethodCallKey(MethodCallExpression methodCall, Type[] a if (method.IsSpecialName && (methodName.StartsWith("get_Char") || methodName.StartsWith("set_Char"))) { var accessorType = methodName.StartsWith("get_Char") ? "get" : "set"; - return $"{GetShortTypeName(method.DeclaringType)}.this[{parameters}].{accessorType}"; + return $"{GetShortTypeName(declaringType)}.this[{parameters}].{accessorType}"; } if (method.IsGenericMethod) { - var containingType = GetShortTypeName(method.DeclaringType); + var containingType = GetShortTypeName(declaringType); var genericArguments = $"<{string.Join(", ", method.GetGenericArguments().Select(GetShortTypeName))}>"; return $"{containingType}.{methodName}{genericArguments}({parameters})"; } @@ -131,7 +132,7 @@ private static string GetUnaryExpressionKey(UnaryExpression unaryExpression) { var operandType = GetShortTypeName(unaryExpression.Operand.Type); var targetType = GetShortTypeName(unaryExpression.Type); - return unaryExpression.Method.Name == "op_Implicit" + return unaryExpression.Method!.Name == "op_Implicit" ? $"{targetType}.implicit operator {targetType}({operandType})" : $"{operandType}.explicit operator {targetType}({operandType})"; } @@ -139,12 +140,13 @@ private static string GetUnaryExpressionKey(UnaryExpression unaryExpression) private static string GetIndexExpressionKey(IndexExpression indexExpression) { var indexParams = string.Join(", ", indexExpression.Arguments.Select(arg => GetShortTypeName(arg.Type))); - return $"{GetShortTypeName(indexExpression.Object.Type)}.this[{indexParams}].get"; + return $"{GetShortTypeName(indexExpression.Object!.Type)}.this[{indexParams}].get"; } private static string GetMethodKey(MethodInfo method, Type[] argumentTypes) { - var containingType = GetShortTypeName(method.DeclaringType); + var declaringType = method.DeclaringType!; + var containingType = GetShortTypeName(declaringType); var parameters = string.Join(", ", argumentTypes.Select(GetShortTypeName)); switch (method.IsSpecialName) @@ -152,7 +154,7 @@ private static string GetMethodKey(MethodInfo method, Type[] argumentTypes) case true when method.Name.StartsWith("get_Char") || method.Name.StartsWith("set_Char"): { var accessorType = method.Name.StartsWith("get_Char") ? "get" : "set"; - return $"{GetShortTypeName(method.DeclaringType)}.this[{parameters}].{accessorType}"; + return $"{containingType}.this[{parameters}].{accessorType}"; } case true when method.Name.StartsWith("op_"): { @@ -179,7 +181,7 @@ private static string GetShortTypeName(Type type) { if (type.IsArray) { - return GetShortTypeName(type.GetElementType()) + "[]"; + return GetShortTypeName(type.GetElementType()!) + "[]"; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) diff --git a/src/Neo.SmartContract.Analyzer/InitialValueAnalyzer.cs b/src/Neo.SmartContract.Analyzer/InitialValueAnalyzer.cs index 4bfae36f2..325f8a32c 100644 --- a/src/Neo.SmartContract.Analyzer/InitialValueAnalyzer.cs +++ b/src/Neo.SmartContract.Analyzer/InitialValueAnalyzer.cs @@ -92,10 +92,10 @@ public class InitialValueCodeFixProvider : CodeFixProvider public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false)!; var diagnostic = context.Diagnostics.First(); var diagnosticSpan = diagnostic.Location.SourceSpan; - var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType().First(); + var declaration = root!.FindToken(diagnosticSpan.Start).Parent!.AncestorsAndSelf().OfType().First(); context.RegisterCodeFix( CodeAction.Create( @@ -120,7 +120,7 @@ private async Task ConvertToLiteralInitializationAsync(Document docume var newInitializer = SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression(argument)); var newField = fieldDeclaration - .RemoveNodes(fieldDeclaration.AttributeLists, SyntaxRemoveOptions.KeepNoTrivia) + .RemoveNodes(fieldDeclaration.AttributeLists, SyntaxRemoveOptions.KeepNoTrivia)! .WithDeclaration(fieldDeclaration.Declaration.WithVariables( SyntaxFactory.SingletonSeparatedList( fieldDeclaration.Declaration.Variables[0].WithInitializer(newInitializer)))); diff --git a/src/Neo.SmartContract.Testing/Coverage/Formats/CoberturaFormat.cs b/src/Neo.SmartContract.Testing/Coverage/Formats/CoberturaFormat.cs index d85a04d0d..7fa44277b 100644 --- a/src/Neo.SmartContract.Testing/Coverage/Formats/CoberturaFormat.cs +++ b/src/Neo.SmartContract.Testing/Coverage/Formats/CoberturaFormat.cs @@ -74,7 +74,7 @@ internal static void WriteReport(XmlWriter writer, IReadOnlyList<(CoveredContrac writer.WriteAttributeString("branch-rate", $"{branchRate:N4}"); writer.WriteAttributeString("branches-covered", $"{branchesCovered}"); writer.WriteAttributeString("branches-valid", $"{branchesValid}"); - writer.WriteAttributeString("version", typeof(CoberturaFormat).Assembly.GetName().Version.ToString()); + writer.WriteAttributeString("version", typeof(CoberturaFormat).Assembly.GetName().Version?.ToString() ?? "Unknown Version"); writer.WriteAttributeString("timestamp", $"{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}"); writer.WriteStartElement("sources"); diff --git a/src/Neo.SmartContract.Testing/PersistingBlock.cs b/src/Neo.SmartContract.Testing/PersistingBlock.cs index f29bc4f7b..fc58d21ef 100644 --- a/src/Neo.SmartContract.Testing/PersistingBlock.cs +++ b/src/Neo.SmartContract.Testing/PersistingBlock.cs @@ -165,7 +165,10 @@ public Block Persist(Transaction[] txs, VMState[] states) for (int x = 0; x < txs.Length; x++) { - var transactionState = clonedSnapshot.TryGet(new KeyBuilder(_engine.Native.Ledger.Storage.Id, prefix_Transaction).Add(txs[x].Hash)); + var key = new KeyBuilder(_engine.Native.Ledger.Storage.Id, prefix_Transaction).Add(txs[x].Hash); + var transactionState = clonedSnapshot.TryGet(key); + if (transactionState is null) + throw new Exception($"Transaction state not found: {txs[x].Hash}"); transactionState.GetInteroperable().State = states[x]; } diff --git a/src/Neo.SmartContract.Testing/Storage/Rpc/RpcSnapshot.cs b/src/Neo.SmartContract.Testing/Storage/Rpc/RpcSnapshot.cs index d7d9defe6..9e68b3755 100644 --- a/src/Neo.SmartContract.Testing/Storage/Rpc/RpcSnapshot.cs +++ b/src/Neo.SmartContract.Testing/Storage/Rpc/RpcSnapshot.cs @@ -12,6 +12,7 @@ using Neo.Persistence; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Neo.SmartContract.Testing.Storage.Rpc; @@ -56,12 +57,12 @@ public void Put(byte[] key, byte[] value) IsDirty = true; } - public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) + public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[]? keyOrPrefix, SeekDirection direction = SeekDirection.Forward) { return Store.Seek(keyOrPrefix, direction); } - public bool TryGet(byte[] key, out byte[]? value) + public bool TryGet(byte[] key, [NotNullWhen(true)] out byte[]? value) { return Store.TryGet(key, out value); } diff --git a/src/Neo.SmartContract.Testing/Storage/Rpc/RpcStore.cs b/src/Neo.SmartContract.Testing/Storage/Rpc/RpcStore.cs index 34ea3984b..6afefdc6e 100644 --- a/src/Neo.SmartContract.Testing/Storage/Rpc/RpcStore.cs +++ b/src/Neo.SmartContract.Testing/Storage/Rpc/RpcStore.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net.Http; using System.Text; @@ -55,18 +56,24 @@ public void Dispose() { } #region Rpc calls - public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] key, SeekDirection direction) + public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[]? key, SeekDirection direction) { + // This(IStore.Seek) is different from LevelDbStore, RocksDbStore and MemoryStore. + if (key is null) + throw new ArgumentNullException(nameof(key)); + + // This(IStore.Seek) is different from LevelDbStore, RocksDbStore and MemoryStore. + // The following logic has this requirement. + if (key.Length < 4) + throw new ArgumentException("Key must be at least 4 bytes(the first 4 bytes are the contract id)", nameof(key)); + if (direction is SeekDirection.Backward) { // Not implemented in RPC, we will query all the storage from the contract, and do it manually // it could return wrong results if we want to get data between contracts - - var prefix = key.Take(4).ToArray(); ConcurrentDictionary data = new(); // We ask for 5 bytes because the minimum prefix is one byte - foreach (var entry in Seek(key.Take(key.Length == 4 ? 4 : 5).ToArray(), SeekDirection.Forward)) { data.TryAdd(entry.Key, entry.Value); @@ -142,7 +149,7 @@ public void Dispose() { } throw new Exception(); } - public bool TryGet(byte[] key, out byte[]? value) + public bool TryGet(byte[] key, [NotNullWhen(true)] out byte[]? value) { var skey = new StorageKey(key); var requestBody = new diff --git a/tests/Neo.Compiler.CSharp.TestContracts/Contract_SecurityAnalyzer/Contract_CheckWitness.cs b/tests/Neo.Compiler.CSharp.TestContracts/Contract_SecurityAnalyzer/Contract_CheckWitness.cs index 4da8c9e25..bfec23800 100644 --- a/tests/Neo.Compiler.CSharp.TestContracts/Contract_SecurityAnalyzer/Contract_CheckWitness.cs +++ b/tests/Neo.Compiler.CSharp.TestContracts/Contract_SecurityAnalyzer/Contract_CheckWitness.cs @@ -16,7 +16,7 @@ namespace Neo.Compiler.CSharp.TestContracts { public class Contract_CheckWitness : SmartContract.Framework.SmartContract { - public static void Main(UInt160 u) + public static void CheckWitnessAnalysis(UInt160 u) { Runtime.CheckWitness(u); ExecutionEngine.Assert(Runtime.CheckWitness(u)); diff --git a/tests/Neo.Compiler.CSharp.UnitTests/SecurityAnalyzer/UnitTest_CheckWitness.cs b/tests/Neo.Compiler.CSharp.UnitTests/SecurityAnalyzer/UnitTest_CheckWitness.cs index 389e327ce..6da1653b5 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/SecurityAnalyzer/UnitTest_CheckWitness.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/SecurityAnalyzer/UnitTest_CheckWitness.cs @@ -21,7 +21,7 @@ public class CheckWitnessTests : DebugAndTestBase [TestMethod] public void Test_CheckWitness() { - CheckWitnessAnalyzer.CheckWitnessVulnerability result = CheckWitnessAnalyzer.AnalyzeCheckWitness(NefFile, Manifest, null); + var result = CheckWitnessAnalyzer.AnalyzeCheckWitness(NefFile, Manifest, null); Assert.AreEqual(result.droppedCheckWitnessResults.Count, 1); } } diff --git a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_CheckWitness.cs b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_CheckWitness.cs index d71060482..c892f9dae 100644 --- a/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_CheckWitness.cs +++ b/tests/Neo.Compiler.CSharp.UnitTests/TestingArtifacts/Contract_CheckWitness.cs @@ -11,7 +11,7 @@ public abstract class Contract_CheckWitness(Neo.SmartContract.Testing.SmartContr { #region Compiled data - public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_CheckWitness"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""main"",""parameters"":[{""name"":""u"",""type"":""Hash160""}],""returntype"":""Void"",""offset"":0,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); + public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_CheckWitness"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""checkWitnessAnalysis"",""parameters"":[{""name"":""u"",""type"":""Hash160""}],""returntype"":""Void"",""offset"":0,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}"); /// /// Optimization: "All" @@ -36,8 +36,8 @@ public abstract class Contract_CheckWitness(Neo.SmartContract.Testing.SmartContr /// ASSERT [1 datoshi] /// RET [0 datoshi] /// - [DisplayName("main")] - public abstract void Main(UInt160? u); + [DisplayName("checkWitnessAnalysis")] + public abstract void CheckWitnessAnalysis(UInt160? u); #endregion }