主页 > imtoken正版app下载 > 以太坊平台上的 Hello World DApp

以太坊平台上的 Hello World DApp

imtoken正版app下载 2023-02-02 07:41:37

1 简介

DApp(去中心化应用):后台运行在去中心化的点对点网络上。 与应用程序不同,后台运行在中央服务器上。 以太坊上的 DApps 通过智能合约与区块链交互。

2.环境准备

搭建好以太坊私有链后,就可以开始开发了。 想想就很郁闷~不过,还是得先准备好开发环境。

三、项目介绍

一家拥有 16 只宠物的宠物店正在开发一个去中心化的应用程序,供大家领养宠物。

在truffle box中,已经提供了宠物店网站部分的代码,我们只需要编写合约和交互部分即可。 项目用户界面的先睹为快:

以太坊经典和以太坊_以太坊联盟和以太坊的关系_下面哪个不是以太坊平台的特性

3.1 创建项目目录

mkdir pet-shop-tutorial
cd pet-shop-tutorial

3.2 使用truffle unbox创建项目

truffle unbox pet-shop

此步骤可能需要一段时间,因为需要下载node_modules,请耐心等待...

结果:

Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
  Run dev server: npm run dev

3.3 项目结构 3.4 编写智能合约

合同/收养。

pragma solidity ^0.4.17;
contract Adoption {
  address[16] public adopters;  // 地址数组,分别对应宠物0-15的领养人的地址
  // 领养宠物
  function adopt(uint petId) public returns (uint) {
    require(petId >= 0 && petId <= 15);  // 确保id在数组长度内
    adopters[petId] = msg.sender;        // 保存领养者的地址
    return petId;
  }
  // 返回领养者
  function getAdopters() public view returns (address[16]) {
    return adopters;
  }
}

这里用于编写以太坊智能合约的语言称为 Solidity。 具体的语法我暂时不用研究了。 这里的例子也非常简单明了。 继续往下~

3.5 编译部署 3.5.1 编译

将Solidity代码编译成EVM字节码,在pet-shop目录下:

truffle compile

输出:

Compiling .\contracts\Adoption.sol...
Compiling .\contracts\Migrations.sol...

以太坊经典和以太坊_下面哪个不是以太坊平台的特性_以太坊联盟和以太坊的关系

Compilation warnings encountered: /D/githome/blockchain/pet-shop/contracts/Migrations.sol:11:3: Warning: No visibility specified. Defaulting to "public". function Migrations() { ^ Spanning multiple lines. ,/D/githome/blockchain/pet-shop/contracts/Migrations.sol:15:3: Warning: No visibility specified. Defaulting to "public". function setCompleted(uint completed) restricted { ^ Spanning multiple lines. ,/D/githome/blockchain/pet-shop/contracts/Migrations.sol:19:3: Warning: No visibility specified. Defaulting to "public". function upgrade(address new_address) restricted { ^ Spanning multiple lines. Writing artifacts to .\build\contracts

这里有一些警告,但没关系。 这里warinig的意思类似于java中写一个方法不写public修饰符,只是编译时会默认编译为public。

编译完成后会多出一个build文件夹,里面的contracts就是编译后的代码,后面的部署就依赖这些文件。

3.5.2 部署前准备

在migrations目录下,已经有一个文件1_initial_migration.js。 如果没有这个文件,也可以通过truffle init命令生成。 该文件的作用是部署 Migrations.sol 合约。 关于这份合约,truffle官方的介绍是:

您必须在第一次迁移中部署此合约才能利用迁移功能。

如果有必要,那就去做吧。

接下来,为了部署我们的Adoption.sol,我们还需要编写一个用于部署这个合约的部署文件,命名为2_deploy_adoption.js。 这里的命名有点学问,truffle会按照你命名的部署文件名的顺序进行部署。

从这里,您可以创建带有递增编号前缀的新迁移,以部署其他合约并执行进一步的部署步骤。

在migrations目录下新建2_deploy_adoption.js:

var Adoption = artifacts.require("Adoption");
module.exports = function(deployer) {
  deployer.deploy(Adoption);
};

然后,在项目根目录下有一个名为truffle.js的部署配置文件:

module.exports = {
  // See 
  // for more about customizing your Truffle configuration!
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "1024",
      gas: 3141593
    }
  }
};

在此文件中,指定您将部署合同的位置。 根据之前的环境,我把它的port从7545改成8545,networkid从*改成1024。gas代表你愿意为部署你的合约支付多少单位的gas,默认是4712388。我改成了我的私链创世块的gasLimit(如果这里没有指定gas,而你的创世块中的gasLimit小于默认值,部署时会报错:exceeds block gas limit)其他具体配置参数,你也可以参考truffle官方的介绍。

最后进入geth控制台创建账号,有账号的可以跳过。

personal.newAccount()

连续两次输入密码后,账号就创建好了。 控制台打印出来的就是你的账户地址。 记住密码,不要忘记~再啰嗦一点。 您刚刚创建的帐户信息已经存在于节点数据库中。 在一个叫keystore的文件夹下,文件名类似于UTC--2018-02-08T16-35-23.044654600Z--9d7578d663e204c90c2b419c05a02046104446f2,是时间戳+地址格式。 在交易过程中,以太坊会使用你的密码和这个账户文件进行数字签名,然后将其打包成交易信息进行广播。 收到交易信息的节点会验证交易的合法性,然后挖矿并保存交易~

话虽如此,您必须检查您的帐户是否有余额。 部署智能合约需要资金。 这笔钱在以太坊中被称为gas,gas是从以太坊转化而来的。

[图片上传失败...(image-573dfe-1522567562814)]

所以说到底,就是要求你的账户必须有以太币。 在geth控制台查看你的账户余额:

web3.fromWei(eth.getBalance(eth.coinbase),"Ether")

这里打印的余额是“以太币”下面哪个不是以太坊平台的特性,以太币。 如果你想查看你有多少 gas,只需使用 eth.getBalance(eth.coinbase)。 如果您的帐户中没有钱,请去挖矿。

miner.start()

以太坊联盟和以太坊的关系_以太坊经典和以太坊_下面哪个不是以太坊平台的特性

如果您是第一次挖矿,则需要等待一段时间初始化后才能挖矿。 如果几乎相同,则可以停止挖掘。

miner.stop()

至此,部署准备工作完成。

3.5.3 部署

在部署之前,请确保您的私有链环境已启动。 然后,你要确保你的私有链上有节点在挖矿。 因为部署智能合约实际上是在发送交易,所以矿工必须将您的交易保存在特定的区块中并确认它才能被视为成功部署。 接下来在项目根目录下进行命名:

truffle migrate

很有可能,您会看到以下有关部署失败的日志:

Using network 'development'.
Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... undefined
Error encountered, bailing. Network state unknown. Review successful transactions manually.
Error: authentication needed: password or unlock

为了安全起见,以太坊会在一段时间后自动锁定账户。 所以解决方法是进入geth交互模式解锁你的默认账户eth.coinbase,因为在部署的时候,默认使用这个账户来部署,除非你在truffle.js中指定了一个账户来部署,那么你就可以去解锁对应账号~

personal.unlockAccount(eth.coinbase)

OK,再次输入部署命令。

Using network 'development'.
Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0xbf625c89f59a08341ed9ed6df0fa5401fa0789689edd8bfc9f3148430c3bb1b4
  Migrations: 0xbda1a6c2e10478dff136eeac357391ff43777554
Saving successful migration to network...
  ... 0xe16b7e83871dd81765c30f3a6e8987a16aab20fa635534a719600b65f2a33485
Saving artifacts...
Running migration: 2_deploy_contract.js
  Deploying Adoption...
  ... 0x2e5e196e78713c2699689b1664cc5fb6e52a730ccd2b65d28db56d456a2cb487
  Adoption: 0xaf5df9828eea7b6ea8e5f614e1e93ce3346b4e37
Saving successful migration to network...
  ... 0x81b036154d713852c59c7f0d183e23272cd753b201749d713166ae692035b799
Saving artifacts...

部署成功。 这个时候我一般用miner.stop(),因为私链上以太币不多,够用就行。 而且挖的越多,后面越难挖。 因为每个区块的难度逐渐增加,挖矿需要计算的nonce越大,意味着挖矿时间会比较长,不利于后续的开发调试。 每个区块的具体信息可以通过eth.getBlock(i)查看。

3.6 测试 3.6.1 编写测试

Truffle 为我们提供了一个很好的测试框架。 在test文件夹新建一个:TestAdoption.sol

pragma solidity ^0.4.17;
import "truffle/Assert.sol";   // 引入的断言
import "truffle/DeployedAddresses.sol";  // 用来获取被测试合约的地址
import "../contracts/Adoption.sol";      // 被测试合约
contract TestAdoption {
  Adoption adoption = Adoption(DeployedAddresses.Adoption());
  // 领养测试用例
  function testUserCanAdoptPet() public {
    uint returnedId = adoption.adopt(8);
    uint expected = 8;
    Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
  }
  // 宠物所有者测试用例

以太坊经典和以太坊_以太坊联盟和以太坊的关系_下面哪个不是以太坊平台的特性

function testGetAdopterAddressByPetId() public { // 期望领养者的地址就是本合约地址,因为交易是由测试合约发起交易, address expected = this; address adopter = adoption.adopters(8); Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded."); } // 测试所有领养者 function testGetAdopterAddressByPetIdInArray() public { // 领养者的地址就是本合约地址 address expected = this; address[16] memory adopters = adoption.getAdopters(); Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded."); } }

3.6.2 运行测试

前提条件:账户解锁,矿工挖矿

truffle test --network development

--network development 指定使用 truffle.js 开发配置 u 运行测试。

结果:

Using network 'development'.
Compiling .\contracts\Adoption.sol...
Compiling .\test\TestAdoption.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...
  TestAdoption
    √ testUserCanAdoptPet (3017ms)
    √ testGetAdopterAddressByPetId (6017ms)
    √ testGetAdopterAddressByPetIdInArray (1006ms)
  3 passing (17s)

这里肯定不会记录坑,是第一次跑测试的时候遇到的:

TestAdoption
    1) "before all" hook: prepare suite
  0 passing (4s)
  1 failing
  1) TestAdoption "before all" hook: prepare suite:
     Error: The contract code couldn't be stored, please check your gas amount.
      at Object.callback (C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\contract.js:147:1)
      at C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\method.js:142:1
      at C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\requestmanager.js:89:1
      at C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\truffle-provider\wrapper.js:134:1
      at XMLHttpRequest.request.onreadystatechange (C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\httpprovider.js:128:1)
      at XMLHttpRequestEventTarget.dispatchEvent (C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:64:1)
      at XMLHttpRequest._setReadyState (C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:354:1)
      at XMLHttpRequest._onHttpResponseEnd (C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:509:1)
      at IncomingMessage. (C:\Users\LIRI7\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:469:1)
      at endReadableNT (_stream_readable.js:1056:12)
      at _combinedTickCallback (internal/process/next_tick.js:138:11)
      at process._tickCallback (internal/process/next_tick.js:180:9)

以太坊联盟和以太坊的关系_下面哪个不是以太坊平台的特性_以太坊经典和以太坊

有人提到了同样的问题,但是这个仍然没有帮助我,问题没有解决,但问题基本上被锁定为气鬼。 直到看到这篇文章,我把初始化创世块的genesis.json中的gasLimit改成一个比较大的值(从0x2fefd9到0x8000000),重建了一条私链,问题就神奇的解决了。 感动得泪流满面~

3.7 界面

当我们的智能合约准备就绪后,我们就可以开始实现 UI 部分了。 在truffle框架中,前端代码写在src下。 在这个宠物店中,部分代码已经开箱即用,现在我们只需要编写与智能合约交互的部分即可。 这里用到的是web3.js,以太坊的JavaScript API,通过web3.js,我们可以和部署好的智能合约进行交互。

修改下面的 src/js/app.js。

3.7.1 初始化web3

找到函数initWeb3,实现如下:

initWeb3: function() {
    
    // Is there an injected web3 instance?
    if (typeof web3 !== 'undefined') {
      App.web3Provider = web3.currentProvider;
    } else {
      // If no injected web3 instance is detected, fall back to Ganache
      App.web3Provider = new Web3.providers.HttpProvider('http://localhost:8545');
    }
    web3 = new Web3(App.web3Provider);
    return App.initContract();
  }

在代码中,首选 Mist 或 MetaMask 将 web3 实例注入浏览器,如果没有,则从本地环境创建一个。 这里的8545是本节点监听的rpc端口。

3.7.2 实例化合约

找到initContract,实现如下:

initContract: function() {
  // 加载Adoption.json,保存了Adoption的ABI(接口说明)信息及部署后的网络(地址)信息,它在编译合约的时候生成ABI,在部署的时候追加网络信息
  $.getJSON('Adoption.json', function(data) {
    // 用Adoption.json数据创建一个可交互的TruffleContract合约实例。
    var AdoptionArtifact = data;
    App.contracts.Adoption = TruffleContract(AdoptionArtifact);
    // Set the provider for our contract
    App.contracts.Adoption.setProvider(App.web3Provider);
    // Use our contract to retrieve and mark the adopted pets
    return App.markAdopted();
  });
  return App.bindEvents();
}

3.7.3 标记采用状态

修改 markAdopted 方法:

markAdopted: function(adopters, account) {
    var adoptionInstance;
    App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;
      // 调用合约的getAdopters(), 用call读取信息不用消耗gas
      return adoptionInstance.getAdopters.call();
    }).then(function(adopters) {
      for (i = 0; i < adopters.length; i++) {
        if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
          $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
        }
      }
    }).catch(function(err) {
      console.log(err.message);
    });
  }

以太坊经典和以太坊_下面哪个不是以太坊平台的特性_以太坊联盟和以太坊的关系

3.7.4 处理领养事件

修改 handleAdopt 方法:

handleAdopt: function(event) {
    event.preventDefault();
    var petId = parseInt($(event.target).data('id'));
    var adoptionInstance;
    // 获取用户账号
    web3.eth.getAccounts(function(error, accounts) {
      if (error) {
        console.log(error);
      }
      var account = accounts[0];// 用第一个账号领养
      App.contracts.Adoption.deployed().then(function(instance) {
        adoptionInstance = instance;
        // 发送交易领养宠物
        return adoptionInstance.adopt(petId, {from: account});
      }).then(function(result) {
        return App.markAdopted();
      }).catch(function(err) {
        console.log(err.message);
      });
    });
  }

3.8 运行APP

在pet-shop目录下,运行npm run dev,会启动lite-server

> lite-server
** browser-sync config **
{ injectChanges: false,
  files: [ './**/*.{html,htm,css,js}' ],
  watchOptions: { ignored: 'node_modules' },
  server:
   { baseDir: [ './src', './build/contracts' ],
     middleware: [ [Function], [Function] ] } }
[Browsersync] Access URLs:
 -------------------------------------
       Local: http://localhost:3003
    External: http://10.222.49.22:3003
 -------------------------------------
          UI: http://localhost:3004
 UI External: http://10.222.49.22:3004
 -------------------------------------
[Browsersync] Serving files from: ./src
[Browsersync] Serving files from: ./build/contracts
[Browsersync] Watching files...

打开浏览器:3003,可以看到16只宠物等你领养。 点击Adopt,你会发现按钮的文字变成了success,不能再点击了。 如果领养不成功,很可能是您的账号被锁定了,此时您需要解锁对应的账号。 领养失败还有一个原因,就是私链上没有节点挖矿,交易无法保存。 此时miner.start()就可以了~

以太坊经典和以太坊_以太坊联盟和以太坊的关系_下面哪个不是以太坊平台的特性

宠物领养数据已保存至区块链。 即使您的节点重新启动,采用数据仍然存在。 就像历史一样下面哪个不是以太坊平台的特性,发生的就是发生的。 不可篡改,已同步到区块链上的其他节点。 .

如果修改合约重新编译部署,那么之前的采用数据仍然会保存在区块链上,但是新合约无法再获取旧合约的数据。 当然,如果你保存旧部署后的ABI,即build目录下的json文件,用它来替换当前build文件夹下的json文件,那么就可以穿梭回旧版本的pet- shop,从UI可以发现,采用的数据还在。

3.9 结束

参考文件: