solidity基础(4)

  1. solidity的函数修改器
    函数修改器可以方便的控制函数的逻辑,比如可以在某个行为执行前检查某个前置条件,函数修改器还支持继承和重写。可能大家会想,一些检查行为提升为一个语言级特性到底有必要吗?由于整个区块链运行环境是透明的,分布式的,且是图灵完备。为保证其上运行的代码安全,势必存在大量的检查行为,升级为语言特性可以让检查代码复用,看起来也更简洁

    1. 定义
      在实际情况中,我们经常需要对调用者进行一些限制,比如,只能是合约的所有者才能改变归属,我们一起来看一下如何用函数修改器实现这一限制:

      pragma solidity ^0.4.18;
      contract Ownable{
        address public owner = msg.sender;
        //限制只有创建者才能访问
        modifier onlyOwner {
          if (msg.sender != owner) throw;
          _;
        }
        //改变合约的所有者
        function changeOwner(address _newOwner) onlyOwner {
          if (newOwner == 0x0) throw;
          owner = _newOwner;
        }
      }
      

    上述例子中,首先我们通过关键字modifier后接函数修改器名onlyOwner,定义了一个函数修改器。在函数修改器代码块内,判断如果不是合约所有者调用就抛出异常,否则就执行占位符_处代码,_代指的是使用函数修改器的函数体。真正调用changeOwner()函数时,由于使用了函数修改器onlyOwner,会先判断调用者是否为所有者,如果是,才会执行changeOwner()的函数体,允许修改为新所有者
    注意,0.4.0以后,_必须要写为_;](https://github.com/ethereum/solidity/releases/tag/v0.4.0)

    1. 函数修改器支持参数
      函数修改器,支持函数内可以访问的任意符号(包括函数的入参)。直接在函数修改器的参数中传入即可,看一下示例:

      pragma solidity ^0.4.18;
      contract Parameter {
        uint balance =10;
        modifier lowerLimit(uint _balance,uint _withdraw) {
          if(_withdraw < 0 || _withdraw > _balance) throw;
          _;
        }
        //含参数的函数修改器
        function f(uint withdraw) lowerLimit(balance,withdraw) returns(uint) {
          return balance;
        }
      }
      

    在上面的例子中,f()函数,有一个函数修改器lowerLimit(),传入了状态变量参数balance,和入参withdraw,以lowerLimit(balance,withdraw)的方式进行调用。最后函数能否正确执行取决于输入的withdraw值大小

    1. 函数修改器参数支持表达式
      函数修改器的参数支持任意的表达式,只要在上下文中,可见的符号均可在函数修改器中使用。而在函数修改器中引入的符号,在函数中不可见(因为它们可能因为重载而发生改变)

      pragma solidity ^0.4.18;
      contract Test {
        modifier m(uint a) {
          if (a > 0)
          _;
        }
        function add(uint a,uint b) private returns(uint) {
          return a+b;
        }
        function f() m(add(1,1)) returns(uint) {
          return 1;
        }
      }
      


    上例中,我们在函数修改器m中,使用了add()函数

    1. return
      如果函数有返回值,但由于函数修改器判断不成功,那么将返回对应类型的默认值,下面是一个简单的例子:

      pragma solidity ^0.4.18;
      contract Return {
        //函数修改器永远不成功
        modifier A() {
          if (false)
          _;
        }
      
        //返回默认值
        function uintReturn() A returns(uint) {
          uint a = 10;
          return a;
        }
      
        //返回默认值
        function stringReturn() A returns(string) {
          return "hello,world";
        }
      }
      

    上例中,我们写了一个特殊的函数修改器A,永远判断不成功,故而uintReturn(),stringReturn()中的代码都将不会执行。上述函数将分别返回uint,string的默认“零值”
    函数修改器允许return;(类似于break,中断当前流程)。但是不允许明确的return值,即return 1之类,社区正在建议能支持返回值,但是目前(2018/10/22)还不可以

    1. 执行流程
      函数体内,或函数修改器内显示return语句,只会退出当前代码块,即退出{}。返回值将被赋值,但会继续执行_后续的逻辑。下面是一个展示执行流程的示例:

      pragma solidity ^0.4.18;
      contract Test {
        mapping(bool => uint) public map;
      
        modifier A(mapping(bool => uint) map) {
          if (map[true] == 0) {
            map[true] = 1;
            _;
            map[true] = 3;
            }
          }
          function f() A(map) returns(uint) {
            map[true] = 2;
            return map[ttue];
        }
      }
      

    上面的例子,函数先执行f(),然后执行函数修改判断器,接着判断map[true]的值,为0,执行map[true] = 1;,然后执行f()的函数体,执行完后回到A中执行map[true] = 3;,合约执行完毕
    个人认为,函数体执行完后就直接返回了,并不是等到整个合约执行完后再一起返回,所有返回值任然是2,使用数组之类的引用传递,依然没有效果

    1. 多函数修改器
      如果一个函数有多个函数修改器,定义时依次填写,并用空格隔开。函数修改器执行时,将按定义顺序依次执行,下面是一个简单的示例:

      pragma solidity ^0.4.18;
      contract Test {
        address owner = msg.sender;
        //限制只有创建者才能访问
        modifier onlyOwner {
          if (msg.sender != owner) throw;
          _;
        }
        //查看状态
        modifier inSate(bool state) {
          if (!state) throw;
          _;
        }
        //多个函数修改器
        function f(bool state) onlyOwner inSate(state) returns(uint) {
          return 1;
        }
      }
      
    2. 重写
      我们可以重写父类的函数修改器,来改变父类的修改器行为,下面来看一个示例:

      pragma solidity ^0.4.18;
      contract bank{
        modifier transferLimit(uint _withdraw) {
          if (_withdraw > 100) throw;
          _;
        }
      }
      
      contract Test is bank{
        //覆盖的父类的
        modifier transferLimit(uint _withdraw) {
          if (_withdraw > 10) throw;
          _;
        }
        function f(uint withdraw) transferLimit(withdraw) returns(uint) {
          return withdraw;
        }
      }
      


    上例中,我们在子类中覆写了父类的函数修改器,加强了对_withdraw的数量检查,但是覆写的修改器只在子类中有效,在父类中依然按照父类的修改器

    1. 函数修改器区域
      函数修改器区域,是一个全新的特性,目前尚不支持,它允许你同时对多个函数应用函数修改器

      //该段代码是不能执行的
      pragma solidity ^0.4.18;
      contract test {
        modifier inState(bool state) {
          if (currentSate != state) throe;
          _;
        }
        using modifier inSate(state.transfer) {
          function f() returns(uint) {return 2;}
          function g() returns(uint) {return 2;}
        }
        using modifier inSate(state.cleanup) {
          function h() returns(uint) {return 2;}
          function i() returns(uint) {return 2;}
        }
      }
      

    上述代码是将一个表示状态的函数修改器同时应用到多个函数的例子。使用函数修改器区域的好处是将能比较直观知道哪些函数应用了某个函数修改器,但也引入了复杂性,以及由于作者不规范,超大代码带来的不方便阅读代码的问题,
    备注:会出现这样的特性本身说明了传统程序与智能合约程序的不同。由于智能合约强检查性的特点,开始慢慢发展出自己的独特特性

  2. solidity的event事件
    在介绍事件前,我们先明确事件,日志这两个概念。事件发生后被记录到区块链上成为了日志,总的来说,事件强调功能,一种行为;日志强调储存,内容。
    事件是以太坊EVM提供的一种日志基础设施。事件可以用来做操作记录,储存为日志。也可以用来实现一些交互功能,比如通过UI,返回函数调用结果等

    1. 事件
      当定义的事件触发时,我们可以将事件储存到EVM的交易日志中,日志是区块中的一种特殊数据结构。日志与合约关联,与合约的储存合并存入区块链中,只要某个区块可以访问,其相关的日志就可以访问。但在合约中,我们不能直接访问日志和事件数据(即便是创建日志的合约),下面我们来看如何在solidity中实现一个事件:

      pragma solidity ^0.4.18;
      contract Transfer {
        event transfer(address indexed _from,address indexed _to,uint indexed value);
      
        function deposit() payable {
          address current = this;
          uint value = msg.value;
          transfer(msg.sender,current.value);
        }
      
        function getBalance() constant returns(uint) {
          return this.balance;
        }
      }
      

    从上面的例子中,我们使用event关键字定义一个事件,参数列表中为要记录的日志参数的名称及类型

    1. 监听事件
      由于监听事件需要使用web3.js,这里先跳过,在后续学习web3.js时再回头来看
    2. 捡索日志
      • Indexed属性
        可以在事件参数上增加indexed属性,最多可以对三个参数增加这样的属性。加上这个属性,可以允许你在web3.js中通过对加了这个属性的参数进行值过滤
      • 后续基本都涉及到web3.js,如果没有学过的可以先跳一下,学过的,我这里附上原文链接
      • 后续切记补上,因为事件和日志在以太坊中异常重要。因为它是合约之间,合约与终端之间的沟通桥梁。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • solidity的自定义结构体深入详解结构体,solidity中的自定义类型,我们可以使用关键字struct来进行...
    Lnhj阅读 1,106评论 0 0
  • 远山 薄雾 几片闲云 一湾溪水东流 绿松 竹海 白鹤群来 坐卧行止随心
    花少颜阅读 138评论 0 0
  • 如果一开始,赵默笙没有死缠着喜欢何以琛,结局或许会不一样,也许她会继续执着于她的摄影,何以琛或许会与他的妹妹结婚生...
    Dz遇见阅读 248评论 0 1
  • 梦是想着来的 想并不能梦着想 如果梦和想在一起 你一定能成功 梦想
    04a14b4a4742阅读 128评论 0 0