Skip to content
This repository has been archived by the owner on Nov 22, 2023. It is now read-only.

Cache ScriptHash #76

Merged
merged 5 commits into from
Jan 11, 2019
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
69 changes: 50 additions & 19 deletions src/neo-vm/ExecutionContext.cs
Original file line number Diff line number Diff line change
@@ -1,63 +1,94 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;

namespace Neo.VM
{
public class ExecutionContext : IDisposable
{
public readonly byte[] Script;
/// <summary>
/// Number of items to be returned
/// </summary>
internal readonly int RVCount;

/// <summary>
/// Binary Reader of the script
/// </summary>
internal readonly BinaryReader OpReader;
private readonly ICrypto crypto;

/// <summary>
/// Script
/// </summary>
public readonly Script Script;

/// <summary>
/// Evaluation stack
/// </summary>
public RandomAccessStack<StackItem> EvaluationStack { get; } = new RandomAccessStack<StackItem>();

/// <summary>
/// Alternative stack
/// </summary>
public RandomAccessStack<StackItem> AltStack { get; } = new RandomAccessStack<StackItem>();

/// <summary>
/// Instruction pointer
/// </summary>
public int InstructionPointer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return (int)OpReader.BaseStream.Position;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
OpReader.BaseStream.Seek(value, SeekOrigin.Begin);
}
}

/// <summary>
/// Next instruction
/// </summary>
public OpCode NextInstruction
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
var position = OpReader.BaseStream.Position;
if (position >= Script.Length) return OpCode.RET;

return (OpCode)Script[position];
var position = (int)OpReader.BaseStream.Position;

return position >= Script.Length ? OpCode.RET : Script[position];
}
}

private byte[] _script_hash = null;

/// <summary>
/// Cached script hash
/// </summary>
public byte[] ScriptHash
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_script_hash == null)
_script_hash = crypto.Hash160(Script);
return _script_hash;
return Script.ScriptHash;
}
}

internal ExecutionContext(ExecutionEngine engine, byte[] script, int rvcount)
/// <summary>
/// Constructor
/// </summary>
/// <param name="script">Script</param>
/// <param name="rvcount">Number of items to be returned</param>
internal ExecutionContext(Script script, int rvcount)
{
this.Script = script;
this.RVCount = rvcount;
this.OpReader = new BinaryReader(new MemoryStream(script, false));
this.crypto = engine.Crypto;
this.Script = script;
this.OpReader = script.GetBinaryReader();
}

public void Dispose()
{
OpReader.Dispose();
}
/// <summary>
/// Free resources
/// </summary>
public void Dispose() => OpReader.Dispose();
}
}
41 changes: 22 additions & 19 deletions src/neo-vm/ExecutionEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,28 +160,20 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context)
case OpCode.APPCALL:
case OpCode.TAILCALL:
{
if (table == null)
{
State |= VMState.FAULT;
return;
}

byte[] script_hash = context.OpReader.SafeReadBytes(20);
if (script_hash.All(p => p == 0))
{
script_hash = context.EvaluationStack.Pop().GetByteArray();
}

byte[] script = table.GetScript(script_hash);
if (script == null)
ExecutionContext context_new = LoadScriptByHash(script_hash);
if (context_new == null)
{
State |= VMState.FAULT;
return;
}

ExecutionContext context_new = LoadScript(script);
context.EvaluationStack.CopyTo(context_new.EvaluationStack);

if (opcode == OpCode.TAILCALL)
InvocationStack.Remove(1).Dispose();
else
Expand Down Expand Up @@ -963,11 +955,6 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context)
case OpCode.CALL_ET:
case OpCode.CALL_EDT:
{
if (table == null)
{
State |= VMState.FAULT;
return;
}
int rvcount = context.OpReader.ReadByte();
int pcount = context.OpReader.ReadByte();
if (context.EvaluationStack.Count < pcount)
Expand All @@ -988,13 +975,12 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context)
script_hash = context.EvaluationStack.Pop().GetByteArray();
else
script_hash = context.OpReader.SafeReadBytes(20);
byte[] script = table.GetScript(script_hash);
if (script == null)
ExecutionContext context_new = LoadScriptByHash(script_hash, rvcount);
if (context_new == null)
{
State |= VMState.FAULT;
return;
}
ExecutionContext context_new = LoadScript(script, rvcount);
context.EvaluationStack.CopyTo(context_new.EvaluationStack, pcount);
if (opcode == OpCode.CALL_ET || opcode == OpCode.CALL_EDT)
InvocationStack.Remove(1).Dispose();
Expand Down Expand Up @@ -1029,7 +1015,24 @@ private void ExecuteOp(OpCode opcode, ExecutionContext context)

public ExecutionContext LoadScript(byte[] script, int rvcount = -1)
{
ExecutionContext context = new ExecutionContext(this, script, rvcount);
ExecutionContext context = new ExecutionContext(new Script(Crypto, script), rvcount);
InvocationStack.Push(context);
return context;
}

private ExecutionContext LoadScript(Script script, int rvcount = -1)
{
ExecutionContext context = new ExecutionContext(script, rvcount);
InvocationStack.Push(context);
return context;
}

private ExecutionContext LoadScriptByHash(byte[] hash, int rvcount = -1)
{
if (table == null) return null;
byte[] script = table.GetScript(hash);
if (script == null) return null;
ExecutionContext context = new ExecutionContext(new Script(hash, script), rvcount);
InvocationStack.Push(context);
return context;
}
Expand Down
84 changes: 84 additions & 0 deletions src/neo-vm/Script.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.IO;
using System.Runtime.CompilerServices;

namespace Neo.VM
{
public class Script
{
private byte[] _scriptHash = null;

private readonly byte[] _value;
private readonly ICrypto _crypto;

/// <summary>
/// Cached script hash
/// </summary>
public byte[] ScriptHash
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_scriptHash == null) _scriptHash = _crypto.Hash160(_value);
return _scriptHash;
}
}

/// <summary>
/// Script length
/// </summary>
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _value.Length;
}
}

/// <summary>
/// Get opcode
/// </summary>
/// <param name="index">Index</param>
/// <returns>Returns the opcode</returns>
public OpCode this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return (OpCode)_value[index];
}
}

/// <summary>
/// Get Binary reader
/// </summary>
/// <returns>Returns the binary reader of the script</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BinaryReader GetBinaryReader()
{
return new BinaryReader(new MemoryStream(_value, false));
}

/// <summary>
/// Constructor
/// </summary>
/// <param name="crypto">Crypto</param>
/// <param name="script">Script</param>
public Script(ICrypto crypto, byte[] script)
{
_crypto = crypto;
_value = script;
}

/// <summary>
/// Constructor
/// </summary>
/// <param name="hash">Hash</param>
/// <param name="script">Script</param>
internal Script(byte[] hash, byte[] script)
{
_scriptHash = hash;
_value = script;
}
}
}