NEO改進協議提案4(NEP-4)

NEOWest發表於2018-12-12

文章目錄

  • 摘要
  • 動機
  • 詳述
    • neo
    • neo-vm
    • 智慧合約示例
  • 原理
  • 向後相容性
  • 實現

摘要

此NEP提案概述了一種機制,通過該機制,智慧合約能夠呼叫直到執行時才知道的其他智慧合約,而不僅限於呼叫在編譯時定義的智慧合約。為了保持智慧合約與未來動態分片過程介面的能力,包括一份用於構建智慧合約的提案詳述會表示智慧合約是否需要動態合約呼叫功能。

動機

此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的更新費用結構。下面列出了更新費用結構的樣例實現。

詳述

該提案概述了Neo專案的三個部分的變更,並提供瞭如何在SC中使用此變更的示例:
•neo
•neo-vm
•neo編譯器
•智慧合約例項
下面列出的變更並非試圖詳盡無遺,而是概述每個庫中所需的重要變更。

neo

為了讓一份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-vm

本詳細提案給NEO虛擬機器新增了一個新OpCode,代表相對於靜態的動態AppCall的使用
DYNAMICCALL = 0xFA
DYNAMICCALL OpCode 在neo.VM.ExecutionEngine.ExecuteOp 方法的執行也按照以下方式不同於現有的APPCALL和TAILCALL OpCodes

                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演示了所提案功能的簡單使用

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收取不同的費用。

向後相容性

本NEP介紹了一套不影響現有SC的新功能。
通過利用現有位元組來指示SC是否需要儲存區或新增額外標記,我們能夠在不影響現有網路協議的情況下維持現有功能和新增該新功能。

實現

• neo-project/neo: github.com/neo-project…
• neo-project/neo-vm: github.com/neo-project…

原文連結: github.com/neo-project…


相關文章