此NEP提案概述了一种机制,经过该机制,智能合约可以调用直到运行时才知道的其余智能合约,而不只限于调用在编译时定义的智能合约。为了保持智能合约与将来动态分片过程接口的能力,包括一份用于构建智能合约的提案详述会表示智能合约是否须要动态合约调用功能。git
此NEP的动机是为让智能合约(SC)的的做者可以为编译时不可知的SC提供接口。例如,操做NEP-5 token的分散交换的SC能够调用在运行时才肯定的token的SC的transferFrom方法。目前,这样的SC须要对全部支持的NEP-5 token地址进行硬编码,并在添加新token时从新发布。以太坊上的许多SC都须要此功能,包括任何符合ERC223 token标准的功能。经过让SC做者可以在运行时指定SC与之交互,相似于更先进的以太坊合约中包含的功能将更容易开发和维护。
值得注意的是,向NEO添加动态SC调用会影响可伸缩性。经过动态SC调用,咱们不用再事先知道将调用哪些其余SC,所以VM状态的子集必须可用于执行才能成功。这使得动态分片更难实现。
为了克服可扩展性的缺点,该提议在区块链上建立时为每一个SC添加一个设定,以表示它是否须要动态调用功能。该设定将容许全部现存的合约和大多数将来合约在预先已知的存储上下文中执行,所以更适合于动态分片,同时还使SC更强大和更具表现力。
考虑到动态调用功能的可扩展性缺陷,该NEP建议了一个须要此功能的SC的更新费用结构。下面列出了更新费用结构的样例实现。github
该提案概述了Neo项目的三个部分的变动,并提供了如何在SC中使用此变动的示例:
•neo
•neo-vm
•neo编译器
•智能合约实例
下面列出的变动并不是试图详尽无遗,而是概述每一个库中所需的重要变动。bash
为了让一份SC表示其是否可以动态调用其余SC,该NEP建议在neo.Core.ContractState对象中添加如下属性,且默认值为false
public bool HasDynamicInvoke
HasDynamicInvoke属性
为了使实现与当前的Neo协议保持互操做,HasDynamicInvoke属性将被序列化为字节标志跟在现有的HasStorage属性以后:网络
[Flags]
public enum ContractPropertyState : byte
{
NoProperty = 0,
HasStorage = 1 << 0,
HasDynamicInvoke = 1 << 1,
}
public class ContractState : StateBase, ICloneable<ContractState>
{
...
public ContractPropertyState ContractProperties;
public bool HasStorage => ContractProperties.HasFlag(ContractPropertyState.HasStorage)
public bool HasDynamicInvoke => ContractProperties.HasFlag(ContractPropertyState.HasDynamicInvoke)
…
public override void Serialize(BinaryWriter writer)
{
base.Serialize(writer);
writer.WriteVarBytes(Script);
writer.WriteVarBytes(ParameterList.Cast<byte>().ToArray());
writer.Write((byte)ReturnType);
writer.Write(ContractProperties); // currently is writer.Write(HasStorage)
writer.WriteVarString(Name);
writer.WriteVarString(CodeVersion);
writer.WriteVarString(Author);
writer.WriteVarString(Email);
writer.WriteVarString(Description);
}
如下在neo.SmartContract.ApplicationEngine中的变动用于计量合约建立不一样的Gas费用。没有额外附加功能的合约建立费用会被下降,而那些HasDynamicInvoke或HasStorage属性为true的合约建立会产生额外的费用。
protected virtual long GetPriceForSysCall()
{
// lines omitted
...
case "Neo.Contract.Create":
case "Neo.Contract.Migrate":
case "AntShares.Contract.Create":
case "AntShares.Contract.Migrate":
long fee = 100L;
ContractState contract = PeekContractState() // this would need to be implemented
if( contract.HasStorage )
{
fee += 400L
}
if( contract.HasDynamicInvoke )
{
fee += 500L;
}
return fee * 100000000L / ratio;
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253复制代码
本详细提案给NEO虚拟机添加了一个新OpCode,表明相对于静态的动态AppCall的使用
DYNAMICCALL = 0xFA
DYNAMICCALL OpCode 在neo.VM.ExecutionEngine.ExecuteOp 方法的执行也按照如下方式不一样于现有的APPCALL和TAILCALL OpCodeside
case OpCode.APPCALL:
case OpCode.TAILCALL:
case OpCode.DYNAMICCALL:
{
if (table == null)
{
State |= VMState.FAULT;
return;
}
byte[] script_hash = null;
if ( opcode == OpCode.DYNAMICCALL )
{
script_hash = EvaluationStack.Pop().GetByteArray();
if ( script_hash.Length != 20 )
{
State |= VMState.FAULT
return;
}
} else {
script_hash = context.OpReader.ReadBytes(20);
}
byte[] script = table.GetScript(script_hash);
if (script == null)
{
State |= VMState.FAULT;
return;
}
if (opcode == OpCode.TAILCALL || opcode == OpCode.DYNAMICCALL)
InvocationStack.Pop().Dispose();
LoadScript(script);
}
break;
123456789101112131415161718192021222324252627282930313233复制代码
neo编译器
将方法调用转换为DYNAMICCALL的示例方法以下:区块链
else if (calltype == CallType.DYNAMICCALL)
{
_ConvertPush(callhash, null, to)
_Convert1by1(VM.OpCode.DYNAMICCALL, null, to);
}
12345复制代码
如下是一个SC演示了所提案功能的简单使用ui
using Neo.SmartContract.Framework.Services.Neo;
namespace Neo.SmartContract
{
public class DynamicTotalSupply : Framework.SmartContract
{
public static int Main(byte[] contract_hash)
{
if( contract_hash.Length == 20 ) {
BigInteger totalSupply = DynamicCall( contract_hash, 'totalSupply')
return totalSupply;
}
return 0;
}
}
}
12345678910111213141516171819复制代码
用动态SC调用来动态分片(以太坊已经提出了许多解决方案)并非不可能的,尽管这会增添已经很困难的任务。
仅仅由于咱们事先知道计算调用图并不意味着咱们可以成功地完美分配资源,没有重叠的子集。仍然可能须要实现分片之间的通讯,如以太坊提案中那样。
考虑到这一点,不向SC添加任何关因而否须要动态调用的元数据并实现动态应用程序调用是有可能的,由于它们能够以相同的方式执行和延申。
可是,即便在能够实现一个系统能够动态SC调用和动态分片这种状况下,本提案仍认为存储HasDynamicInvoke属性在该实现中多是颇有用的。
存储此属性还能使系统对使用HasDynamicInvoke属性发布的SC收取不一样的费用。this
本NEP介绍了一套不影响现有SC的新功能。
经过利用现有字节来指示SC是否须要存储区或添加额外标记,咱们可以在不影响现有网络协议的状况下维持现有功能和添加该新功能。编码
• neo-project/neo: github.com/neo-project…
• neo-project/neo-vm: github.com/neo-project…lua
原文连接: github.com/neo-project…