• DAPP开发初探——永存的留言


    转载地址

    https://blog.csdn.net/qq_33764491/article/details/80570266

    前言

    最近DAPP的开发貌似很火,学习了区块链的一些知识之后,相信有很多人和我一样,也想了解开发一个DAPP是一个怎样的流程。

    下面将通过一个简单的栗子来初识一下DAPP的开发流程,届时,我们也将开发出第一个DAPP应用–《永存的留言》。

    在线体验(Ludis):http://words.ldsun.com/

    项目介绍

    《永存的留言》是一个基于以太坊的在线留言平台。它的功能十分简单–用户可以在平台上进行留言,平台每10s随机的展示留言内容。
    但是它的特点在于,利用区块链的特性,保证了数据的真实性、完整性和安全性。
    这里写图片描述

    • 使用Solidity开发后端方法
    • 使用Truffle框架
    • 基于unbox react脚手架项目
    • 部署在以太坊测试网络上Ropoetn Test Network
    • 使用MetaMask钱包插件发布交易

    开发步骤

    下载react项目模板

    确保本地已经准备好Truffle所需的环境,准备以下命令,下载react项目模板。
    $ mkdir a && cd a
    truffle unbox react
    当看到 Unbox successful. Sweet!提示时,表明下载成功。

    编写智能合约

    这是我们唯一的实现合约,包含的发送留言和读取留言的方法。

    pragma solidity ^0.4.19;
    
    contract SimpleStorage {
        // 留言结构体
        struct Message {
            string word; // 留言
            address from; // 留言者地址
            string timestamp ; // 留言unix时间戳
        }
    
        Message[] private wordArr;
    
        /**
         * 写入留言的方法
         */
        function setWord(string s, string t) public {
            wordArr.push(Message({
                word: s,
                from: msg.sender,
                timestamp: t
            }));
        }
    
        /**
         * 获取随机留言的方法
         */
        function getRandomWord(uint random) public view returns (uint, string, address, string) {
            if(wordArr.length==0){
                return (0, "", msg.sender, "");
            }else{
                Message storage result = wordArr[random];
                return (wordArr.length, result.word, result.from, result.timestamp);
            }
        }
    }

    编译、部署合约

    修改发布的脚本。

    var SimpleStorage = artifacts.require("./SimpleStorage.sol");
    //var Words = artifacts.require("Words");
    
    module.exports = function(deployer) {
      deployer.deploy(SimpleStorage);
      //deployer.deploy(Words);
    };
    

    执行truffle compile进行合约的编译。

    获取合约地址。

    • 这里我们打开MetaMask钱包插件,左上角选择Ropoetn Test Network网络.
      这里写图片描述
    • 利用Remix编译,发布合约到以太坊测试环境。
      这里写图片描述
    • 通过MetaMask的交易hash查询,获取已经部署到以太坊测试网络中的合约地址。
      这里写图片描述

    编写前端页面

    这个部分主要是编写前端的展示效果和与合约交互的逻辑,这一部分最难编写,也最耗时间。

    • 主要逻辑代码
    const contractAddress = "0x39e5196750dcddb1aaf6dda7d6e8dbb633482905" // 合约地址(以太坊测试网络)
    var simpleStorageInstance // 合约实例
    
    class App extends Component {
      // 初始化构造
      constructor(props) {
        super(props)
        this.state = {
            word: null,
            from: null,
            timestamp: null,
            random: 0,
            count: 0,
            input: '',
            web3: null,
            emptyTip: "还没有留言,快来创建全世界第一条留言吧~",
            firstTimeLoad: true,
            loading: false,
            loadingTip: "留言正在写入,请耐心等待~",
            waitingTip: "留言正在写入,请耐心等待~",
            successTip: "留言成功",
            animate: "",
            in: css(styles.in),
            out: css(styles.out)
        }
      }
    
      // 获取Web3实例
      componentWillMount() {
        // Get network provider and web3 instance.
        getWeb3
        .then(results => {
          this.setState({
            web3: results.web3
          })
          // Instantiate contract once web3 provided.
          this.instantiateContract()
        })
        .catch(() => {
          console.log('Error finding web3.')
        })
      }
    
      // 获取合约实例
      instantiateContract() {
         /*
         * SMART CONTRACT EXAMPLE
         *
         * Normally these functions would be called in the context of a
         * state management library, but for convenience I've placed them here.
         */
        const contract = require('truffle-contract')
        const simpleStorage = contract(SimpleStorageContract)
        simpleStorage.setProvider(this.state.web3.currentProvider)
    
        // Get accounts.
        this.state.web3.eth.getAccounts((error, accounts) => {
          simpleStorage.at(contractAddress).then(instance => {
            simpleStorageInstance = instance
    
            /*simpleStorage.deployed().then((instance) => {
            simpleStorageInstance = instance // 部署本地Ganache*/
            console.log("合约实例获取成功")
          })
          .then(result => {
            return simpleStorageInstance.getRandomWord(this.state.random)
          })
          .then(result => {
                    console.log("读取成功", result)
                    if(result[1]!=this.setState.word){
                        this.setState({
                            animate: this.state.out
                        })
                        setTimeout(() => {
                            this.setState({
                                count: result[0].c[0],
                                word: result[1],
                                from: result[2],
                                timestamp: result[3],
                                animate: this.state.in,
                                firstTimeLoad: false
                            })
                        }, 2000)
                    }else{
                        this.setState({
                            firstTimeLoad: false
                        })
                    }
            this.randerWord()
          })
    
        })
      }
    
      // 循环从区块上随机读取留言
      randerWord() {
        setInterval(() => {
          let random_num = Math.random() * (this.state.count? this.state.count: 0)
          this.setState({
            random: parseInt(random_num)
          })
          console.log("setInterval读取", this.state.random)
          simpleStorageInstance.getRandomWord(this.state.random)
          .then(result => {
                    console.log("setInterval读取成功", result)
                    if(result[1]!=this.setState.word){
                        this.setState({
                            animate: this.state.out
                        })
                        setTimeout(() => {
                            this.setState({
                                count: result[0].c[0],
                                word: result[1],
                                from: result[2],
                                timestamp: result[3],
                                animate: this.state.in
                            })
                        }, 2000)
                    }
          })
        }, 10000)
      }
    
       // 写入区块链
      setWord(){
            if(!this.state.input) return
            this.setState({
                loading: true
            })
        let timestamp = new Date().getTime()
        simpleStorageInstance.setWord(this.state.input, String(timestamp), {from: this.state.web3.eth.accounts[0]})
        .then(result => {
                this.setState({
                    loadingTip: this.state.successTip
                })
                setTimeout(() => {
                    this.setState({
                        loading: false,
                        input: '',
                        loadingTip: this.state.waitingTip
                    })
                }, 1500)
    
            })
            .catch(e => {
                // 拒绝支付
                this.setState({
                    loading: false
                })
            })
      }
      // 时间戳转义
      formatTime(timestamp) {
          let date = new Date(Number(timestamp))
          let year = date.getFullYear()
          let month = date.getMonth() + 1
          let day = date.getDate()
          let hour = date.getHours()
          let minute = date.getMinutes()
          let second = date.getSeconds()
          let fDate = [year, month, day, ].map(this.formatNumber)
          return fDate[0] + '年' + fDate[1] + '月' + fDate[2] + '日' + ' ' + [hour, minute, second].map(this.formatNumber).join(':') 
      }
      /** 小于10的数字前面加0 */
      formatNumber(n) {
          n = n.toString()
          return n[1] ? n : '0' + n
      }
    
    }

    运行项目

    使用npm start启动项目,浏览器的3000端口运行。
    效果如下(一定要连接到Ropoetn Test Network网络才可以)。
    这里写图片描述

    总结

    这样我们就开发出了我们的第一个DAPP,体会了开发的基本流程。

    • 明确项目需求
    • 确定开发环境
    • 编写、测试、部署合约
    • 设计前端页面,使前后端交互
    • 发布测试

    项目源码

    GitHub

    参考文章

    Ludis的博文

  • 相关阅读:
    MongoDB用户及数据库管理命令
    Windows 10 Enterprise 2016 LTSB / 2019 LTSC下载与激活
    mongodb 3.6 集群搭建:分片+副本集
    nginx设置目录浏览及解决中文乱码问题
    如何取消Paypal自动付款功能
    Linux硬盘性能测试工具
    用expect实现SCP/SSH自动输入密码登录
    Redmine简易安装与系统优化
    MySQL数据目录(data)迁移
    忘掉Ghost!利用Win10自带功能,玩转系统备份&恢复 -- 系统重置
  • 原文地址:https://www.cnblogs.com/qq874455953/p/10264443.html
Copyright © 2020-2023  润新知