以太坊之Truffle 2.0版本升级3.0版本的集成指南

举报
Serendipity·y 发表于 2022/07/06 22:46:27 2022/07/06
【摘要】 一、前言 Truffle 3.0 版本引入了大量的新特性,这些特性为我们带来了大量的重要革新性变化,让 network 的管理更简单,新的抽象的合约层,允许你从第三方引入各种依赖文件。伴随以太坊的开发工...

一、前言

  • Truffle 3.0 版本引入了大量的新特性,这些特性为我们带来了大量的重要革新性变化,让 network 的管理更简单,新的抽象的合约层,允许你从第三方引入各种依赖文件。
  • 伴随以太坊的开发工具逐步成熟,这样的革新非常有价值。这个升级同样适用于从 beta 3.0.0-9 升级到 3.0.1 的用户。

二、配置(Configuration)

  • Truffle 2.0 中使用了一个不舒服的配置方式,不仅可以使用一个 default,匿名的网络设置(通过 rpc 配置项);同时也可以使用命名的网络设置,比如 ropsten 或者 live。由此带来的一个后果是,可能会无意中覆盖了网络配置,或者部署到错误的网络。在 Truffle 3.0 中,解决了这个问题,代价是对配置方式的调整。
  • 在 Truffle 2.0 版本中,一个命名网络的声明方式示例如下:
module.exports = {
  rpc: {
    host: "localhost",
    port: 8545
  },
  networks: {
    staging: {
      host: "localhost",
      port: 8546,
      network_id: 1337
    },
    ropsten: {
      host: "158.253.8.12",
      port: 8545,
      network_id: 3
    }
  }
};
  • 在 Truffle 3.0 中,修改为下述的方式:
module.exports = {
  networks: {
    development: {
      host: "localhost",
      port: 8545,
      network_id: "*"
    },
    staging: {
      host: "localhost",
      port: 8546,
      network_id: 1337
    },
    ropsten: {
      host: "158.253.8.12",
      port: 8545,
      network_id: 3
    }
  }
};
  • 变化点如下:
    • 默认网络配置项 rpc 被移除,取而代之的是在 networks 配置项下的一个专门的名为 development 的网络配置项,development 中会指定的 ip 和 port,和配置的网络类型 1,默认是任意 *;
    • 如果没有指定网络,在执行 migrate 等命令时默认使用名为 development 的网络;
    • 为了避免出现部署到错误网络的情况,也可以完全移除配置项 development。如果已经移除 development 的网络配置,但在 truffle migrate 等命令时不指定网络会有下述错误发生:
$ truffle migrate
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

Error: No network specified. Cannot determine current network.
    at Object.detect (/usr/local/lib/node_modules/truffle/lib/environment.js:27:23)
    at /usr/local/lib/node_modules/truffle/lib/commands/migrate.js:33:19
    at /usr/local/lib/node_modules/truffle/lib/contracts.js:51:11
    at /usr/local/lib/node_modules/truffle/lib/contracts.js:83:9
  • 如果要指定网络,如 ropsten,使用命令:
truffle migrate --network ropsten
  • 另外每个网络配置项,都需要指定一个网络 ID[networkID],这可以算是一种安全机制来保证部署的安全性,来保证一定是部署到你想要部署的网络,development 这样的网络环境,可以使用*来表示任意,以方便调测。

三、移植和测试依赖(Migrations and Test Dependencies)

  • 在没有引入包管理前,Truffle 可以假设所有智能合约都是需要移植和测试的。在有了包管理之后,由于依赖可以来自多个不同的地方,不需要再进行默认关联,如果需要关联,必须明确指定,以减少自动关联带来的潜在问题。
  • 来看一个migration(移植)的手动引入关联的例子:
    • 在 2.0 版本中的 ./migrations/2_deploy_contracts.js:
module.exports = function(deployer) {
  deployer.deploy(ConvertLib);
  deployer.autolink();
  deployer.deploy(MetaCoin);
};
    • 3.0 版本中的 ./migrations/2_deploy_contracts.js:
var ConvertLib = artifacts.require("ConvertLib.sol");
var MetaCoin = artifacts.require("MetaCoin.sol");

module.exports = function(deployer) {
  deployer.deploy(ConvertLib);
  deployer.link(ConvertLib, MetaCoin);
  deployer.deploy(MetaCoin);
};
  • 改进之处如下:
    • 提供 artifacts.require() 方法,类似 Node.js 的 require() 语句,用于声明依赖;
    • 可以通过 artifacts.require() 来引入一个本地的 Solidity 文件,或者是包中依赖路径,这个方法将负责引入对应的文件;
    • Javascript 中的测试用例也需要通过 artifacts.require() 语句来引入依赖。
  • 自动关联为依赖问题的解决带来了一点点方便,但随着包管理的引入,不再可能自动判断在移植中所真正需要的所有的依赖,提供的替代方案是,通过明确指定的方式来关联库。

四、合约

① 合约抽象:JSON 格式(不再使用 .sol.js!)

  • Truffle 之前将合约定义为 Javascript 的格式,以 .sol.js 为后缀的文件存储。存为这样的格式的考虑,主要是可以在任何地方容易的使用。但最终发现,我们考虑的并不完善,不仅 Javascript 中存在一些情况中难以直接使用,更在非 Javascript 环境完全不可用。为解决这样的局限性,Truffle 3.0 将所有合约编译后的结果存为 JSON 格式,以能随时随地的,跨环境使用。
  • 如果已经使用 Truffle 2.0 生成了许多的文件,我们提供了一个工具来进行转换。如果要升级 Truffle2.0 生成的 .sol.js,需要先安装 truffle-soljs-updater:
$ npm install -g truffle-soljs-updater
  • 安装好后,可以通过 sjsu 命令来使用这个工具,使用方法如下:
$ cd ./build/contracts
$ sjsu
Converting ConvertLib.sol.js...
Converting MetaCoin.sol.js...
Converting Migrations.sol.js...
Files converted successfully.
  • 具体使用方法是,进入到 .sol.js 文件所在目录,这里是 ./build/contracts,然后运行 sjsu 即可。默认情况下 sjsu 命令只会创建新格式的 .json 后缀结尾的文件,并不会删除原始文件,需要手动删除旧的 .sol.js 来保证 Truffle 3.0 能正常工作。需要注意的是,建议删除前将旧的 .sol.js 的文件备份到其它地方,最终确认生成的 .json 文件正确的情况下再删除。
  • 另外一种选择是使用强制模式,sjsu -f 通过加 -f 参数,这样 sjsu 会删除旧的 .sol.js 文件,并生成生的 .json 文件,但需要保证旧的文件已提前进行了妥善的保存,以避免造成不必要的损失。
$ cd ./build/contracts
//务必保证你已经备份了旧的`.sol.js`文件
//`-f`参数会强制删除旧的`.sol.js`文件
$ sjsu -f
Converting ConvertLib.sol.js...
Converting MetaCoin.sol.js...
Converting Migrations.sol.js...
Files converted successfully.
Successfully deleted old .sol.js files.

② 合约交互抽象层:.deployed() 现在提供 then()

  • 这个改变,会影响 Migrations(移植)、测试、应用代码。在 Truffle2.0 中,合约抽象层使用原生的方式来管理网络。上文中的 default 的方式,引发潜在的网络不正确的可能,导致可能的部署到错误的网络的问题。
  • 原抽象层提供了一个精心考虑,方便使用的语法,MyContract.deployed().myFunction(…),但将错误直接暴露给了开发者。在 Truffle 3.0 中,改变了这个语法,为 .deployed() 提供了类似 promise 的语法。同时,当前的合约抽象层可以与以太坊的包管理标准,EIP190eip190 进行无缝集成,但这意味着必须改变原有代码的语法。
  • v2.0 版本的语法:
MyContract.setProvider(someWeb3Provider);
MyContract.deployed().someFunction().then(function(tx) {
  
});
  • v3.0 版本的语法:
MyContract.setProvider(someWeb3Provider);
MyContract.deployed().then(function(instance) {  
  return instance.someFunction();
}).then(function(result) {
  
});
  • 上述的语法上可额能有些啰嗦,但目的是保证合约抽象层连接到了正确的网络。再来看看新语法的例子:
MyContract.setProvider(someWeb3Provider);

var deployed;

MyContract.deployed().then(function(instance) {
  deployed = instance;  
  return deployed.someFunction();
}).then(function(result) {
  return deployed.anotherFunction();
}).then(function(result) {
  
});
  • 需要注意的是:
    • 如果合约方法 f() 不会改变区块链上的数据的,那么调用时要使用 instance.f.call();
    • 如果合约方法 f() 会改变区块链上的数据的,则需使用 instance.f() 来进行调用;
    • 如果想在一个 promise 链中调用多个方法,需要注意示例中的 instance 的作用域,可以在外部定义一个对象,查看 var deployed; 的定义。

③ 合约抽象层:交易结果对象

  • 一直以来有个麻烦的事,关于在 Web3 中监听事件,在大多数情况下,事件一般不是通过主动跟踪事件,而是我们进行了对应的操作,从而引发对应的事件产生。尽管主动监听事件的方式仍然支持,但为了让后一种情况实现起来更加简单,调整了 transaction 的返回结果。
  • 在 Truffle2.0 版本中,transaction 简单的返回了一个交易的哈希串,而在 Truffle3.0 中,将返回一个包含交易详情的结果对象。
  • v2.0:
MyContract.deployed().someFunction().then(function(tx) {
  
});
  • v3.0:
MyContract.deployed().then(function(instance) {
  deployed = instance;  
  return deployed.someFunction();
}).then(function(result) {
  //
});
  • 在 Truffle 3.0 中通过返回 transaction 的详情信息,可以更加方便的决定是否要触发某些相应的行为。如下所示,是一个假想的关注合约发布事件:
var assert = require("assert");
var PackageIndex = artifacts.require("PackageIndex.sol");

contract("PackageIndex", function(accounts) {

  it("publishes a release correctly", function() {
    return PackageIndex.deployed().then(function(deployed) {
      return deployed.publish("v2.0.0");
    }).then(function(result) {

      var found_published_event = false;

      for (var i = 0; i < result.logs.length; i++) {
        var log = result.logs[i];

        if (log.event == "ReleasePublished") {
          found_published_event = true;
          break;
        }
      }

      assert(found_published_event, "Uh oh! We didn't find the published event!")
    });
  });

});
  • 虽然这样,需要在所有返回结果中,去找我们感兴趣的事件,但也许是比 PackageIndex.ReleasePublished.watch(…) 更好的一种方式,因为可以在当前的代码中管理事件逻辑,而不是分散在代码各处。

五、构建流:默认不再需要构建器

  • Truffle1.0 和 Truffle2.0 中,将 Web 应用与整个框架紧紧的捆绑在一起,所以 Truffle 打包提供了一个默认的构建流,以便可以快速的建立 dapp,并运行起来。虽然在有些情况下,这带来了极大的便利,但在其它的场景下却显得极为鸡肋。
  • 基于以太坊的应用持续的在增加,从开始的仅仅支持 Web 的 dapp 应用,发展为可以支持原生语言,在手机或电脑上独立运行。支持各种各样的用户场景,一直是 Truffle 的初衷,所以 Truffle3.0 决定移除默认的构建流。如果你想使用 Truffle 来集成你的应用,仍然可以编写自定义的构建流,因为 Truffle 打算专注做智能合约相关的最好用工具。因此关于构建,通过集成更好工具,如 webpack,browserify,Grunt,Metalsmith 来实现。
  • 尽管构建流默认被移除,但并不意味着没得选择,在 Truffle 中非常注重开发者的使用体验,也永远不会对大家弃之不顾,下面提供两种可选方式,后一种选择会深度绑定到最终选择的构建工具上,如 webpack。

① 在 Truffle3.0 中使用旧的构建器

  • 如果在 Truffle2.0 中使用了默认的构建器,而又想升级到 Truffle3.0,我们升级了默认构建器4,所以它完全可以与 Truffle3.0 兼容。但这将是最后一次升级默认的构建器,因为要使之兼容所有的场景,将是一件工程非常复杂的事,所以推荐最终选用后面提到的其它构建系统。
  • 默认构建器,并未集成在 Truffle3.0 中,要使用它,需要安装 truffle-default-builder,并做为一个依赖进行引入,在工程目录运行下面的命令:
$ npm install truffle-default-builder --save
  • 一旦安装,可以在 truffle.js 配置文件中,使用默认构建器,配置文件的前后变化:
    • v2.0:Truffle.js:
module.exports = {
  build: {
    "index.html": "index.html",
    "app.js": [
      "javascripts/app.js"
    ],
    "app.css": [
      "stylesheets/app.css"
    ],
    "images/": "images/"
  },
  rpc: {
    host: "localhost",
    port: 8545
  }
};
    • v3.0: truffle.js:
var DefaultBuilder = require("truffle-default-builder");

module.exports = {
  build: new DefaultBuilder({
    "index.html": "index.html",
    "app.js": [
      "javascripts/app.js"
    ],
    "app.css": [
      "stylesheets/app.css"
    ],
    "images/": "images/"
  }),
  networks: {
    development: {
      host: "localhost",
      port: 8545,
      network_id: "*" // Match any network id
    }
  }
};
  • 可以发现,除了需要引入作为依赖的默认构建器包,放入构建器需要的配置文件以外,其它是完全一致的。由于默认构建器已经使用了最新的合约抽象层 5,因此上面的所有版本升级需要进行的调整,都适用使用默认构建器。

② 使用自定义构建流程/构建工具

  • 自定义构建流程并没有想像中难以使用,真正的难点在于写一个构建流程能兼容各种各样的构建需求,这里推荐去看看适合自己项目的构建工具,比如之前提及的 webpack,browserify,Grunt,Metalsmith,最终哪个的特性能满足我们自己的需要,需要视项目及构建需求来定。无论想构建一个浏览器应用,命令行工具,JS 库,还是原生的手机应用,合约的初始化,部署合约的使用均遵循通用的流程。
  • 当配置自定义工具或应用时,应遵循下述的流程:
    • 编译合约文件,将生成的 .json 结果放到 ./build/contracts 目录下;
    • 通过 truffle-contract[truffle-contract],将编译的合约结果,转为合约抽象层,来方便使用;
    • 为合约抽象层设置 web3 provider,需要注意的是在 Metamask 和 Mist 中,环境内会自动提供,但在其它情况下,需要手动通过配置完成指定。
  • 如下所示,集成 NodeJS:
// 1.引入编译好的合约文件结果 
var json = require("./build/contracts/MyContract.json");

// 2.将合约转为合约抽象层实例
var contract = require("truffle-contract");
var MyContract = contract(json);

// 3.设置合约抽象层实例的 web3 provider
MyContract.setProvider(new Web3.providers.HttpProvider("http://localhost:8545"));

// 4.使用
MyContract.deployed().then(function(deployed) {
  return deployed.someFunction();
});
  • 所有的构建流程,均遵循上述的流程,核心关注点是保证自定义流程要加载所有的合约资源,并正确的设置合约抽象层。

文章来源: blog.csdn.net,作者:╰つ栺尖篴夢ゞ,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/Forever_wj/article/details/125628456

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。