此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…