NEO VM原理及其实现(转载)
NEO Vm原理及其实现
简介及与evm主要区别
neo vm和evm类似。底层都实现了一套opcode以及对应的执行器,opcode设计差距蛮大的,总体上来说evm的更加简洁,neo vm的功能更加丰富强大。
neo底层原生支持类型系统,所有栈上的内容都是有类型的,而在evm中所有的栈上内容都是无类型的,依赖于运行时转换。实质上在于空间和时间的取舍。
neo在opcode级别支持类型及其操作,在内部函数调用方面evm通过jumpdest解决,neo vm的jump类指令则仅用于循环,函数调用则通过call和systemcall解决(这样很好的支持了调用函数栈)。总的来说neo vm一些高级操作可以下沉到code级别执行,方便了代码的转换。而evm则更加考验编译器的能力。
隔离执行器和外部对象的具体实现,vm提供了一些外部接口用于执行一些和外部对象和存储相关的操作。eth的外部对象和指令揉合在一起。
理论上来说eth也可以支持多种语言,实际上来说solidity一家独大,其中未尝没有eth的opcode转换难度大的原因,而neo vm的opcode则相对友好,支持多种语言开发。当然evm也并非没有好处,solidity支持内联编程,如果熟悉evm的opcode能写出效率非常高的合约。neo在这方面尚未提供支持。
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原理及其实现(转载)的更多相关文章
- JAVA CAS原理深度分析-转载
参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...
- NET/ASP.NET Routing路由(深入解析路由系统架构原理)(转载)
NET/ASP.NET Routing路由(深入解析路由系统架构原理) 阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模 ...
- JMeter使用中遇到的问题:Jmeter Debug - "Unrecognized VM option '+HeapDumpOnOutOfMemoryError"(转载)
转载自 http://www.cnblogs.com/yangxia-test 启动JMeter.bat的程序时,出现以下出错信息: Unrecognized VM option '+HeapDump ...
- mysql 索引原理及查询优化 -转载
转载自 mysql 索引原理及查询优化 https://www.cnblogs.com/panfb/p/8043681.html 潘红伟 mysql 索引原理及查询优化 一 介绍 为何要有索引? ...
- Redis集群的原理和搭建(转载)
转载来源:https://www.jianshu.com/p/c869feb5581d Redis集群的原理和搭建 前言 Redis 是我们目前大规模使用的缓存中间件,由于它强大高效而又便捷的功能,得 ...
- 深入浅出Java并发包—CountDownLauch原理分析 (转载)
转载地址:http://yhjhappy234.blog.163.com/blog/static/3163283220135875759265/ CountDownLauch是Java并发包中的一个同 ...
- JPEG原理详解 (转载)
JPEG算法解密 by jinchao 图片压缩有多重要,可能很多人可能并没有一个直观上的认识,举个例子,一张800X800大小的普通图片,如果未经压缩,大概在1.7MB左右,这个体积如果存放文本 ...
- 弱类型变量原理探究(转载 http://www.csdn.net/article/2014-09-15/2821685-exploring-of-the-php)
N首页> 云计算 [问底]王帅:深入PHP内核(一)——弱类型变量原理探究 发表于2014-09-19 09:00| 13055次阅读| 来源CSDN| 36 条评论| 作者王帅 问底PHP王帅 ...
- DPM(Deformable Parts Model)--原理(一)(转载)
DPM(Deformable Parts Model) Reference: Object detection with discriminatively trained partbased mode ...
随机推荐
- Thinkphp5 Nginx Pathinfo配置
server { listen ; server_name sui.com; root /tmmee/sad.cn/public; index index.php index.html index.h ...
- RocketMQ消息存储
转载:RocketMQ源码学习--消息存储篇 消息中间件—RocketMQ消息存储(一) RocketMQ高性能之底层存储设计 存储架构 RMQ存储架构 上图即为RocketMQ的消息存储整体架构,R ...
- [转]一次CMS GC问题排查过程(理解原理+读懂GC日志)
这个是之前处理过的一个线上问题,处理过程断断续续,经历了两周多的时间,中间各种尝试,总结如下.这篇文章分三部分: 1.问题的场景和处理过程:2.GC的一些理论东西:3.看懂GC的日志 先说一下问题吧 ...
- Java冒泡排序与选择排序
Java排序: 一.冒泡排序算法的运作如下: 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. ...
- java-Object类的解析(持续更新)
1.getClass()方法 public class Object { /*一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用*/ private static native v ...
- angular4-常用指令
ngIf 指令(它与 AngularJS 1.x 中的 ng-if 指令的功能是等价) <div *ngIf="condition">...</div> n ...
- ubuntu 删除开机系统引导,设置快速开机和安静开机
1.隐藏开机选择界面 1.sudo gedit /etc/default/grub GRUB_HIDDEN_TIMEOUT=0 GRUB_HIDDEN_TIMEOUT_QUIET=true //隐藏开 ...
- vuesheng生命周期
对着官网的demo写例子,碰到了生命周期钩子方法,之前只是根据官网的图,了解了大概, 现在忍不住想去深扒一下,因此找了几个博客看了下,受益匪浅,故此笔记: 参考:http://www.cnblogs. ...
- JavaScript+CSS+DIV实现下拉菜单示例
<!DOCTYPE html> <html> <head> <title>下拉菜单示例</title> <script languag ...
- 第六节 静态的(static)和单例模式
main函数 主函数是一个特殊的函数,作为程序的入口,可以被jvm(虚拟器)调用 主函数的定义 public 表示该函数的访问权限是最大的. static 代表主函数随着类的加载就已经存在了. voi ...