NEO Vm原理及其实现

简介及与evm主要区别

neo vm和evm类似。底层都实现了一套opcode以及对应的执行器,opcode设计差距蛮大的,总体上来说evm的更加简洁,neo vm的功能更加丰富强大。

  1. neo底层原生支持类型系统,所有栈上的内容都是有类型的,而在evm中所有的栈上内容都是无类型的,依赖于运行时转换。实质上在于空间和时间的取舍。

  2. neo在opcode级别支持类型及其操作,在内部函数调用方面evm通过jumpdest解决,neo vm的jump类指令则仅用于循环,函数调用则通过call和systemcall解决(这样很好的支持了调用函数栈)。总的来说neo vm一些高级操作可以下沉到code级别执行,方便了代码的转换。而evm则更加考验编译器的能力。

  3. 隔离执行器和外部对象的具体实现,vm提供了一些外部接口用于执行一些和外部对象和存储相关的操作。eth的外部对象和指令揉合在一起。

  4. 理论上来说eth也可以支持多种语言,实际上来说solidity一家独大,其中未尝没有eth的opcode转换难度大的原因,而neo vm的opcode则相对友好,支持多种语言开发。当然evm也并非没有好处,solidity支持内联编程,如果熟悉evm的opcode能写出效率非常高的合约。neo在这方面尚未提供支持。

  5. neo vm支持debug,支持step into,step over,break point operation。

如需了解evm的详细实现可以参考另一篇博文 https://my.oschina.net/hunjixin/blog/1805306

代码结构

.
├── ExecutionContext.cs //执行上下文 引擎,代码,断点
├── ExecutionEngine.cs //执行引擎
├── Helper.cs //编码及long型
├── ICrypto.cs //加密解密
├── IInteropInterface.cs //外部对象相关
├── InteropService.cs //存储相关
├── IScriptContainer.cs //脚本
├── IScriptTable.cs //脚本读取
├── neo-vm.csproj
├── OpCode.cs //操作码
├── RandomAccessStack.cs //快速访问栈
├── ScriptBuilder.cs //脚本构建
├── StackItem.cs //栈
├── Types //类型
│ ├── Array.cs //数组
│ ├── Boolean.cs //bool
│ ├── ByteArray.cs //比特数组
│ ├── Integer.cs //整数
│ ├── InteropInterface.cs //操作对象接口
│ ├── Map.cs //映射
│ └── Struct.cs //结构体
└── VMState.cs //执行状态

类型系统

vm支持七种类型,分别是数组,bool,byte数组,整数,外部对象,映射,结构体。所有的结构体都是继承子StackItem,同时具有自己的数据字段。

stackItem

 public abstract class StackItem : IEquatable<StackItem>
{
public abstract bool Equals(StackItem other); public sealed override bool Equals(object obj)
{
if (obj == null) return false;
if (obj == this) return true;
if (obj is StackItem other)
return Equals(other);
return false;
} public static StackItem FromInterface(IInteropInterface value)
{
return new InteropInterface(value);
} public virtual BigInteger GetBigInteger()
{
return new BigInteger(GetByteArray());
} public virtual bool GetBoolean()
{
return GetByteArray().Any(p => p != );
} public abstract byte[] GetByteArray(); public override int GetHashCode()
{
unchecked
{
int hash = ;
foreach (byte element in GetByteArray())
hash = hash * + element;
return hash;
}
} public virtual string GetString()
{
return Encoding.UTF8.GetString(GetByteArray());
} public static implicit operator StackItem(int value)
{
return (BigInteger)value;
} public static implicit operator StackItem(uint value)
{
return (BigInteger)value;
} public static implicit operator StackItem(long value)
{
return (BigInteger)value;
} public static implicit operator StackItem(ulong value)
{
return (BigInteger)value;
} public static implicit operator StackItem(BigInteger value)
{
return new Integer(value);
} public static implicit operator StackItem(bool value)
{
return new Boolean(value);
} public static implicit operator StackItem(byte[] value)
{
return new ByteArray(value);
} public static implicit operator StackItem(StackItem[] value)
{
return new Array(value);
} public static implicit operator StackItem(List<StackItem> value)
{
return new Array(value);
}
}

bool 为例子

 public class Boolean : StackItem
{
private static readonly byte[] TRUE = { };
private static readonly byte[] FALSE = new byte[]; private bool value; public Boolean(bool value)
{
this.value = value;
} public override bool Equals(StackItem other)
{
if (ReferenceEquals(this, other)) return true;
if (ReferenceEquals(null, other)) return false;
if (other is Boolean b) return value == b.value;
byte[] bytes_other;
try
{
bytes_other = other.GetByteArray();
}
catch (NotSupportedException)
{
return false;
}
return GetByteArray().SequenceEqual(bytes_other);
} public override BigInteger GetBigInteger()
{
return value ? BigInteger.One : BigInteger.Zero;
} public override bool GetBoolean()
{
return value;
} public override byte[] GetByteArray()
{
return value ? TRUE : FALSE;
}
}

opecode

贴一些上来感受下

数值常量

PUSH2 = 0x52, // The number 2 is pushed onto the stack.
PUSH3 = 0x53, // The number 3 is pushed onto the stack.
PUSH4 = 0x54, // The number 4 is pushed onto the stack.
PUSH5 = 0x55, // The number 5 is pushed onto the stack.

跳转

    JMP = 0x62,
JMPIF = 0x63,
JMPIFNOT = 0x64,

调用

    CALL = 0x65,
RET = 0x66,
APPCALL = 0x67,
SYSCALL = 0x68,
TAILCALL = 0x69,

栈操作,neo再着一块的相对比较丰富(这里并不是全部)

    DROP = 0x75, // Removes the top stack item.
DUP = 0x76, // Duplicates the top stack item.
PICK = 0x79, // The item n back in the stack is copied to the top.
ROLL = 0x7A, // The item n back in the stack is moved to the top.
SWAP = 0x7C, // The top two items on the stack are swapped.

运算,仅贴了些代表性的上来

    INC = 0x8B, // 1 is added to the input.
SIGN = 0x8D,
ABS = 0x90, // The input is made positive.
NZ = 0x92, // Returns 0 if the input is 0. 1 otherwise.
DIV = 0x96, // a is divided by b.
MOD = 0x97, // Returns the remainder after dividing a by b.
SHR = 0x99, // Shifts a right b bits, preserving sign.
BOOLAND = 0x9A, // If both a and b are not 0, the output is 1. Otherwise 0.
GTE = 0xA2, // Returns 1 if a is greater than or equal to b, 0 otherwise.
MAX = 0xA4, // Returns the larger of a and b.
WITHIN = 0xA5, // Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.

加密验证

    SHA1 = 0xA7, // The input is hashed using SHA-1.
SHA256 = 0xA8, // The input is hashed using SHA-256.
HASH160 = 0xA9,
HASH256 = 0xAA,
CHECKSIG = 0xAC,
VERIFY = 0xAD,
CHECKMULTISIG = 0xAE,

数组,结构体及相关操作

    ARRAYSIZE = 0xC0,
PACK = 0xC1,
UNPACK = 0xC2,
PICKITEM = 0xC3,
SETITEM = 0xC4,
NEWARRAY = 0xC5, //用作引用類型
NEWSTRUCT = 0xC6, //用作值類型
NEWMAP = 0xC7,
APPEND = 0xC8,
REVERSE = 0xC9,
REMOVE = 0xCA,
HASKEY = 0xCB,
KEYS = 0xCC,
VALUES = 0xCD,

异常

 THROW = 0xF0,
THROWIFNOT = 0xF1

外部接口

脚本容器,保存当前执行脚本

 public interface IScriptContainer : IInteropInterface
{
byte[] GetMessage();
}

合约脚本查找

    public interface IScriptTable
{
byte[] GetScript(byte[] script_hash);
}

加密

    public interface ICrypto
{
byte[] Hash160(byte[] message); byte[] Hash256(byte[] message); bool VerifySignature(byte[] message, byte[] signature, byte[] pubkey);
}

外部服务调用接口

 public class InteropService
{
private Dictionary<string, Func<ExecutionEngine, bool>> dictionary = new Dictionary<string, Func<ExecutionEngine, bool>>(); public InteropService()
{
Register("System.ExecutionEngine.GetScriptContainer", GetScriptContainer);
Register("System.ExecutionEngine.GetExecutingScriptHash", GetExecutingScriptHash);
Register("System.ExecutionEngine.GetCallingScriptHash", GetCallingScriptHash);
Register("System.ExecutionEngine.GetEntryScriptHash", GetEntryScriptHash);
} protected void Register(string method, Func<ExecutionEngine, bool> handler)
{
dictionary[method] = handler;
} internal bool Invoke(string method, ExecutionEngine engine)
{
if (!dictionary.TryGetValue(method, out Func<ExecutionEngine, bool> func)) return false;
return func(engine);
} private static bool GetScriptContainer(ExecutionEngine engine)
{
engine.EvaluationStack.Push(StackItem.FromInterface(engine.ScriptContainer));
return true;
} private static bool GetExecutingScriptHash(ExecutionEngine engine)
{
engine.EvaluationStack.Push(engine.CurrentContext.ScriptHash);
return true;
} private static bool GetCallingScriptHash(ExecutionEngine engine)
{
engine.EvaluationStack.Push(engine.CallingContext.ScriptHash);
return true;
} private static bool GetEntryScriptHash(ExecutionEngine engine)
{
engine.EvaluationStack.Push(engine.EntryContext.ScriptHash);
return true;
}
}

外部对象接口

    public interface IInteropInterface
{
}

执行器

 public class ExecutionEngine : IDisposable
{
//调用栈
public RandomAccessStack<ExecutionContext> InvocationStack { get; } = new RandomAccessStack<ExecutionContext>();
//执行栈
public RandomAccessStack<StackItem> EvaluationStack { get; } = new RandomAccessStack<StackItem>();
//参数栈
public RandomAccessStack<StackItem> AltStack { get; } = new RandomAccessStack<StackItem>();
public ExecutionContext CurrentContext => InvocationStack.Peek();
public ExecutionContext CallingContext => InvocationStack.Count > ? InvocationStack.Peek() : null;
public ExecutionContext EntryContext => InvocationStack.Peek(InvocationStack.Count - );
//执行状态
public VMState State { get; protected set; } = VMState.BREAK; //载入执行脚本
void LoadScript(byte[] script, bool push_only = false){}
//添加断点
void AddBreakPoint(uint position){}
//删除断点
bool RemoveBreakPoint(uint position){} //执行脚本
void Execute(){}
//执行opcode
void ExecuteOp(OpCode opcode, ExecutionContext context){} //执行下一步
void StepInto(){}
//当前call执行完成
void StepOut(){}
//全部执行
void StepOver(){}
}

操作符号执行过程

     private void ExecuteOp(OpCode opcode, ExecutionContext context)
{
if (opcode > OpCode.PUSH16 && opcode != OpCode.RET && context.PushOnly)
{
State |= VMState.FAULT;
return;
}
if (opcode >= OpCode.PUSHBYTES1 && opcode <= OpCode.PUSHBYTES75)
EvaluationStack.Push(context.OpReader.ReadBytes((byte)opcode));
else
switch (opcode)
{
//常量push
case OpCode.PUSH1:
case OpCode.PUSH16:
EvaluationStack.Push((int)opcode - (int)OpCode.PUSH1 + );
break;
case OpCode.JMP: //跳转
{
int offset = context.OpReader.ReadInt16();
offset = context.InstructionPointer + offset - ;
if (offset < || offset > context.Script.Length)
{
State |= VMState.FAULT;
return;
}
bool fValue = true;
if (opcode > OpCode.JMP)
{
fValue = EvaluationStack.Pop().GetBoolean();
if (opcode == OpCode.JMPIFNOT)
fValue = !fValue;
}
if (fValue)
context.InstructionPointer = offset;
}
break;
case OpCode.CALL: //和systemcall差不多, 区别在于system是系统预先注册的函数 call调用的是用户自己写的函数
InvocationStack.Push(context.Clone());
context.InstructionPointer += ;
ExecuteOp(OpCode.JMP, CurrentContext);
break;
case OpCode.RET: //退出当前函数栈
InvocationStack.Pop().Dispose();
if (InvocationStack.Count == )
State |= VMState.HALT;
break;
case OpCode.APPCALL: //调用外部合约
case OpCode.TAILCALL:
{
if (table == null)
{
State |= VMState.FAULT;
return;
} byte[] script_hash = context.OpReader.ReadBytes();
if (script_hash.All(p => p == ))
{
script_hash = EvaluationStack.Pop().GetByteArray();
} byte[] script = table.GetScript(script_hash);
if (script == null)
{
State |= VMState.FAULT;
return;
}
if (opcode == OpCode.TAILCALL)
InvocationStack.Pop().Dispose();
LoadScript(script);
}
break;
case OpCode.SYSCALL: //内部合约函数调用
if (!service.Invoke(Encoding.ASCII.GetString(context.OpReader.ReadVarBytes()), this))
State |= VMState.FAULT;
break;
case OpCode.DROP: //移除栈顶
EvaluationStack.Pop();
break;
case OpCode.DUP: //赋值栈顶 有对应按位置复制的指令
EvaluationStack.Push(EvaluationStack.Peek());
break;
case OpCode.EQUAL: //判等
{
StackItem x2 = EvaluationStack.Pop();
StackItem x1 = EvaluationStack.Pop();
EvaluationStack.Push(x1.Equals(x2));
}
break; // Numeric
case OpCode.ABS: //运算 加减乘除 最大值最小值等等
{
BigInteger x = EvaluationStack.Pop().GetBigInteger();
EvaluationStack.Push(BigInteger.Abs(x));
}
break;
// Crypto
case OpCode.SHA256: //加密
using (SHA256 sha = SHA256.Create())
{
byte[] x = EvaluationStack.Pop().GetByteArray();
EvaluationStack.Push(sha.ComputeHash(x));
}
break;
case OpCode.CHECKSIG: //验证
{
byte[] pubkey = EvaluationStack.Pop().GetByteArray();
byte[] signature = EvaluationStack.Pop().GetByteArray();
try
{
EvaluationStack.Push(Crypto.VerifySignature(ScriptContainer.GetMessage(), signature, pubkey));
}
catch (ArgumentException)
{
EvaluationStack.Push(false);
}
}
break;
// Array
case OpCode.PICKITEM: //数组映射取值
{
StackItem key = EvaluationStack.Pop();
if (key is ICollection)
{
State |= VMState.FAULT;
return;
}
switch (EvaluationStack.Pop())
{
case VMArray array:
int index = (int)key.GetBigInteger();
if (index < || index >= array.Count)
{
State |= VMState.FAULT;
return;
}
EvaluationStack.Push(array[index]);
break;
case Map map:
if (map.TryGetValue(key, out StackItem value))
{
EvaluationStack.Push(value);
}
else
{
State |= VMState.FAULT;
return;
}
break;
default:
State |= VMState.FAULT;
return;
}
}
break;
case OpCode.SETITEM: //数组 映射赋值
{
StackItem value = EvaluationStack.Pop();
if (value is Struct s) value = s.Clone();
StackItem key = EvaluationStack.Pop();
if (key is ICollection)
{
State |= VMState.FAULT;
return;
}
switch (EvaluationStack.Pop())
{
case VMArray array:
int index = (int)key.GetBigInteger();
if (index < || index >= array.Count)
{
State |= VMState.FAULT;
return;
}
array[index] = value;
break;
case Map map:
map[key] = value;
break;
default:
State |= VMState.FAULT;
return;
}
}
break;
case OpCode.NEWARRAY: //创建数组
{
int count = (int)EvaluationStack.Pop().GetBigInteger();
List<StackItem> items = new List<StackItem>(count);
for (var i = ; i < count; i++)
{
items.Add(false);
}
EvaluationStack.Push(new Types.Array(items));
}
break;
case OpCode.NEWSTRUCT: //创建结构体
{
int count = (int)EvaluationStack.Pop().GetBigInteger();
List<StackItem> items = new List<StackItem>(count);
for (var i = ; i < count; i++)
{
items.Add(false);
}
EvaluationStack.Push(new VM.Types.Struct(items));
}
break;
case OpCode.NEWMAP: //创建映射
EvaluationStack.Push(new Map());
break;
case OpCode.APPEND: //追加元素
{
StackItem newItem = EvaluationStack.Pop();
if (newItem is Types.Struct s)
{
newItem = s.Clone();
}
StackItem arrItem = EvaluationStack.Pop();
if (arrItem is VMArray array)
{
array.Add(newItem);
}
else
{
State |= VMState.FAULT;
return;
}
}
break;
case OpCode.REMOVE: //移除元素
{
StackItem key = EvaluationStack.Pop();
if (key is ICollection)
{
State |= VMState.FAULT;
return;
}
switch (EvaluationStack.Pop())
{
case VMArray array:
int index = (int)key.GetBigInteger();
if (index < || index >= array.Count)
{
State |= VMState.FAULT;
return;
}
array.RemoveAt(index);
break;
case Map map:
map.Remove(key);
break;
default:
State |= VMState.FAULT;
return;
}
}
break;
case OpCode.KEYS: //获取映射键集合,对应的还有获取值集合 haskey
switch (EvaluationStack.Pop())
{
case Map map:
EvaluationStack.Push(new VMArray(map.Keys));
break;
default:
State |= VMState.FAULT;
return;
}
break; // Exceptions
case OpCode.THROW: //异常中止
State |= VMState.FAULT;
return;
case OpCode.THROWIFNOT:
if (!EvaluationStack.Pop().GetBoolean())
{
State |= VMState.FAULT;
return;
}
break; default:
State |= VMState.FAULT;
return;
}
if (!State.HasFlag(VMState.FAULT) && InvocationStack.Count > )
{
//断点起效的位置
if (CurrentContext.BreakPoints.Contains((uint)CurrentContext.InstructionPointer))
State |= VMState.BREAK;
}
}

转载自 https://my.oschina.net/hunjixin/blog/1812516

												

NEO VM原理及其实现(转载)的更多相关文章

  1. JAVA CAS原理深度分析-转载

    参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...

  2. NET/ASP.NET Routing路由(深入解析路由系统架构原理)(转载)

    NET/ASP.NET Routing路由(深入解析路由系统架构原理) 阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模 ...

  3. JMeter使用中遇到的问题:Jmeter Debug - "Unrecognized VM option '+HeapDumpOnOutOfMemoryError"(转载)

    转载自 http://www.cnblogs.com/yangxia-test 启动JMeter.bat的程序时,出现以下出错信息: Unrecognized VM option '+HeapDump ...

  4. mysql 索引原理及查询优化 -转载

    转载自 mysql 索引原理及查询优化 https://www.cnblogs.com/panfb/p/8043681.html 潘红伟   mysql 索引原理及查询优化 一 介绍 为何要有索引? ...

  5. Redis集群的原理和搭建(转载)

    转载来源:https://www.jianshu.com/p/c869feb5581d Redis集群的原理和搭建 前言 Redis 是我们目前大规模使用的缓存中间件,由于它强大高效而又便捷的功能,得 ...

  6. 深入浅出Java并发包—CountDownLauch原理分析 (转载)

    转载地址:http://yhjhappy234.blog.163.com/blog/static/3163283220135875759265/ CountDownLauch是Java并发包中的一个同 ...

  7. JPEG原理详解 (转载)

    JPEG算法解密 by jinchao   图片压缩有多重要,可能很多人可能并没有一个直观上的认识,举个例子,一张800X800大小的普通图片,如果未经压缩,大概在1.7MB左右,这个体积如果存放文本 ...

  8. 弱类型变量原理探究(转载 http://www.csdn.net/article/2014-09-15/2821685-exploring-of-the-php)

    N首页> 云计算 [问底]王帅:深入PHP内核(一)——弱类型变量原理探究 发表于2014-09-19 09:00| 13055次阅读| 来源CSDN| 36 条评论| 作者王帅 问底PHP王帅 ...

  9. DPM(Deformable Parts Model)--原理(一)(转载)

    DPM(Deformable Parts Model) Reference: Object detection with discriminatively trained partbased mode ...

随机推荐

  1. GitHub学习三-远程版本库更新与提交

    1.远程版本库更新 一般来说,将本地与远程相关联之后,首先将数据从远程更新下来再上传比较好. 输入 git pull origin master 如果新建版本库的话勾选了初始化包含readme.md, ...

  2. Oracle 11.2.0.4.0 Dataguard部署和日常维护(3)-Datauard监控篇

    1.  v$database    查看当前数据库的角色和保护模式 primary库查看 column NAME format a10 column PROTECTION_MODE format a2 ...

  3. wps去除首字母自动大写

    首字母大写功能在不是进行英文编写时是个“自作聪明”的功能,我们可能会想把它关掉.

  4. 维护一个旧程序 linq2sql,出现row not found or changed的异常

    维护一个旧程序 linq2sql,出现row not found or changed的异常, 查博客园,文章都是一大抄,都不对. 想想之前都能保存的.这个异常是在加了字段之后出现的. 因为用vs.n ...

  5. php 异常捕获的坑

    thinkphp 框架需要注意 书写为(Exception $e)将无效   需要写成 (\Exception $e) try { throw new \Exception("Error P ...

  6. H5离线缓存技术Application Cache

    H5离线缓存技术Application Cache 1.离线缓存技术:是浏览器本身的一种机制 HTML5引入Application Cache(应用程序缓存)技术,离线存储可以将站点的一些文件存储在本 ...

  7. UVA 11464 - Even Parity 状态压缩,分析 难度: 2

    题目 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&a ...

  8. console.log()显示图片以及为文字加样式

    有兴趣的同学可以文章最后的代码复制贴到控制台玩玩. Go for Code 在正常模式下,一般只能向console 控制台输出简单的文字信息.但为了把信息输出得更优雅更便于阅读,除了cosole.lo ...

  9. 银联支付 Asp.Net 对接开发内容简介

    银联对接开发主要包含测试环境以及生产环境两部分. 其中程序开发部分测试以及生产是相同的. 不同的是,测试环境与生产环境请求支付的Url地址,以及分别使用的证书不同. 一.配置部分 1,测试环境证书获取 ...

  10. day25-python操作redis一

    1.     Python操作nosql数据库 NoSQL,泛指非关系型的数据库.随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2 ...