forked from neo-project/neo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor InteropService (neo-project#1655)
- Loading branch information
Showing
51 changed files
with
1,220 additions
and
1,860 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} |
Oops, something went wrong.