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 SYSCALLs #1511

Merged
merged 5 commits into from
Apr 9, 2020
Merged
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
5 changes: 5 additions & 0 deletions src/neo/SmartContract/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ public static UInt160 ToScriptHash(this byte[] script)
return new UInt160(Crypto.Hash160(script));
}

public static UInt160 ToScriptHash(this ReadOnlySpan<byte> script)
{
return new UInt160(Crypto.Hash160(script));
}

internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snapshot, long gas)
{
if (gas < 0) return false;
Expand Down
13 changes: 3 additions & 10 deletions src/neo/SmartContract/InteropService.Binary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,9 @@ public static class Binary

private static bool Binary_Serialize(ApplicationEngine engine)
{
byte[] serialized;
try
{
serialized = BinarySerializer.Serialize(engine.CurrentContext.EvaluationStack.Pop(), engine.MaxItemSize);
}
catch
{
return false;
}
engine.CurrentContext.EvaluationStack.Push(serialized);
if (!engine.TryPop(out StackItem item)) return false;
byte[] serialized = BinarySerializer.Serialize(item, engine.MaxItemSize);
engine.Push(serialized);
return true;
}

Expand Down
31 changes: 17 additions & 14 deletions src/neo/SmartContract/InteropService.Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ partial class InteropService
{
public static class Contract
{
public const int MaxLength = 1024 * 1024;

public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall);
public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall);
public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All, CallFlags.None);

/// <summary>
/// Calculate corresponding account scripthash for given public key
/// Warning: check first that input public key is valid, before creating the script.
Expand All @@ -35,56 +36,58 @@ private static long GetDeploymentPrice(EvaluationStack stack, StoreView snapshot

private static bool Contract_Create(ApplicationEngine engine)
{
byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray();
if (script.Length > 1024 * 1024) return false;
if (!engine.TryPop(out ReadOnlySpan<byte> script)) return false;
if (script.Length == 0 || script.Length > MaxLength) return false;

var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString();
if (manifest.Length > ContractManifest.MaxLength) return false;
if (!engine.TryPop(out ReadOnlySpan<byte> manifest)) return false;
if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) return false;

UInt160 hash = script.ToScriptHash();
ContractState contract = engine.Snapshot.Contracts.TryGet(hash);
if (contract != null) return false;
contract = new ContractState
{
Id = engine.Snapshot.ContractId.GetAndChange().NextId++,
Script = script,
Script = script.ToArray(),
Manifest = ContractManifest.Parse(manifest)
};

if (!contract.Manifest.IsValid(hash)) return false;

engine.Snapshot.Contracts.Add(hash, contract);
engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(contract));
engine.Push(StackItem.FromInterface(contract));
return true;
}

private static bool Contract_Update(ApplicationEngine engine)
{
byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray();
if (script.Length > 1024 * 1024) return false;
var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString();
if (manifest.Length > ContractManifest.MaxLength) return false;
if (!engine.TryPop(out StackItem item0)) return false;
shargon marked this conversation as resolved.
Show resolved Hide resolved
if (!engine.TryPop(out StackItem item1)) return false;

var contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash);
if (contract is null) return false;

if (script.Length > 0)
if (!item0.IsNull)
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
{
ReadOnlySpan<byte> script = item0.GetSpan();
if (script.Length == 0 || script.Length > MaxLength) return false;
UInt160 hash_new = script.ToScriptHash();
if (hash_new.Equals(engine.CurrentScriptHash)) return false;
if (engine.Snapshot.Contracts.TryGet(hash_new) != null) return false;
contract = new ContractState
{
Id = contract.Id,
Script = script,
Script = script.ToArray(),
Manifest = contract.Manifest
};
contract.Manifest.Abi.Hash = hash_new;
engine.Snapshot.Contracts.Add(hash_new, contract);
engine.Snapshot.Contracts.Delete(engine.CurrentScriptHash);
}
if (manifest.Length > 0)
if (!item1.IsNull)
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
{
ReadOnlySpan<byte> manifest = item1.GetSpan();
if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) return false;
contract = engine.Snapshot.Contracts.GetAndChange(contract.ScriptHash);
contract.Manifest = ContractManifest.Parse(manifest);
if (!contract.Manifest.IsValid(contract.ScriptHash)) return false;
Expand Down
34 changes: 10 additions & 24 deletions src/neo/SmartContract/InteropService.Json.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Neo.IO.Json;
using Neo.VM;
using Neo.VM.Types;
using System;

namespace Neo.SmartContract
{
Expand All @@ -12,33 +13,18 @@ public static class Json

private static bool Json_Serialize(ApplicationEngine engine)
{
var item = engine.CurrentContext.EvaluationStack.Pop();
try
{
var json = JsonSerializer.SerializeToByteArray(item, engine.MaxItemSize);
engine.CurrentContext.EvaluationStack.Push(json);
return true;
}
catch
{
return false;
}
if (!engine.TryPop(out StackItem item)) return false;
byte[] json = JsonSerializer.SerializeToByteArray(item, engine.MaxItemSize);
engine.Push(json);
return true;
}

private static bool Json_Deserialize(ApplicationEngine engine)
{
var json = engine.CurrentContext.EvaluationStack.Pop().GetSpan();
try
{
var obj = JObject.Parse(json, 10);
var item = JsonSerializer.Deserialize(obj, engine.ReferenceCounter);
engine.CurrentContext.EvaluationStack.Push(item);
return true;
}
catch
{
return false;
}
if (!engine.TryPop(out ReadOnlySpan<byte> json)) return false;
StackItem item = JsonSerializer.Deserialize(JObject.Parse(json, 10), engine.ReferenceCounter);
engine.Push(item);
return true;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/neo/SmartContract/InteropService.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private static bool CheckItemForNotification(StackItem state)
}
break;
case PrimitiveType primitive:
size += primitive.GetByteLength();
size += primitive.Size;
break;
case Null _:
break;
Expand All @@ -65,7 +65,7 @@ private static bool CheckItemForNotification(StackItem state)
items_checked.Add(map);
foreach (var pair in map)
{
size += pair.Key.GetByteLength();
size += pair.Key.Size;
items_unchecked.Enqueue(pair.Value);
}
}
Expand Down
19 changes: 8 additions & 11 deletions src/neo/SmartContract/InteropService.Storage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,22 @@ public static class Storage

private static long GetStoragePrice(EvaluationStack stack, StoreView snapshot)
{
var key = stack.Peek(1);
var value = stack.Peek(2);
var newDataSize = value.IsNull ? 0 : value.GetByteLength();
if (!(stack.Peek() is InteropInterface _interface))
throw new InvalidOperationException();

StorageContext context = _interface.GetInterface<StorageContext>();
StorageContext context = ((InteropInterface)stack.Peek(0)).GetInterface<StorageContext>();
shargon marked this conversation as resolved.
Show resolved Hide resolved
ReadOnlySpan<byte> key = stack.Peek(1).GetSpan();
ReadOnlySpan<byte> value = stack.Peek(2).GetSpan();
int newDataSize;
StorageKey skey = new StorageKey
{
Id = context.Id,
Key = key.GetSpan().ToArray()
Key = key.ToArray()
};
var skeyValue = snapshot.Storages.TryGet(skey);
if (skeyValue is null)
newDataSize += key.GetByteLength();
else if (newDataSize <= skeyValue.Value.Length)
newDataSize = key.Length + value.Length;
else if (value.Length <= skeyValue.Value.Length)
newDataSize = 1;
else
newDataSize -= skeyValue.Value.Length;
newDataSize = value.Length - skeyValue.Value.Length;
return newDataSize * GasPerByte;
}

Expand Down
6 changes: 4 additions & 2 deletions src/neo/SmartContract/Manifest/ContractManifest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Neo.IO;
using Neo.IO.Json;
using System.Collections.Generic;
using System;
using System.IO;
using System.Linq;

Expand Down Expand Up @@ -117,7 +117,9 @@ public static ContractManifest FromJson(JObject json)
/// </summary>
/// <param name="json">Json</param>
/// <returns>Return ContractManifest</returns>
public static ContractManifest Parse(string json) => FromJson(JObject.Parse(json));
public static ContractManifest Parse(ReadOnlySpan<byte> json) => FromJson(JObject.Parse(json));

internal static ContractManifest Parse(string json) => FromJson(JObject.Parse(json));
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

It is recommended to use this overload.

public static ContractManifest Parse(ReadOnlySpan<byte> json) => FromJson(JObject.Parse(json));


/// <summary
/// To json
Expand Down
1 change: 1 addition & 0 deletions src/neo/VM/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ public static int GetByteLength(this StackItem item)
{
PrimitiveType p => p.Size,
Buffer b => b.Size,
Null _ => 0,
_ => throw new ArgumentException(),
};
}
Expand Down
4 changes: 2 additions & 2 deletions tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void ApplicationEngineVariablePrices()
debugger.StepInto(); // push 03 (length 1)
debugger.StepInto(); // push 00
Action act = () => InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot);
act.Should().Throw<InvalidOperationException>();
act.Should().Throw<InvalidCastException>();
}

// System.Storage.PutEx: 73e19b3a (requires push key and value)
Expand All @@ -82,7 +82,7 @@ public void ApplicationEngineVariablePrices()
debugger.StepInto(); // push 03 (length 1)
debugger.StepInto(); // push 00
Action act = () => InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot);
act.Should().Throw<InvalidOperationException>();
act.Should().Throw<InvalidCastException>();
}
}

Expand Down
4 changes: 2 additions & 2 deletions tests/neo.UnitTests/SmartContract/UT_InteropService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,10 +338,10 @@ public void TestRuntime_Serialize()
.Should().Be(new byte[] { 0x21, 0x01, 0x64 }.ToHexString());

engine.CurrentContext.EvaluationStack.Push(new byte[1024 * 1024 * 2]); //Larger than MaxItemSize
InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeFalse();
Assert.ThrowsException<InvalidOperationException>(() => InteropService.Invoke(engine, InteropService.Binary.Serialize));

engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new object())); //NotSupportedException
InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeFalse();
Assert.ThrowsException<NotSupportedException>(() => InteropService.Invoke(engine, InteropService.Binary.Serialize));
}

[TestMethod]
Expand Down