本體技術視點 | Python智慧合約終極篇:合約執行引擎API

買賣虛擬貨幣
01 導語

在前兩期的本體技術視點中,我們介紹了跨合約靜態呼叫與動態呼叫,講述瞭如何使用 RegisterAppCall API 與 DynamicAppCall API 跨合約呼叫其他合約的函式。本期將進入本體 Python 智慧合約語法專輯的終極篇,探討如何使用合約執行引擎 API,即 ExecutionEngine API。它包含了3個 API,用法如下:

本期語法難度較大,堪比 Python 智慧合約界的九陰真經,學成了你就厲害了!

下面我們具體講述一下 ExecutionEngine API 的使用方法。在這之前,小夥伴們可以在本體智慧合約開發工具 SmartX 中新建一個合約,跟著我們進行操作。同樣,在文章最後我們將給出這次講解的所有原始碼以及影片講解。

02 ExecutionEngine API  使用方法

使用 ExecutionEngine API 前需要將其引入。這可以透過下面的語句實現上述三個函式的實現:

from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash

2.1 GetExcutingScriptHash

GetExecutingScriptHash API 最為簡單,它的作用是返回當前合約的合約雜湊反序,即當前合約賬戶地址。

from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash

def Main(operation, args):
    if operation == "get_contract_hash":
        return get_contract_hash()

    return False

def get_contract_hash():
    return GetExecutingScriptHash()

如圖,右上角 Basic Info 顯示了合約雜湊,左下角控制檯返回了當前合約雜湊的反序。

2.2 GetCallingScriptHash

GetCallingScriptHash API 返回上一級呼叫者,即直接呼叫者的指令碼雜湊,該返回值與合約以及呼叫函式相關。因此不同合約、不同函式呼叫 GetCallingScriptHash 都會得到不同的指令碼雜湊,因為合約和函式是不同的呼叫者。

from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash

def Main(operation, args):
    if operation == "GetCallingScriptHash_test1":
        return GetCallingScriptHash_test1()
    if operation == "GetCallingScriptHash_test2":
        return GetCallingScriptHash_test2()

    return False

def GetCallingScriptHash_test1():
    return GetCallingScriptHash()

def GetCallingScriptHash_test2():
    return GetCallingScriptHash()

如圖所示,GetCallingScriptHash_test1 函式與 GetCallingScriptHash_test2 函式返回了兩個不同的指令碼雜湊。此外,將相同的函式放入不同的合約,也會返回不同的指令碼雜湊。

2.3 GetEntryScriptHash

在智慧合約的呼叫中,有直接呼叫者就有間接呼叫者(跨合約呼叫)。GetEntryScriptHash,它會返回入口(最初)呼叫者的指令碼雜湊。我們準備兩個智慧合約,合約 A 與合約 B,假定合約 A 來呼叫合約 B 的功能函式。

合約B的程式碼如下:

from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash
from ontology.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize

def Main(operation, args):
    if operation == "invokeB":
        return invokeB()
    if operation == "avoidToBeInvokedByContract":
        return avoidToBeInvokedByContract()

    return False

def invokeB():
    # the result of GetCallingScriptHash and GetEntryScriptHash is same
    # if they are invoked by the same contract
    callerHash = GetCallingScriptHash()
    entryHash = GetEntryScriptHash()
    Notify([callerHash, entryHash])
    return [callerHash, entryHash]

def avoidToBeInvokedByContract():
    # the purpose is to prevent hack from other contract
    callerHash = GetCallingScriptHash()
    entryHash = GetEntryScriptHash()
    if callerHash != entryHash:
        Notify(["You are not allowed to invoke this method through contract"])
        return False
    else:
        Notify(["You can implement what you need to do here!"])
        return True

合約 A 的程式碼如下,該合約呼叫合約 B。

from ontology.interop.System.App import RegisterAppCall
from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash
from ontology.interop.System.Runtime import CheckWitness, GetTime, Notify, Serialize, Deserialize

ContractB = RegisterAppCall('0f44f18d37515a917364362ec256c2110a7b1377', 'operation', 'args')

def Main(operation, args):
    if operation == "invokeA":
        opt = args[0]
        return invokeA(opt)
    if operation == "checkHash":
        return checkHash()
    return False

def invokeA(opt):
    callerHash = GetCallingScriptHash()
    entryHash = GetEntryScriptHash()
    Notify(["111_invokeA",callerHash, entryHash])
    return ContractB(opt, [])

def checkHash():
    Notify(["111_checkHash"])
    callerHash = GetCallingScriptHash()
    entryHash = GetEntryScriptHash()
    Notify([callerHash, entryHash])
    return True

如圖,首先執行 checkHash 函式,我們發現在同一個合約的同一個函式中,呼叫GetCallingScriptHash與GetEntryScriptHash API返回值相同,都是"a37ca1f1a3421d36b504769a96c06024a07b2bfa"。這是因為他們既是直接呼叫者,也是最初呼叫者(沒有跨合約呼叫),所以兩個 API 返回值相同。但如果跨合約呼叫呢?

執行合約 A 中的 invokeA 函式。首先還是在同一個合約的同一個函式中,呼叫 GetCallingScriptHash 與 GetEntryScriptHash API。Notify 返回了兩個相同的指令碼雜湊"11540b9836be257a66c7779fec76fd2e8154b706"。接著我們看 return 的值,它們分別是"16fda714afa56165fa9e5c5a6dc18347a17c9e02"以及"11540b9836be257a66c7779fec76fd2e8154b706", 可以發現返回值不再相同。

導致上面這一結果的原因是,在合約 B 中,GetCallingScriptHash 的上一級呼叫者是合約 A 與 invokeB 函式,而 GetEntryScriptHash 的最初呼叫者是來自合約 A 的 invokeA 函式,因此返回值不同。可以看到返回的"11540b9836be257a66c7779fec76fd2e8154b706"與 Notify 返回的值相同。

03 總結

ExecutionEngine API 在防範跨合約呼叫中有廣泛的應用場景,因為不是所有合約都允許被跨合約訪問。因為如果有安全漏洞是非常容易被攻擊的。合約 B 中的 avoidToBeInvokedByContract 函式便提供了一個防止跨合約呼叫的範例,當 GetCallingScriptHash() 與 GetEntryScriptHash() 的返回值不相等時,直接 return false 結束程式。以上就是本期的內容講解,小夥伴們可以參照影片學習,相信會對你有幫助哦~ 

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;