【智能合约实战】——入门级DAPP,没有想象中那么难


最近我司准备引入区块链技术,打算将整车上链,学习新技术的机会千载难逢,于是乎加入智能合约研究小组,以下是我一周了解和学习的入门级小成果

什么是dapp?

Dapp就是去中心化应用程序的简称 ,是以某种方式连接到区块链的在线应用程序。从理论上讲,dapp是运行在节点服务器上面的,而dapp的整个后端存储是在区块链而不是服务器上完成的,重点是dapp的数据是存储在区块链上的,不是中心数据库;DApp 的使用方式与其他任何app都相同

dapp和app交互方式有什么不同?

从用户角度看没有任务区别,发起请求,完成活动;从开发交互上讲,主要是业务功能存储在智能合约上,存储数据上链,公开透明。
在这里插入图片描述

DAPP

1.界面:前端与集中式应用程序基本相同。

2.智能合约:首先智能合约是DApp 的一部分,Dapp的前端使用 API 与智能合约进行通信,智能合约负责与区块链网络的交互。

3.区块链网络:通过智能合约的运行,选择数据并将数据存储在去中心化区块链网络上。

APP

1.界面:用户通过网络、移动或桌面界面的前端与应用程序交互。什么是前端,我们手机、电脑或者其他终端安装的这个我们通常说的网络应用app

2.服务器:来自前端的请求然后被传递到中央服务器。

3.数据库:前端用户操作的数据(通过服务器请求获取)被收集并存储在单个公司或个人拥有的集中式数据库中

程序编写

目的:实现一个用户年龄的更新
完整项目的github地址:
firstTruffle-updateAge-dapp

开发前准备:

  • 开发工具:vsCode
  • 开发框架:node,truffle
  • 开发语言:solidity,JavaScript
  • 钱包:metamask【开发前请确保自己已经拥有一个账户】
  • node环境: macos安装node教程
  • 安装truffle:npm install -g truffle

初始化项目结构

mkdir firstTruffle
npm init
truffle init

执行后项目的结构为:
在这里插入图片描述

网络配置

1.本地虚拟网络-安装gancache客户端【https://github.com/trufflesuite/ganache/releases】安装完成打开自动生产虚拟网络

2.通常通过infura【https://infura.io/】,先注册后登陆创建项目,选择网络后,会自动生成链接,详情见【infura的使用
配置好之后,在根目录下创建.env文件,将infura的私钥和链接配置进行【注意不提交到git上,防止私钥泄露】
在这里插入图片描述
然后安装dotenv【将变量从 .env 文件加载到 process.env 】支持
:const { API_URL, PRIVATE_KEY } = process.env;

#【全局配置文件】
npm install dotenv
#【提供web链接方法,后续链接钱包地址】
npm install truffle-hdwallet-provider

最终truffle-config.js中配置:其中dev是Gancahe的虚拟网络,本地测试部署是非常快的;rinkeby是以太坊的测试网络地址,可以在infura上找到地址

const webProvider=require("truffle-hdwallet-provider")
require('dotenv').config();
const { API_URL, PRIVATE_KEY } = process.env;

module.exports = {
  defaultNetwork:"rinkeby",
  networks: {
    dev: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*" // Match any network id
    },
    rinkeby:{
      provider:function(){
        return new webProvider(PRIVATE_KEY, API_URL) 
      },
      network_id: "*" , // Match any network id
      gas: 4500000,
    }
  }
};

编写智能合约

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

contract InfoContract {
    string name;
    uint age;

     /**定义事件*/
    event Instructor(string name, uint age);

    function setInfo(string memory _name, uint _age) public {
        name = _name;
        age = _age;
        /**触发事件 */
        emit Instructor(name, age);
    }

    function getInfo() public view returns(string memory, uint) {
        return (name, age);
    }
}

编写部署脚本:

const Migrations = artifacts.require("Migrations");

module.exports = function (deployer) {
  deployer.deploy(Migrations);
};

编写完成后,可以编译下看是否有问题:truffle compile,若成功显示如下结果。内部使用solc编译器
在这里插入图片描述

智能合约测试

#【先引入truffle中合约包,减少开发成本,本期主要是为了测试】
npm install truffle-contract

测试用例编写:

pragma solidity >=0.4.22 <0.9.0;
import "truffle/Assert.sol";
/**地址合约*/
import "truffle/DeployedAddresses.sol";
import "../contracts/InfoContract.sol";

contract TestInfoContract {
   InfoContract info = InfoContract(DeployedAddresses.InfoContract());
   string name;
   uint age;

   function testInfo() public {
     info.setInfo("ABC", 10);

     (name, age) = info.getInfo();

     Assert.equal(name, "ABC", "设置名字出错");
     Assert.equal(age, 10, "设置年龄出错");
   }
}

编写完测试用例可以执行:truffle test,若成功则会显示
在这里插入图片描述

智能合约部署

#在dev网络上部署合约:
truffle migrate --network dev

#在rinkeby网络上部署合约: 
truffle migrate --network rinkeby

web3j和智能合约交互

在根目录下创建src文件,再下面创建js文件夹,在js文件夹下引入【web3.min.js,truffle.contract.min.js,jquery.js】这三个公共包,创建app.js,这个是自己新生成,里面也有自己写的注释,方便理解

App = {
  web3Provider: null,
  contracts: {},

  init: function () {
    return App.initWeb3();
  },

  /**初始化web3,与钱包联通 */
  initWeb3: function () {
    if (window.ethereum) {
      this.provider = window.ethereum;
      try {
        window.ethereum.enable();
      } catch (error) {
        console.error("User denied account access");
      }

      App.web3Provider = web3.currentProvider
    } else if (typeof web3 !== 'undefined') {
      App.web3Provider = web3.currentProvider
      web3 = new Web3(App.web3Provider);
    } else {
      App.web3Provider = new Web3.providers.HttpProvider('HTTP://127.0.0.1:7545')
      web3 = new Web3(App.web3Provider);
    }

    return App.initContract();
  },

  /**获取账户的合约,绑定事件,并且设置监听*/
  initContract: function () {

    $.getJSON('InfoContract.json', function (data) {
      /**获取合约*/
      App.contracts.InfoContract = TruffleContract(data);
      /**连接账户*/
      App.contracts.InfoContract.setProvider(App.web3Provider);
      /**初始化数据 */
      App.getInfo();
      /**设置监听 */
      App.watchChanged();
    });

    /**绑定事件 */
    App.bindEvents();

  },

  /**合约交互,获取用户的年龄数据,显示在html上 */
  getInfo: function () {
    App.contracts.InfoContract.deployed().then(function (instance) {
      return instance.getInfo.call();
    }).then(function (result) {
      $("#loader").hide();
      $("#info").html(result[0] + ' (' + result[1] + ' years old)');
      console.log(result);
    }).catch(function (err) {
      console.error(err);
    });
  },

  /**更新按钮上绑定事件,调用合约进行更新 */
  bindEvents: function () {
    $("#button").click(function () {
      $("#loader").show();
      web3 = new Web3(App.web3Provider);

      web3.eth.defaultAccount = web3.eth.accounts[0];

      App.contracts.InfoContract.deployed().then(function (instance) {
        return instance.setInfo($("#name").val(), $("#age").val(), { gas: 500000 });
      }).then(function (result) {
        return App.getInfo();
      }).catch(function (err) {
        console.error(err);
      });
    });
  },

  /**监听合约内容发生改变,则页面显示更新 */
  watchChanged: function () {
    App.contracts.InfoContract.deployed().then(function (instance) {
      var infoEvent = instance.Instructor();
      return infoEvent.watch(function (err, result) {
        $("#loader").hide();
        $("#info").html(result.args.name + ' (' + result.args.age + ' years old)');
      });
    });
  }

}


$(function () {
  $(window).load(function () {
    App.init();
  });
});

页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>First Truffle DApp Demo</title>
    <link rel="stylesheet" type="text/css" href="main.css">

</head>
<body>
    <div class="container">
        <h1> First Truffle DApp Demo</h1>

        <h2 id="info"></h2>
        <!-- <img id="loader" src=""> -->

        <label for="name" class="col-lg-2 control-label">姓名:</label>
        <input id="name" type="text">

        <label for="name" class="col-lg-2 control-label">年龄:</label>
        <input id="age" type="text">

        <button id="button">更新</button>

    </div>

    <script src="js/jquery.min.js"></script>
    <script src="js/web3.min.js"></script>
    <script src="js/truffle-contract.min.js"></script>
    <script src="js/app.js"></script>

</body>
</html>```



css样式

```css
body {
    background-color:#F0F0F0;
    padding: 2em;
    font-family: 'Raleway','Source Sans Pro', 'Arial';
}
.container {
    width: 50%;
    margin: 0 auto;
}
label {
    display:block;
    margin-bottom:10px;
}
input {
    padding:10px;
    width: 50%;
    margin-bottom: 1em;
}
button {
    margin: 2em 0;
    padding: 1em 4em;
    display:block;
}

#info {
    padding:1em;
    background-color:#fff;
    margin: 1em 0;
}

#loader {
    width: 100px;
    display:none;
}

此时可以执行npm run dev,但是显示文件找不到,那是因为启动的时候找index.html是从根目录开始找的,而index.html在src目录下,所以需要配置下,让从src下找index目录,所以在根目录下创建bs-config.json文件,设置
{
“server”:
{ “baseDir”: [“./src”, “./build/contracts”] }
}
执行npm run dev,出现页面【此刻会先与自己浏览器钱包metamask链接,选择上传过合约的那个账户;然后输入姓名和年龄点击更新】
在这里插入图片描述
最终项目的目录结构为:
在这里插入图片描述

总结

对于程序员来说,任何了解都不如上手运行一把,从实践中学习,切身体会,就像是以一个hello-world打开程序的世界,这是我学习智能合约的起点,却不是重点,感兴趣的小伙伴们也可以在评论区留言,互相交流学习经验

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值