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

Refactor InteropService #1655

Merged
merged 8 commits into from
May 30, 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
2 changes: 1 addition & 1 deletion src/neo/Ledger/Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ private static Transaction DeployNativeContracts()
byte[] script;
using (ScriptBuilder sb = new ScriptBuilder())
{
sb.EmitSysCall(InteropService.Native.Deploy);
sb.EmitSysCall(ApplicationEngine.Neo_Native_Deploy);
script = sb.ToArray();
}
return new Transaction
Expand Down
20 changes: 20 additions & 0 deletions src/neo/SmartContract/ApplicationEngine.Binary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Neo.VM.Types;

namespace Neo.SmartContract
{
partial class ApplicationEngine
{
public static readonly InteropDescriptor System_Binary_Serialize = Register("System.Binary.Serialize", nameof(BinarySerialize), 0_00100000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor System_Binary_Deserialize = Register("System.Binary.Deserialize", nameof(BinaryDeserialize), 0_00500000, TriggerType.All, CallFlags.None);

internal byte[] BinarySerialize(StackItem item)
{
return BinarySerializer.Serialize(item, MaxItemSize);
}

internal StackItem BinaryDeserialize(byte[] data)
{
return BinarySerializer.Deserialize(data, MaxStackSize, MaxItemSize, ReferenceCounter);
}
}
}
103 changes: 103 additions & 0 deletions src/neo/SmartContract/ApplicationEngine.Blockchain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using System;
using System.Numerics;

namespace Neo.SmartContract
{
partial class ApplicationEngine
{
public const uint MaxTraceableBlocks = Transaction.MaxValidUntilBlockIncrement;

public static readonly InteropDescriptor System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", nameof(GetBlockchainHeight), 0_00000400, TriggerType.Application, CallFlags.AllowStates);
public static readonly InteropDescriptor System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", nameof(GetBlock), 0_02500000, TriggerType.Application, CallFlags.AllowStates);
public static readonly InteropDescriptor System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", nameof(GetTransaction), 0_01000000, TriggerType.Application, CallFlags.AllowStates);
public static readonly InteropDescriptor System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", nameof(GetTransactionHeight), 0_01000000, TriggerType.Application, CallFlags.AllowStates);
public static readonly InteropDescriptor System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", nameof(GetTransactionFromBlock), 0_01000000, TriggerType.Application, CallFlags.AllowStates);
public static readonly InteropDescriptor System_Blockchain_GetContract = Register("System.Blockchain.GetContract", nameof(GetContract), 0_01000000, TriggerType.Application, CallFlags.AllowStates);

internal uint GetBlockchainHeight()
{
return Snapshot.Height;
}

internal Block GetBlock(byte[] indexOrHash)
{
UInt256 hash;
if (indexOrHash.Length < UInt256.Length)
{
BigInteger bi = new BigInteger(indexOrHash);
if (bi < uint.MinValue || bi > uint.MaxValue)
throw new ArgumentOutOfRangeException(nameof(indexOrHash));
hash = Blockchain.Singleton.GetBlockHash((uint)bi);
}
else if (indexOrHash.Length == UInt256.Length)
{
hash = new UInt256(indexOrHash);
}
else
{
throw new ArgumentException();
}
if (hash is null) return null;
Block block = Snapshot.GetBlock(hash);
if (block is null) return null;
if (!IsTraceableBlock(Snapshot, block.Index)) return null;
return block;
}

internal Transaction GetTransaction(UInt256 hash)
{
TransactionState state = Snapshot.Transactions.TryGet(hash);
if (state != null && !IsTraceableBlock(Snapshot, state.BlockIndex)) state = null;
return state?.Transaction;
}

internal int GetTransactionHeight(UInt256 hash)
{
TransactionState state = Snapshot.Transactions.TryGet(hash);
if (state is null) return -1;
if (!IsTraceableBlock(Snapshot, state.BlockIndex)) return -1;
return (int)state.BlockIndex;
}

internal Transaction GetTransactionFromBlock(byte[] blockIndexOrHash, int txIndex)
{
UInt256 hash;
if (blockIndexOrHash.Length < UInt256.Length)
{
BigInteger bi = new BigInteger(blockIndexOrHash);
if (bi < uint.MinValue || bi > uint.MaxValue)
throw new ArgumentOutOfRangeException(nameof(blockIndexOrHash));
hash = Blockchain.Singleton.GetBlockHash((uint)bi);
}
else if (blockIndexOrHash.Length == UInt256.Length)
{
hash = new UInt256(blockIndexOrHash);
}
else
{
throw new ArgumentException();
}
if (hash is null) return null;
TrimmedBlock block = Snapshot.Blocks.TryGet(hash);
if (block is null) return null;
if (!IsTraceableBlock(Snapshot, block.Index)) return null;
if (txIndex < 0 || txIndex >= block.Hashes.Length - 1)
throw new ArgumentOutOfRangeException(nameof(txIndex));
return Snapshot.GetTransaction(block.Hashes[txIndex + 1]);
}

internal ContractState GetContract(UInt160 hash)
{
return Snapshot.Contracts.TryGet(hash);
}

private static bool IsTraceableBlock(StoreView snapshot, uint index)
{
if (index > snapshot.Height) return false;
return index + MaxTraceableBlocks > snapshot.Height;
}
}
}
181 changes: 181 additions & 0 deletions src/neo/SmartContract/ApplicationEngine.Contract.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.Ledger;
using Neo.SmartContract.Manifest;
using Neo.SmartContract.Native;
using Neo.VM;
using System;
using System.Linq;
using Array = Neo.VM.Types.Array;

namespace Neo.SmartContract
{
partial class ApplicationEngine
{
public const int MaxContractLength = 1024 * 1024;

public static readonly InteropDescriptor System_Contract_Create = Register("System.Contract.Create", nameof(CreateContract), 0, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor System_Contract_Update = Register("System.Contract.Update", nameof(UpdateContract), 0, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor System_Contract_Destroy = Register("System.Contract.Destroy", nameof(DestroyContract), 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall);
public static readonly InteropDescriptor System_Contract_CallEx = Register("System.Contract.CallEx", nameof(CallContractEx), 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall);
public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 0_00030000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 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.
/// </summary>
public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 0_00010000, TriggerType.All, CallFlags.None);

internal ContractState CreateContract(byte[] script, byte[] manifest)
{
if (script.Length == 0 || script.Length > MaxContractLength || manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength)
throw new ArgumentException();

if (!AddGas(StoragePrice * (script.Length + manifest.Length)))
throw new InvalidOperationException();

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

if (!contract.Manifest.IsValid(hash)) throw new InvalidOperationException();

Snapshot.Contracts.Add(hash, contract);
return contract;
}

internal void UpdateContract(byte[] script, byte[] manifest)
{
if (!AddGas(StoragePrice * (script?.Length ?? 0 + manifest?.Length ?? 0)))
throw new InvalidOperationException();

var contract = Snapshot.Contracts.TryGet(CurrentScriptHash);
if (contract is null) throw new InvalidOperationException();

if (script != null)
{
if (script.Length == 0 || script.Length > MaxContractLength)
throw new ArgumentException();
UInt160 hash_new = script.ToScriptHash();
if (hash_new.Equals(CurrentScriptHash) || Snapshot.Contracts.TryGet(hash_new) != null)
throw new InvalidOperationException();
contract = new ContractState
{
Id = contract.Id,
Script = script.ToArray(),
Manifest = contract.Manifest
};
contract.Manifest.Abi.Hash = hash_new;
Snapshot.Contracts.Add(hash_new, contract);
Snapshot.Contracts.Delete(CurrentScriptHash);
}
if (manifest != null)
{
if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength)
throw new ArgumentException();
contract = Snapshot.Contracts.GetAndChange(contract.ScriptHash);
contract.Manifest = ContractManifest.Parse(manifest);
if (!contract.Manifest.IsValid(contract.ScriptHash))
throw new InvalidOperationException();
if (!contract.HasStorage && Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id)).Any())
throw new InvalidOperationException();
}
}

internal void DestroyContract()
{
UInt160 hash = CurrentScriptHash;
ContractState contract = Snapshot.Contracts.TryGet(hash);
if (contract == null) return;
Snapshot.Contracts.Delete(hash);
if (contract.HasStorage)
foreach (var (key, _) in Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id)))
Snapshot.Storages.Delete(key);
}

internal void CallContract(UInt160 contractHash, string method, Array args)
{
CallContractInternal(contractHash, method, args, CallFlags.All);
}

internal void CallContractEx(UInt160 contractHash, string method, Array args, CallFlags callFlags)
{
if ((callFlags & ~CallFlags.All) != 0)
throw new ArgumentOutOfRangeException(nameof(callFlags));
CallContractInternal(contractHash, method, args, callFlags);
}

private void CallContractInternal(UInt160 contractHash, string method, Array args, CallFlags flags)
{
if (method.StartsWith('_')) throw new ArgumentException();

ContractState contract = Snapshot.Contracts.TryGet(contractHash);
if (contract is null) throw new InvalidOperationException();

ContractManifest currentManifest = Snapshot.Contracts.TryGet(CurrentScriptHash)?.Manifest;

if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method))
throw new InvalidOperationException();

if (invocationCounter.TryGetValue(contract.ScriptHash, out var counter))
{
invocationCounter[contract.ScriptHash] = counter + 1;
}
else
{
invocationCounter[contract.ScriptHash] = 1;
}

ExecutionContextState state = CurrentContext.GetState<ExecutionContextState>();
UInt160 callingScriptHash = state.ScriptHash;
CallFlags callingFlags = state.CallFlags;

ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method);
if (md is null) throw new InvalidOperationException();
int rvcount = md.ReturnType == ContractParameterType.Void ? 0 : 1;
ExecutionContext context_new = LoadScript(contract.Script, rvcount);
state = context_new.GetState<ExecutionContextState>();
state.CallingScriptHash = callingScriptHash;
state.CallFlags = flags & callingFlags;

if (NativeContract.IsNative(contractHash))
{
context_new.EvaluationStack.Push(args);
context_new.EvaluationStack.Push(method);
}
else
{
for (int i = args.Count - 1; i >= 0; i--)
context_new.EvaluationStack.Push(args[i]);
context_new.InstructionPointer = md.Offset;
}

md = contract.Manifest.Abi.GetMethod("_initialize");
if (md != null) LoadClonedContext(md.Offset);
}

internal bool IsStandardContract(UInt160 hash)
{
ContractState contract = Snapshot.Contracts.TryGet(hash);
return contract is null || contract.Script.IsStandardContract();
}

internal CallFlags GetCallFlags()
{
var state = CurrentContext.GetState<ExecutionContextState>();
return state.CallFlags;
}

internal UInt160 CreateStandardAccount(ECPoint pubKey)
{
return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash();
}
}
}
Loading