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

Read only methods #1052

Closed
wants to merge 26 commits into from
Closed
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
16 changes: 8 additions & 8 deletions neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class UT_ContractManifest
[TestMethod]
public void ParseFromJson_Default()
{
var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[]}";
var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any"",""readOnly"":false},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[]}";
var manifest = ContractManifest.Parse(json);

Assert.AreEqual(manifest.ToString(), json);
Expand All @@ -21,7 +21,7 @@ public void ParseFromJson_Default()
[TestMethod]
public void ParseFromJson_Features()
{
var json = @"{""groups"":[],""features"":{""storage"":true,""payable"":true},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[]}";
var json = @"{""groups"":[],""features"":{""storage"":true,""payable"":true},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any"",""readOnly"":false},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[]}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToJson().ToString(), json);

Expand All @@ -33,7 +33,7 @@ public void ParseFromJson_Features()
[TestMethod]
public void ParseFromJson_Permissions()
{
var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""safeMethods"":[]}";
var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any"",""readOnly"":false},""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[]}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);

Expand All @@ -50,21 +50,21 @@ public void ParseFromJson_Permissions()
}

[TestMethod]
public void ParseFromJson_SafeMethods()
public void ParseFromJson_ReadOnlyMethods()
{
var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[""balanceOf""]}";
var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any"",""readOnly"":true},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[]}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);

var check = ContractManifest.CreateDefault(UInt160.Zero);
check.SafeMethods = WildCardContainer<string>.Create("balanceOf");
check.Abi.EntryPoint.ReadOnly = true;
Assert.AreEqual(manifest.ToString(), check.ToString());
}

[TestMethod]
public void ParseFromJson_Trust()
{
var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""safeMethods"":[]}";
var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any"",""readOnly"":false},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""]}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);

Expand All @@ -76,7 +76,7 @@ public void ParseFromJson_Trust()
[TestMethod]
public void ParseFromJson_Groups()
{
var json = @"{""groups"":[{""pubKey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141""}],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[]}";
var json = @"{""groups"":[{""pubKey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141""}],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any"",""readOnly"":false},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[]}";
var manifest = ContractManifest.Parse(json);
Assert.AreEqual(manifest.ToString(), json);

Expand Down
66 changes: 66 additions & 0 deletions neo.UnitTests/SmartContract/UT_Syscalls.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Ledger;
using Neo.SmartContract;
using Neo.SmartContract.Manifest;
using Neo.VM;
using System.Linq;

Expand All @@ -9,6 +10,62 @@ namespace Neo.UnitTests.SmartContract
[TestClass]
public class UT_Syscalls
{
[TestMethod]
public void System_Storage_GetContext()
{
var snapshot = TestBlockchain.GetStore().GetSnapshot();
var contracts = (TestDataCache<UInt160, ContractState>)snapshot.Contracts;

// Call System_Storage_GetContext syscall

var script = new ScriptBuilder();
script.EmitSysCall(InteropService.System_Storage_GetContext);

var contract = new ContractState() { Script = script.ToArray() };
contract.Manifest = ContractManifest.CreateDefault(contract.ScriptHash);

contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contract.ScriptHash.ToArray()));
contracts.Add(contract.ScriptHash, contract);

// Call Contract

script = new ScriptBuilder();
script.EmitSysCall(InteropService.System_Contract_Call, contract.ScriptHash.ToArray(), "", 0);

// Execute

var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true);
engine.LoadScript(script.ToArray());
Assert.AreEqual(engine.Execute(), VMState.HALT);

// Check the results

Assert.AreEqual(1, engine.ResultStack.Count);
Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(VM.Types.InteropInterface));

var context = ((VM.Types.InteropInterface)engine.ResultStack.Pop()).GetInterface<StorageContext>();
Assert.AreEqual(context.ScriptHash, contract.ScriptHash);

// Change to ReadOnly Abi

contract.Manifest.Abi.EntryPoint.ReadOnly = true;
contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contract.ScriptHash.ToArray()));
contracts.Add(contract.ScriptHash, contract);

// Execute

engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true);
engine.LoadScript(script.ToArray());
Assert.AreEqual(engine.Execute(), VMState.FAULT);

// Check the results

Assert.AreEqual(0, engine.ResultStack.Count);

// Clean
contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contract.ScriptHash.ToArray()));
}

[TestMethod]
public void System_Runtime_GetInvocationCounter()
{
Expand All @@ -27,6 +84,10 @@ public void System_Runtime_GetInvocationCounter()
var contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() };
var contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() };

contractA.Manifest = ContractManifest.CreateDefault(contractA.ScriptHash);
contractB.Manifest = ContractManifest.CreateDefault(contractB.ScriptHash);
contractC.Manifest = ContractManifest.CreateDefault(contractC.ScriptHash);

contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray()));
contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray()));
contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray()));
Expand Down Expand Up @@ -61,6 +122,11 @@ public void System_Runtime_GetInvocationCounter()
1 /* C */
}
);

// Clean
contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray()));
contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray()));
contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray()));
}
}
}
7 changes: 6 additions & 1 deletion neo/SmartContract/ExecutionContextState.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
namespace Neo.SmartContract
namespace Neo.SmartContract
{
public class ExecutionContextState
{
/// <summary>
/// Script hash
/// </summary>
public UInt160 ScriptHash { get; set; }

/// <summary>
/// Is read only
/// </summary>
public bool ReadOnly { get; set; } = false;
}
}
12 changes: 7 additions & 5 deletions neo/SmartContract/InteropDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,27 @@ internal class InteropDescriptor
public long Price { get; }
public Func<RandomAccessStack<StackItem>, long> PriceCalculator { get; }
public TriggerType AllowedTriggers { get; }
public bool RequireWriteAccess { get; }

public InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, long price, TriggerType allowedTriggers)
: this(method, handler, allowedTriggers)
public InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, long price, TriggerType allowedTriggers, bool requireWriteAccess)
: this(method, handler, allowedTriggers, requireWriteAccess)
{
this.Price = price;
}

public InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, Func<RandomAccessStack<StackItem>, long> priceCalculator, TriggerType allowedTriggers)
: this(method, handler, allowedTriggers)
public InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, Func<RandomAccessStack<StackItem>, long> priceCalculator, TriggerType allowedTriggers, bool requireWriteAccess)
: this(method, handler, allowedTriggers, requireWriteAccess)
{
this.PriceCalculator = priceCalculator;
}

private InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, TriggerType allowedTriggers)
private InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, TriggerType allowedTriggers, bool requireWriteAccess)
{
this.Method = method;
this.Hash = method.ToInteropMethodHash();
this.Handler = handler;
this.AllowedTriggers = allowedTriggers;
this.RequireWriteAccess = requireWriteAccess;
Copy link
Contributor

@igormcoelho igormcoelho Aug 26, 2019

Choose a reason for hiding this comment

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

I don't think this is a good idea @shargon... what is RequireWriteAccess? To Storage?
Could we remove this?
I thought that, if current ExecutionContextState is running on ReadOnly, then methods that require write access could simply fail (example: Storage.GetCurrentContext`).

Copy link
Member Author

Choose a reason for hiding this comment

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

We can rename the var, but not remove it, because is used for ban the syscall when is in readOnly mode

}

public long GetPrice(RandomAccessStack<StackItem> stack)
Expand Down
6 changes: 3 additions & 3 deletions neo/SmartContract/InteropService.NEO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Neo.SmartContract
{
static partial class InteropService
{
public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application);
public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application, true);
public static readonly uint Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", Crypto_CheckSig, 0_01000000, TriggerType.All);
public static readonly uint Neo_Crypto_CheckMultiSig = Register("Neo.Crypto.CheckMultiSig", Crypto_CheckMultiSig, GetCheckMultiSigPrice, TriggerType.All);
public static readonly uint Neo_Header_GetVersion = Register("Neo.Header.GetVersion", Header_GetVersion, 0_00000400, TriggerType.Application);
Expand All @@ -27,8 +27,8 @@ static partial class InteropService
public static readonly uint Neo_Transaction_GetWitnesses = Register("Neo.Transaction.GetWitnesses", Transaction_GetWitnesses, 0_00010000, TriggerType.All);
public static readonly uint Neo_Witness_GetVerificationScript = Register("Neo.Witness.GetVerificationScript", Witness_GetVerificationScript, 0_00000400, TriggerType.All);
public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000, TriggerType.All);
public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application);
public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application);
public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application, true);
public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application, true);
public static readonly uint Neo_Contract_GetScript = Register("Neo.Contract.GetScript", Contract_GetScript, 0_00000400, TriggerType.Application);
public static readonly uint Neo_Contract_IsPayable = Register("Neo.Contract.IsPayable", Contract_IsPayable, 0_00000400, TriggerType.Application);
public static readonly uint Neo_Storage_Find = Register("Neo.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application);
Expand Down
Loading