以太坊智能合约设计模式

2023-11-13

设计模式是许多开发场景中的首选解决方案,本文将介绍五种经典的以太坊智能合约设计模式并给出以太坊solidity实现代码:自毁合约、工厂合约、名称注册表、映射表迭代器和提款模式。

1、自毁合约

合约自毁模式用于终止一个合约,这意味着将从区块链上永久删除这个合约。 一旦被销毁,就不可能调用合约的功能,也不会在账本中记录交易。

现在的问题是:“为什么我要销毁合约?”。

有很多原因,比如某些定时合约,或者那些一旦达到里程碑就必须终止的合约。 一个典型的案例是贷款合约,它应当在贷款还清后自动销毁;另一个案例是基于时间的拍卖合约,它应当在拍卖结束后终止 —— 假设我们不需要在链上保存拍卖的历史记录。

在处理一个被销毁的合约时,有一些需要注意的问题:

  • 合约销毁后,发送给该合约的交易将失败
  • 任何发送给被销毁合约的资金,都将永远丢失

为避免资金损失,应当在发送资金前确保目标合约仍然存在,移除所有对已销毁合约的引用。

现在我们来看看代码:

contract SelfDesctructionContract {
   public address owner;
   public string someValue;
   modifier ownerRestricted {
      require(owner == msg.sender);
      _;
   } 
   // constructor
   function SelfDesctructionContract() {
      owner = msg.sender;
   }
   // a simple setter function
   function setSomeValue(string value){
      someValue = value;
   } 
   // you can call it anything you want
   function destroyContract() ownerRestricted {
     suicide(owner);
   }
}

正如你所看到的, destroyContract()方法负责销毁合约。

请注意,我们使用自定义的ownerRestricted修饰符来显示该方法的调用者,即仅允许合约的拥有者 销毁合约。

2、工厂合约

工厂合约用于创建和部署“子”合约。 这些子合约可以被称为“资产”,可以表示现实生活中的房子或汽车。

工厂用于存储子合约的地址,以便在必要时提取使用。 你可能会问,为什么不把它们存在Web应用数据库里?

这是因为将这些地址数据存在工厂合约里,就意味着是存在区块链上,因此更加安全,而数据库的损坏可能会造成资产地址的丢失,从而导致丢失对这些资产合约的引用。 除此之外,你还需要跟踪所有新创建的子合约以便同步更新数据库。

工厂合约的一个常见用例是销售资产并跟踪这些资产(例如,谁是资产的所有者)。 需要向负责部署资产的函数添加payable修饰符以便销售资产。 代码如下:

contract CarShop {
   address[] carAssets;
   function createChildContract(string brand, string model) public payable {
      // insert check if the sent ether is enough to cover the car asset ...
      address newCarAsset = new CarAsset(brand, model, msg.sender);            
      carAssets.push(newCarAsset);   
   }
   function getDeployedChildContracts() public view returns (address[]) {
      return carAssets;
   }
}

contract CarAsset {
   string public brand;
   string public model;
   address public owner;
   function CarAsset(string _brand, string _model, address _owner) public {
      brand = _brand;
      model = _model;
      owner = _owner;
   }
}   

代码address newCarAsset = new CarAsset(...)将触发一个交易来部署子合约并返回该合约的地址。 由于工厂合约和资产合约之间唯一的联系是变量address[] carAssets,所以一定要正确保存子合约的地址。

3、名称注册表

假设你正在构建一个依赖与多个合约的DApp,例如一个基于区块链的在线商城,这个DApp使用了ClothesFactoryContract、GamesFactoryContract、BooksFactoryContract等多个合约。

现在想象一下,将所有这些合约的地址写在你的应用代码中。 如果这些合约的地址随着时间的推移而变化,那该怎么办?

这就是名称注册表的作用,这个模式允许你只在代码中固定一个合约的地址,而不是数十、数百甚至数千个地址。它的原理是使用一个合约名称 => 合约地址的映射表,因此可以通过调用getAddress("ClothesFactory")从DApp内查找每个合约的地址。 使用名称注册表的好处是,即使更新那些合约,DApp也不会受到任何影响,因为我们只需要修改映射表中合约的地址。

代码如下:

contract NameRegistry {
   struct ContractDetails {
      address owner;
      address contractAddress;
      uint16 version;
   }
   mapping(string => ContractDetails) registry;
   function registerName(string name, address addr, uint16 ver) returns (bool) {
      // versions should start from 1
      require(ver >= 1);
      
      ContractDetails memory info = registry[name];
      require(info.owner == msg.sender);
      // create info if it doesn't exist in the registry
       if (info.contractAddress == address(0)) {
          info = ContractDetails({
             owner: msg.sender,
             contractAddress: addr,
             version: ver
          });
       } else {
          info.version = ver;
          info.contractAddress = addr;
       }
       // update record in the registry
       registry[name] = info;
       return true;
   }
    function getContractDetails(string name) constant returns(address, uint16) {
      return (registry[name].contractAddress, registry[name].version);
   }
}

你的DApp将使用getContractDetails(name)来获取指定合约的地址和版本。

4、映射表迭代器

很多时候我们需要对一个映射表进行迭代操作 ,但由于Solidity中的映射表只能存储值,并不支持迭代,因此映射表迭代器模式非常有用。 需要指出的是,随着成员数量的增加,迭代操作的复杂性会增加,存储成本也会增加,因此请尽可能地避免迭代。

实现代码如下:

contract MappingIterator {
   mapping(string => address) elements;
   string[] keys;
   function put(string key, address addr) returns (bool) {
      bool exists = elements[key] == address(0)
      if (!exists) {
         keys.push(key);
      }
      elements[key] = addr;
      return true;
    }
    function getKeyCount() constant returns (uint) {
       return keys.length;
    }
    function getElementAtIndex(uint index) returns (address) {
       return elements[keys[index]];
    }
    function getElement(string name) returns (address) {
       return elements[name];
    }
}

实现put()函数的一个常见错误,是通过遍历来检查指定的键是否存在。正确的做法是elements[key] == address(0)。虽然遍历检查的做法不完全是一个错误,但它并不可取,因为随着keys数组的增长,迭代成本越来越高,因此应该尽可能避免迭代。

5、提款模式

假设你销售汽车轮胎,不幸的是卖出的所有轮胎出问题了,于是你决定向所有的买家退款。

假设你跟踪记录了合约中的所有买家,并且合约有一个refund()函数,该函数会遍历所有买家并将钱一一返还。

你可以选择 - 使用buyerAddress.transfer()或buyerAddress.send() 。 这两个函数的区别在于,在交易异常时,send()不会抛出异常,而只是返回布尔值false ,而transfer()则会抛出异常。

为什么这一点很重要?

假设大多数买家是外部账户(即个人),但一些买家是其他合约(也许是商业)。 假设在这些买方合约中,有一个合约,其开发者在其fallback函数中犯了一个错误,并且在被调用时抛出一个异常,fallback()函数是合约中的默认函数,如果将交易发送到合同但没有指定任何方法,将调用合约的fallback()函数。 现在,只要我们在refund函数中调用contractWithError.transfer() ,就会抛出异常并停止迭代遍历。 因此,任何一个买家合约的fallback()异常都将导致整个退款交易被回滚,导致没有一个买家可以得到退款。

虽然在一次调用中退款所有买家可以使用send()来实现,但是更好的方式是提供withdrawFunds()方法,它将单独按需要退款给调用者。 因此,错误的合约不会应用其他买家拿到退款。

实现代码如下:

contract WithdrawalContract {
   mapping(address => uint) buyers;
   function buy() payable {
      require(msg.value > 0);
      buyers[msg.sender] = msg.value;
   }
   function withdraw() {
      uint amount = buyers[msg.sender];
      require(amount > 0);
      buyers[msg.sender] = 0;      
      require(msg.sender.send(amount));
   }
}

如果你希望高效的学习以太坊DApp开发,可以访问汇智网提供的最热门在线互动教程:

其他更多内容也可以访问这个以太坊博客

转载于:https://my.oschina.net/u/2275217/blog/1806362

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

以太坊智能合约设计模式 的相关文章

  • ssm框架+Layui整合案例

    业余写得整合案例 想学习的可以来参考 初来乍到 准备工作 Layui tomcat mysql 目录 1 实现的效果图 2 实现代码 2 0 前端代码 2 1 登录页面login jsp 2 2 登录后跳转的主页面 main jap web
  • SQLi LABS Less-22

    第22关使用POST请求提交参数 对账号和密码中的特殊字符执行了转译的操作 难度较大 这一关的重点在于Cookie 用户登录成功后 将base64编码后的用户名保存到Cookie中 点击提交按钮时 会从Cookie中获取用户名 使用base
  • Yum update和upgrade的区别

    Yum update和upgrade的区别 Linux yum中package升级命令有两个分别是 yum upgrade 和 yum update 1 区别 默认情况下 yum update和yum upgrade的功能是完全一样的 都是
  • 基于.NET CORE 3.1的WEB API通过EF CORE连接MySQL

    基于 NET CORE 3 1的WEB API通过EF CORE连接MySQL 注 本文不采用CodeFirst 不使用迁移 1 准备好一个WEB API项目 可以看我之前的文章 2 准备好一个MySQL数据库并创建表 3 引用nuget包
  • 神经网络学习小记录44——训练资源汇总贴

    神经网络学习小记录44 训练资源汇总贴 前言 权值文件 1 迁移学习 传统神经网络 2 目标检测 a keras权重 b pytorch权重 c tensorflow2权重 3 实例分割 4 语义分割 旧版 5 语义分割 新版 a kera
  • nginx请求返回html文件,nginx返回json或者文本格式的方法

    用nginx怎么返回json格式或者文本格式的数据 其实很简单 如下代码 1 返回文本格式 location get text default type text html return 200 hello world 2 返回json格式
  • sublime配置go环境_Win10下sublime text3搭建go语言开发环境--工具篇

    进行 go 语言开发环境的搭建 最近进行了大量的搜索 因为在搭建的过程中遇到了挺多的问题 先介绍搭建的环境 系统 Win10 IDE sublime text3 相关插件 GoSublime 这篇文会介绍如下几个部分 1 下载Golang
  • 知道 Redis RDB 这些细节,可以少踩很多坑

    在使用 Redis 的过程中 你是否遇到过下面这些问题 开启 RDB 落盘 业务频繁出现请求超时 除了 save 和 bgsave 命令 还有哪些操作会触发 RDB 落盘 执行了 flushall 发现 flushall 之前写的数据又冒出
  • 根据传入的年份和月份获取该月属于本年的第几周和每周的开始和结束日期

    function getInfo year month var getInfo function year month var d new Date d setFullYear year month 1 1 var w1 d getDay
  • unity学习笔记-unity(2019)实现与as相互跳转

    Unity学习笔记 Unity 2019 嵌入安卓开发 实现相互跳转 思路 流程 先在unity中添加跳转到安卓的方法 AS配置unity的信息 2021 5 27更新一下 as添加跳转至unity的方法 as添加unity跳转到app的方
  • 8086CPU只有16位寄存器,却可以访问20位的物理地址

    一 背景介绍 Intel 8086是一个由Intel于1978年所设计的16位微处理器芯片 是x86架构的鼻祖 它是以8080和8085的设计为基础 拥有类似的寄存器组 但是数据总线扩充为16位 总线界面单元 Bus Interface U
  • javafx实现登录注册界面

    package sample import JavaBigJob BaseScene import javafx scene Parent import javafx scene layout Pane import javafx appl
  • 前端学习总结:5、Bootstrap

    前端学习总结 5 Bootstrap 文章目录 前端学习总结 5 Bootstrap 1 前言 2 资料 3 下载安装 Bootstrap 4 Bootstrap css 按钮 5 Bootstrap css 表格 6 Bootstrap
  • ZYNQ PL开发流程

    2 ZYNQ PL开发 开发流程 开发使用vivado 流程如下 1 新建工程 工程项目含义 这里简单介绍下各个工程类型的含义 RTL Project 是指按照正常设计流程所选择的类型 这也是常用的一种类型 RTL Project 下的 D
  • 《大话数据结构》-程杰 读书笔记

    认为程序设计的实质是对确定的问题选择一种好的结构 加上设计一种好的算法 可见 数据结构在程序设计当中占据了重要的地位 程序设计 数据结构 算法 要你相信自己一定可以学得会 学得好 既然无数人已经掌握了 你凭什么不行 于每个链表来说 它所占用
  • CentOS中安装docker-compose

    下载安装包 wget https github com docker compose releases download v2 2 3 docker compose linux x86 64 移动到 usr local bin 目录 mv
  • 【VUE3源码学习】nextTick 实现原理

    什么是nextTick 定义 在下次 DOM 更新循环结束之后执行延迟回调 在修改数据之后立即使用这个方法 获取更新后的 DOM 看完这个定义不免心生疑问 下次 DOM 更新循环结束之后是什么时候 执行延迟回调 更新后的 DOM 基于以上问
  • 使用 MMDETECTION 和 LABEL-STUDIO 进行半自动化目标检测标注

    标注数据是一个费时费力的任务 本文介绍了如何使用 MMDetection 中的 RTMDet 算法联合 Label Studio 软件进行半自动化标注 具体来说 使用 RTMDet 预测图片生成标注 然后使用 Label Studio 进行
  • Istio服务网格详解

    一 架构的发展历史 发展历史时间轴 1 单机小型机时代 第一个计算机网络诞生于1969年 也就是美军的阿帕网 阿帕网能够实现与其它计算机进行联机操作 但是早期仅仅是为了军事目的而服务 2000年初 中国的网民大约890万 很多人都不知道互联
  • Deepin设置接受从Windows的远程桌面连接

    信息化时代 网络越来越普及 电脑越来越多 经常会有使用远程桌面操作多台电脑 PC 服务器 虚拟机 云服务器等等 的情形 例如用远程桌面连接Windows服务器 台式机 Xshell连接Linux服务器 台式机 还有更多的工具 包括QQ远程协

随机推荐

  • 推多米诺

    LeetCode 838 推多米诺 一行中有 N 张多米诺骨牌 我们将每张多米诺骨牌垂直竖立 在开始时 我们同时把一些多米诺骨牌向左或向右推 每过一秒 倒向左边的多米诺骨牌会推动其左侧相邻的多米诺骨牌 同样地 倒向右边的多米诺骨牌也会推动竖
  • Java-对象转型(casting)

    对象转型 casting 一个基类的引用类型变量可以指向其子类的对象 一个基类的引用不可以访问其子类对象新增加的方法 属性 方法 可以使用 引用 变量instanceof类名 来判断该引用型变量所指向的对象是否属于该类或该类的子类 子类的对
  • 线程间通信方式

    参考视频 实现一个场景 三个线程分别交替打印A B C 打印100次 基于synchronized 锁的实现方式 public class test private static Integer num 0 public static vo
  • 玩转Mysql系列 - 第18篇:流程控制语句(高手进阶)

    这是Mysql系列第18篇 环境 mysql5 7 25 cmd命令中进行演示 代码中被 包含的表示可选 符号分开的表示可选其一 上一篇存储过程 自定义函数 对存储过程和自定义函数做了一个简单的介绍 但是如何能够写出复杂的存储过程和函数呢
  • HAproxy的负载均衡与keepalive+haproxy实现高可用

    目录 实验环境 一 内核配置 二 编译安装 编译前先产看内核版本 三 建立配置文件 四 添加为系统服务 五 配置web1 web2 高可用实验环境 一 安装keepalived 二 配置keepalviled 三 配置haproxy 四 验
  • Auto-GPT部署教程

    今天介绍下如何部署Auto GPT 官网已经写的很好了 分享下我的部署过程和踩到的坑 拉取代码 github仓库 GitHub Significant Gravitas Auto GPT An experimental open sourc
  • 新Windows配置C#开发环境(.NET Core)

    新Windows配置C 开发环境 NET Core 平时需要使用C 进行开发 但是又不想下载VS VS的体积太大 电脑跑起来比较吃力 所以直接下载 NET Core来配置C 的开发环境 体积小 而且跟vscode一起使用基本上够用了 下面就
  • python实训 俄罗斯方块 面向对象编程MVC模式

    前端演示 viewtest py from tkinter import from random import import threading from tkinter messagebox import showinfo from tk
  • Weblogic未授权远程代码执行漏洞 (CVE-2023-21839)

    前言 Weblogic 允许远程用户在未经授权的情况下通过IIOP T3进行JNDI lookup 操作 当JDK版本过低或本地存在javaSerializedData时 这可能会导致RCE漏洞 0x00 环境设置 此次实验 我们使用P神的
  • 华为OD机试 - 增强的strstr(Java)

    题目描述 C 语言有一个库函数 char strstr const char haystack const char needle 实现在字符串 haystack 中查找第一次出现字符串 needle 的位置 如果未找到则返回 null 现
  • 【工作记录】MQTT介绍、安装部署及springboot集成@20230912

    背景 近期公司可能会有物联网设备相关项目内容 提前对用到的mqtt协议做预研和初步使用 最初接触到mqtt协议应该是早些年的即时通讯吧 现在已经是物联网设备最热门的协议了 作为记录 也希望能帮助到需要的朋友 MQTT介绍 MQTT 协议规范
  • windows环境下snort的安装

    windows环境下snort的安装 snort 一 windows环境下snort的安装 1 MYSQL安装 2 apache安装 3 php安装 4 配置PHP 5 安装配置adodb 6 安装配置acid 7 安装WinPcap 4
  • 从零实现DevOps(八):Jenkins结合Docker私服实现远程自动部署

    从零实现DevOps 八 Jenkins结合Docker私服实现远程自动部署 在上一篇文章 从零实现DevOps 七 Jenkins GitLab Docker部署SpringBoot项目 的总结部分 我们提出了一个疑问 就是项目的安全性和
  • 出现AttributeError: type object ‘object‘ has no attribute ‘dtype‘的另一种问题

    首先 出现AttributeError type object object has no attribute dtype 错误 可能是pandas或numpy版本问题 解决办法可见下文 https blog csdn net XYmush
  • my97DatePicker日期控件——日期输入框联动,使用focus使第二个输入框没展示出日期控件...

    描述问题场景 1 jquery使用的版本是jquery 1 7 2 min js 2 代码不是写在页面上的 是通过事件后追加的 1 2 3 4 5 6 7
  • 转行软件测试四个月学习,第一次面试经过分享

    我是去年上半年从销售行业转行到测试的 从销售公司辞职之后选择去培训班培训软件测试 经历了四个月左右的培训 在培训班结课前两周就开始投简历了 在结课的时候顺利拿到了offer 在新的公司从事软件测试工作已经将近半年有余 回想当时面试的过程 想
  • 我遇到的DSO missing from command line

    首先 使用C 代码编译了一个静态链接库libmy a 然后在另外一个C语言的项目中去调用此静态库 结果出现如下错误 usr bin ld libmy a message mm o undefined reference to symbol
  • Axure8下载和安装

    1 axureRP 和墨刀的选择 原型图工具有axure 墨刀 摩客 Mockplus xiaopiu Sketch等 其中现在最常用的原型图工具主要包括axure和墨刀 axure作为老牌的原型图工具 功能最齐全 交互最多样 基本任何想要
  • 【从零开始的Java开发】2-9-1 JavaScript入门:语法、JS对表单元进行设置、事件与DOM

    文章目录 页面使用JS的方法 相关语法 变量 调试 自定义函数 数据类型及类型转换 变量作用域 运算符 程序控制语句 内置函数 数组 JS对表单元进行设置 单击与刷新 单选按钮 列表框 列表框 刷新后默认选中间的值 与 修改对应月份的日期
  • 以太坊智能合约设计模式

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 设计模式是许多开发场景中的首选解决方案 本文将介绍五种经典的以太坊智能合约设计模式并给出以太坊solidity实现代码 自毁合约 工厂合约 名称注册表 映射表迭代器和提款模