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, GetEntryScriptHas
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 结束程序。
以上就是本期的内容讲解,小伙伴们可以参照视频学习,相信会对你有帮助哦~
点此观看视频教程
转载:https://blog.csdn.net/ontologycoding/article/details/101557239