在区块链上开发可更新的智能合约

2023-11-10

由于区块链不可篡改的特性,智能合约一旦部署在区块链上,其执行的逻辑就无法再更改。长期来看,这个重要的特性反而限制了智能合约的弹性和发展。

接下来要介绍如何设计及部署合约才能让合约在需要时可以更新。但这里的更新意思不是修改已经部署的合约,而是部署新的合约、新的执行逻辑但同时能继续利用已经存在的资料。

首先要知道的是Ethereum Virtual Machine(EVM)要知道如何执行合约的那个函数。合约最后都会被编译成字节码,而你发起一个transaction要执行合约里的某个函数时,交易里的数据同样也是字节码,而不是人看得懂的函数名称。 以一个简单的合约为例:


contract Multiply {
    function multiply(int x, int y) constant returns(int) {
        return x*y; 
    }
}

编译完的二进制码:


6060604052341561000c57fe5b5b60ae8061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633c4308a814603a575bfe5b3415604157fe5b605e60048080359060200190919080359060200190919050506074565b6040518082815260200191505060405180910390f35b600081830290505b929150505600a165627a7a72305820c40f61d36a3a1b7064b58c57c89d5c3d7c73b9116230f9948806b11836d2960c0029

如果你要执行multiply函数,算出8*7等于多少,你的transaction里的数据是 0x3c4308a800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000007 
分成三部分: 第一个是四个字节的3c4308a8,第二和第三个分別是32个字节长的参数,8和7。

3c4308a8是multiply函数的signature(签名,是取函数名称和参数类型使用sha3取前四个byte而得到(不包含0x):


sha3("multiply(int256,int256)"));
//0x3c4308a8851ef99b4bfa5ffd64b68e5f2b4307725b25ad0d14040bdb81e3bafc sha3("multiply(int256,int256)")).substr(2,8);
//3c4308a8

EVM就是靠函数的signature来知道该执行那个函数的。在合约编译完的字节码里查询也能找到这个signature。

接下来要介紹Solidity里的三种调用方式:call、callcode和delegatecall

  • call:一般的调用都是这种方式,执行背景跳到下一个函数的环境(这里的环境是指msg的值和合约的Storage)。如果被调用的是不同合约的函数那么变换成被调用的合约的环境,且msg.sender编程调用者。
  • callcode:和call相同,只是将被调用函数搬到调用者的环境里执行
    假设A合约的x函数用callcode方式调用B合约的y函数,就会在A合约里执行y函数,使用A的参数,所以如果y函数里修改某个参数的值且这个参数的名称刚好和A的某个参数名称一致,则A的该参数就会被修改。就把它想像成A多了一个y函数并执行。
  • delegatecall:和callcode相同,都是把被调用的函数搬到调用者的环境里执行,只是在msg.sender的值上有区别。
    来看一个例子:加入A合约用delegatecall的方式调用B合约的函数,B合约的函数接下用callcode或call的方式调用C合约的函数,那么函数里看到的msg.sender会是B;但如果B改用delegatecall的方式调用C合约的函数的话,那么函数里看到的msg.sender会是A。就把它想像成把msg相关的值保持不变传递下去就ok了。

接下来实际来看一下delegatecall的效果:

contract Plus {
    int z;
    function plus(int x, int y) {
        z = x+y;
    }
}
contract Multiply {
    int public z;
    function multiply(int x, int y) {
        z = x*y;
    }
    function delegateToPlus(address _plus, int x, int y) {
        _plus.delegatecall( bytes4(sha3("plus(int256,int256)")) ,x ,
        y);
    }
}

部署并按顺序执行Multiply的multiply和delegateToPlus并观察z值的变化:

可以看到执行delegatecall之后z的值变成是8+7。 所以如果要让我们未来可以改变执行逻辑的话怎么写代码呢?

contract Plus {
    int z;
    function plus(int x, int y) { //sig:"0xccf65503"
        z = x+y;
    }
}
contract Multiply {
    int z;
    function multiply(int x, int y) { //sig:"0x3c4308a8"
        z = x*y;
    }
}
contract Main {
    int public z;
    function delegateCall(address _dest, bytes4 sig, int x, int y) {
        _dest.delegatecall(sig, x , y);
    }
}

我们将合约的地址和函数的signature当做参数传递给delegateCall去执行,假设原本是用Plus合约的执行路基,现在我们更新成Multiply合约:

0x4429 是Plus合约的地址, 0xe905 是Multiply合约的地址。
我们以后只要给它改变后的函数signature和合约地址就可以使用新的执行逻辑了!

但如果合约不是只给一个人使用的话,应当在更新合约的時候所有参与的人都必须要更新新合约的位置。这时候可以用一个合约来帮助我们导到新的合约位置,就像路由器似的,我们统一发送(还是以delegatecall的形式)到路由合约,再由路由合约帮我们导到正确的位置,未来更新合约就只需要更新路由合约的资料即可。

contract Upgrade {
    mapping(bytes4=>uint32) returnSizes;
    int z;
    
    function initialize() {
        returnSizes[bytes4(sha3("get()"))] = 32;
    }
    
    function plus(int _x, int _y) {
        z = _x + _y;
    }
    function get() returns(int) {
        return z;
    }
}
contract Dispatcher {
    mapping(bytes4=>uint32) returnSizes;
    int z;
    address upgradeContract;
    address public dispatcherContract;
    function replace(address newUpgradeContract) {
        upgradeContract = newUpgradeContract;
        upgradeContract.delegatecall(bytes4(sha3("initialize()")));
    }
    function() {
        bytes4 sig;
        assembly { sig := calldataload(0) }
        var len = returnSizes[sig];
        var target = upgradeContract;
        
        assembly {
            calldatacopy(mload(0x40), 0x0, calldatasize)
            delegatecall(sub(gas, 10000), target, mload(0x40),
                         calldatasize, mload(0x40), len)
            return(mload(0x40), len)
        }
    }
}
contract Main {
    mapping(bytes4=>uint32) public returnSizes;
    int public z;
    address public upgradeContract;
    address public dispatcherContract;
    
    function deployDispatcher() {
        dispatcherContract = new Dispatcher();
    }
    
    function updateUpgrade(address newUpgradeContract) {
        dispatcherContract.delegatecall(
            bytes4( sha3("replace(address)")), newUpgradeContract
        );
    }
    
    function delegateCall(bytes4 _sig, int _x, int _y) {
        dispatcherContract.delegatecall(_sig, _x, _y);
    }
    
    function get() constant returns(int output){
        dispatcherContract.delegatecall(bytes4( sha3("get()")));
        assembly {
            output := mload(0x60)
        }
    }
}

执行顺序:
1. 执行Main.deployDispatcher() 部署路由合约
2. 部署upgrade合约并将其address当做Main.updateUpgrade()的参数传入用来更新upgrade合约的地址资料。
3. 执行Main.delegateCall(),参数是plus(int256,int256)的signature和任意两个值。
4. 执行Main.get(),由delegatecall去调用upgrade合约的get函数,回传相加完的z值。因为是delegatecall,所以这个z值其实是Main合约自己的,upgrade合约的z值是零。

如果delegatecall调用的函数有返回值的话,必须要用assembly来手动获得返回值,因为delegatecall和call一样,只会回传true of false来代表执行是否成功。Dispatcher在调用是同样也是用assembly code。
但因为是用assembly手动获得返回值,因此前提是返回值的长度必须是固定且已知的,所以当我们在步骤2更新upgrade合约时,Dispatcher合约同时去调用upgrade合约的initialize()函数,upgrade合约在initialize函数里将它所有会有返回值的函数的返回值大小写入returnSizes中,之后如果调用具有返回值的函数时,Dispatcher就知道返回值的大小了

這个还有一个重点是参数定义的顺序
因为合约执行要用参数值的时候,它会到对应的Storage位置去找。所以如果你的合约参数定义像這樣子
upgrade:
int x
int y
 — — — — 
Dispathcer:
int x
int y
 — — — — 
Main:
int x
int abc
int y
当upgrade合约的函数需要用到x和y的值的时候,它会找不到y,因为Storage是Main的。

分享两个教程和一些免费资料给读者:

一个适合区块链新手的以太坊DApp开发教程:

http://xc.hubwiz.com/course/5a952991adb3847553d205d1

一个用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台:

http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd6

收集整理了一些免费区块链、以太坊技术开发相关的文件,有需要的可以下载,文件链接:

1. web3.js API官方文档中文版:https://pan.baidu.com/s/1hOV9hEzi7hFxJCL4LTvC6g
2. 以太坊官方文档中文版     :https://pan.baidu.com/s/1ktODJKLMBmkOsi8MPrpIJA
3. 以太坊白皮书中文版       :https://pan.baidu.com/s/1bzAFnzJ35hlQxJ2J4Oj-Ow
4. Solidity的官方文档中文版 :https://pan.baidu.com/s/18yp9XjEqAHpiFm2ZSCygHw
5. Truffle的官方文档中文版  :https://pan.baidu.com/s/1y6SVd7lSLUHK21YF5FzIUQ
6. C#区块链编程指南         :https://pan.baidu.com/s/1sJPLqp1eQqkG7jmxqwn3EA
7. 区块链技术指南          :https://pan.baidu.com/s/13cJxAa80I6iMCczA04CZhg
8. 精通比特币中文版        :https://pan.baidu.com/s/1lz6te3wcQuNJm28rFvBfxg
9. Node.js区块链开发        :https://pan.baidu.com/s/1Ldpn0DvJ5LgLqwix6eWgyg
10. geth使用指南文档中文版   :https://pan.baidu.com/s/1M0WxhmumF_fRqzt_cegnag
11. 以太坊DApp开发环境搭建-Ubuntu   : https://pan.baidu.com/s/10qL4q-uKooMehv9X2R1qSA
12. 以太坊DApp开发环境搭建-windows  :https://pan.baidu.com/s/1cyYkhIJIFuI2oyxM9Ut0eA
13. 以太坊DApp开发私链搭建-Ubuntu   : https://pan.baidu.com/s/1aBOFZT2bCjD2o0EILBWs-g
14. 以太坊DApp开发私链搭建-windows  :https://pan.baidu.com/s/10Y6F1cqUltZNN99aJv9kAA


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在区块链上开发可更新的智能合约 的相关文章

  • 荐读

    本文转自 链闻 ChainNews 作者 Karen 虽然区块链技术诞生至今不过短短十余年 但是东西方天然的文化差异在加密世界中同样留下了一些痕迹 当社交媒体上关于 东方区块链 只关心币价 利益和投机而只有 西方区块链 才关心底层技术的革新
  • [区块链安全-CTF Protocol]区块链智能合约安全实战(已完结)

    区块链安全 CTF Protocol 区块链智能合约安全实战 前言 1 The Lost Kitty 2 RootMe 3 Trickster 4 The Golden Ticket 5 Smart Horrocrux 6 Gas Valv
  • 浅析『链上数据分析』 : 区块链 + 数据分析

    什么是链上数据分析 01 区块链 02 链上数据 03 为什么要分析链上数据 04 数据分析思维 05 数据分析技能 06 数据分析工具 07 业务逻辑理解 什么是链上数据分析 链上数据分析 顾名思义 就是对区块链上的数据进行分析 其实就是
  • 期货开户手续费的组成和收费模式

    期货公司的费用 公开透明 不会私自提高费率 A级别的期货公司 不会私自提高期货投资者的交易费率 不会私自提高期货投资者的保证金比例 说一下投资者最关系的费率问题 期货投资交易的手续费是由两部分组成 1 交易所手续费 这个是固定标准也是市场上
  • 区块链程序要怎么去测试?

    区块链程序开发越来越多 区块链怎么做测试 让大家很好奇 与传统测试不同的是区块链系统边界模糊 不管是是独立的应用程序 还是客户端 服务器模式的应用程序 传统软件都具备明显的系统边界 可以通过UI用户界面或者客户端去进行测试 区块链底层则不同
  • 区块链的安全性与去中心化特点:深入探讨区块链技术的安全性和去中心化特点

    摘要 本文将深入探讨区块链技术的两个核心特点 安全性和去中心化 区块链作为一种分布式账本技术 通过其独特的安全性和去中心化特点 在许多领域引起了广泛关注 我们将分析区块链的安全性原理和其与去中心化的关系 以及区块链技术在保护数据安全和提供信
  • 如何使用区块链技术保护个人隐私和数据安全

    区块链技术是一种分布式账本技术 它具有不可篡改 去中心化 透明度高等特点 区块链技术能够实现数据的可信存证 隐私保护和交易安全 并且能够通过智能合约的自动执行 因此被广泛应用于金融 电商 物流 社交网络等领域 区块链技术的核心是 分布式账本
  • 交易的本质 什么样的信仰,决定什么样的交易人生

    什么样的信仰 决定什么样的生活 同样 什么样的理念 也决定了什么样的交易 多数的交易员都把能否在交易市场稳定化盈利归结于自己从事这个行业的时间 通常很多人会说五年入门 十年小成等等 好像只要坚持个五年十年就能够在交易市场找到自己的位置 你信
  • 就现在!为元宇宙和Web3对互联网的改造做准备!

    欢迎来到Hubbleverse 关注我们 关注宇宙新鲜事 预计阅读时长 8分钟 本文仅代表作者个人观点 不代表平台意见 不构成投资建议 如今 互联网是各种不同的网站 应用程序和平台的集合 由于彼此分离 它们缺乏互操作性和数据可移植性 因此
  • 以太坊执行miner.start返回null( 转载)

    博文地址 http blog csdn net wo541075754 article details 78735711
  • 【区块链与密码学】第2-3讲:区块链基础技术大剖析之哈希函数

    本课堂内容全部选编自PlatON首席密码学家 武汉大学国家网络安全学院教授 博士生导师何德彪教授的 区块链与密码学 授课讲义 教材及互联网 版权归属其原作者所有 如有侵权请立即与我们联系 我们将及时处理 2 4 1 哈希函数 区块链作为一个
  • Cumulus Encrypted Storage System(CESS)激励测试网 v0.7.5 于11月29日正式上线

    Cumulus Encrypted Storage System CESS 是基于区块链的去中心化云存储网络和 CDN 网络 支持数据在线存储和实时共享 为 Web3 高频动态数据的存储和检索提供全栈解决方案 CESS 数据价值网络是以 D
  • 新加坡社区领袖卓顺发的荣誉与大爱精神

    2023年11月24日 善济医社义务执行主席卓顺发太平绅士JP BBM L PVPA受邀出席内政部主办的答谢活动2023 主宾为内政部长兼律政部长尚穆根先生 Mr K Shanmugam 2018年起 卓顺发受委为太平绅士后 应内政部邀请
  • 凌晨!比特币突破4万美元,本轮上涨分析与未来展望!

    比特币在上周五 1日 成功突破38000美元大关后 就持续震荡走高 在今晨06 30左右 更是一举突破4万大关 最高来到41750美元 再度刷下2023年新高 以太坊 ETH 在差不多同样时间 最高来到2265美元 刷新年内新高 近24小时
  • NFTScan | 11.27~12.03 NFT 市场热点汇总

    欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总 周期 2023 11 20 2023 11 26 NFT Hot News 01 Web3 教育平台 Open Campus 获 Binance Labs
  • Sui生态DeFi将参加Builder DAO举办的活动,为期三天畅谈如何Build on Sui

    LeadUp the Night是一个定期举办的MeetUp活动 由Builder DAO邀请区块链各方项目开发者 VC担任本活动的讲师 这个活动旨在促进区块链技术的发展和应用 让参与者有机会开发创新的区块链应用 探索区块链技术的潜力 12
  • 股指期权开通要什么条件?

    股指期权是一种金融衍生工具 它赋予持有者在未来某一特定日期按照约定的价格买入或卖出标的资产的权利 对于投资者来说 开通股指期权账户需要满足一定的条件 那么股指期权开通要什么条件 本文来自 财顺期权 开通股指期权账户需要的条件是 申请前20个
  • 一文了解Substrate

    Substrate是用于构建特定类型区块链的工具箱 它本身并不是一个区块链 而是开发者用来创建独特而强大的区块链的一套工具 Substrate允许开发者可以自己创建所需所想功能的区块链 无需受限于现有的设计 Substrate使用FRAME
  • 期货和期权哪个风险更大?有哪些相同和不同的地方?

    期货和期权这两个都是高风险 高收益的投资工具是两个相同的部分 而期货呢是保证金交易理论上亏损是无限的 而期权呢买入期权的话最大亏损也就是权利金 这个是两者的最大区别 下文科普期货和期权哪个风险更大 有哪些相同和不同的地方 本文来自 期权酱
  • 期权开户最低多少钱个人可以开?

    大家新年好 现在2024年龙年 很多想玩期权的小伙伴都知道期权开通需要50万的门槛 那么今天来给大家详细介绍下期权开户最低多少钱个人可以开 本文将对期权开户最低金额进行探讨 帮助投资者更好地了解期权交易的投资门槛 本文来自 期权酱 一 期权

随机推荐