前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >本体技术视点 | Python智能合约终极篇:合约执行引擎API

本体技术视点 | Python智能合约终极篇:合约执行引擎API

作者头像
本体Ontology
发布2019-12-05 17:50:02
5040
发布2019-12-05 17:50:02
举报
文章被收录于专栏:本体研究院本体研究院

01 导语

在前两期的本体技术视点中,我们介绍了跨合约静态调用动态调用,讲述了如何使用 RegisterAppCall API 与 DynamicAppCall API 跨合约调用其他合约的函数。本期将进入本体 Python 智能合约语法专辑的终极篇,探讨如何使用合约执行引擎 API,即 ExecutionEngine API。它包含了3个 API,用法如下:

本期语法难度较大,堪比 Python 智能合约界的九阴真经,学成了你就厉害了!

下面我们具体讲述一下 ExecutionEngine API 的使用方法。在这之前,小伙伴们可以在本体智能合约开发工具 SmartX 中新建一个合约,跟着我们进行操作。同样,在文章最后我们将给出这次讲解的所有源代码以及视频讲解。

图 | 网络

02 ExecutionEngine API 使用方法

使用 ExecutionEngine API 前需要将其引入。这可以通过下面的语句实现上述三个函数的实现:

代码语言:javascript
复制
from ontology.interop.System.ExecutionEngine import GetExecutingScriptHash, GetCallingScriptHash, GetEntryScriptHash

2.1 GetExcutingScriptHash

GetExecutingScriptHash API 最为简单,它的作用是返回当前合约的合约哈希反序,即当前合约账户地址。

代码语言:javascript
复制
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 都会得到不同的脚本哈希,因为合约和函数是不同的调用者。

代码语言:javascript
复制
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的代码如下:

代码语言:javascript
复制
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。

代码语言:javascript
复制
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 结束程序。以上就是本期的内容讲解,小伙伴们可以参照视频学习,相信会对你有帮助哦~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 本体研究院 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
区块链
云链聚未来,协同无边界。腾讯云区块链作为中国领先的区块链服务平台和技术提供商,致力于构建技术、数据、价值、产业互联互通的区块链基础设施,引领区块链底层技术及行业应用创新,助力传统产业转型升级,推动实体经济与数字经济深度融合。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档