新手教程 以传统的 WEB 开发方式,来举例理解 Dapp 开发

Surou · 2018年09月20日 · 最后由 penglipengnb 回复于 2019年02月12日 · 522 次阅读
本帖已被设为精华帖!

前言

常规的讲解DApp开发的文章,大都直接从区块链方式讲起,对于没有接触过区块链的同学,可能不太好理解,今天我们尝试从传统的web开发,对比讲解DApp开发。

对于目前常见的DApp大都是以Web的形式,其实DApp并不局限于Web形式,也可以是PC客户端,也可以是App。为了更好理解和演示,我们将以常见的Web形式的DApp讲起。

演示实例

我们将要演示的DApp例子为,一个计算器,合约中计算两个输入的值,并将值存入链上,并提供查询的接口。

如果我们拿常规的Web去实现下这个App。基本分为3部分,前端(获取输入的值),服务器Api接收程序(接收前端输入的值,并计算结果,并将结果写入数据库,以及提供查询的接口),数据库(存储计算后的值)。

实现和部署对比

实现 Web App Web DApp
前端 普通html 普通html
服务器Api接收程序 php开发的api程序 执行链上部署的合约
数据库 mysql 写入链上对应的RAM
服务器部署 云服务器 前端放在云服务器,合约部署在EOS网络

传统Web实现

1.前端

<form method="post" name="form" action="http://www.xxx.xx/postGet.php">
  <table >
      <tr>
       <td>第一个参数:</td>
       <td><input type="text" name="FirstParm"/></td>
      </tr>
      <tr>
       <td>第二个参数:</td>
       <td><input type="text" name="SecondParm"/></td>
      </tr>
      <tr>
       <td><input type="submit" name="Submit" value="计算两数和"/></td>
      </tr>
  </table>
</form>

2.创建数据库

CREATE DATABASE results
CREATE TABLE result(name CHAR(13), iresult int);

3.Api接收程序

<?php
$firstparm = $_POST[FirstParm'];
$secondparm = $_POST['SecondParm'];
$result = $firstparm + $secondparm;
    $con = mysql_connect("localhost","cryptokylinq","5KKu8Nmm4XUavcZFzpvdGG5Mc9Rkg6LiwxuqXkSokEHKSXKXs9b");
    if (!$con) {
      die('Could not connect: ' . mysql_error());
      }
    mysql_select_db("results", $con);
    mysql_query("INSERT INTO result (iresult) VALUES ($result)");
    mysql_close($con);
?>

查询

<?php
$con = mysql_connect("localhost","cryptokylinq","5KKu8Nmm4XUavcZFzpvdGG5Mc9Rkg6LiwxuqXkSokEHKSXKXs9b");
if (!$con) {
  die('Could not connect: ' . mysql_error());
  }
mysql_select_db("results", $con);
$result = mysql_query("SELECT * FROM result");
while($row = mysql_fetch_array($result)) {
  echo $row['iresult'] ;
  echo "<br />";
  }
mysql_close($con);
?>

上面三步简单的完成了,常规Web的计算,写入,查询操作。

Web DApp实现

1.编写合约

//Calculator.hpp
#include <eosiolib/asset.hpp>
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

class Calculator : public eosio::contract {
public:
    Calculator(account_name self)
            : eosio::contract(self)
            ,results(_self, _self){}

public:
    void doit(account_name from, const uint64_t firstParm, const uint64_t secoundParm);

private:
//@abi table results i64
    struct result{
        account_name name;
        uint8_t iresult;
        uint64_t primary_key()const { return name; }
        EOSLIB_SERIALIZE(result, (name)(iresult))
    };

    typedef eosio::multi_index<N(results),result> result_table;
    result_table results;
};
EOSIO_ABI( Calculator, (doit) )
#include "Calculator.hpp"


void Calculator::doit(account_name from, const uint64_t firstParm, const uint64_t secoundParm){
    require_auth(from);

    eosio_assert(firstParm >= 1, "firstParm mast >= 0"); //没有实际意义 只是做个数值的判断演示
    auto account_itr = results.find(from);
    if(account_itr == results.end()){
        account_itr = results.emplace(_self, [&](auto& result){
            result.name = from;
            result.iresult = firstParm + secoundParm;
        });
    }
    else{
        results.modify(account_itr, 0, [&](auto& result) {
            result.iresult = firstParm + secoundParm;
        });
    }
}

合约中包含了数据库的创建格式,合约部署后,相当于,数据库也创建好了,以及数据写入需要执行的api接口

2.合约编译,需要将合约初步编译,才能部署到链上

  • 生成API文件
suroudeMacBook-Pro:Calculator surou$ eosiocpp -g Calculator.abi Calculator.cpp
  • 生成WAST文件
suroudeMacBook-Pro:Calculator surou$ eosiocpp -o Calculator.wast Calculator.cpp

执行完成后目录文件如下

suroudeMacBook-Pro:Calculator surou$ ls
Calculator.abi  Calculator.cpp  Calculator.hpp  Calculator.wasm Calculator.wast

3.创建麒麟测试网的测试账号,用于合约的部署,以及使用资源的购买

{
    msg: "succeeded",
    keys: - {
    active_key: - {
        public: "EOS6L7pr3AaKekTs1dbratDq1PutoSdmBWJFwbLcStsnKBbJtNUws",
        private: "5KKu8Nmm4XUavcZFzpvdGG5Mc9Rkg6LiwxuqXkSokEHKSXKXs9b"
    },
    owner_key: - {
        public: "EOS89vNXgGrkM4GYFAav78FgnX94QzVXYtcEKiKicSq4YcykPmnC7",
        private: "5KZbLRwZAZQJQKS6sM1RL1d2Af9s49zA9qka9f63ce7J2jUDWwQ"
    }
    },
    account: "cryptokylinq"
}

一个账号一般有两对秘钥,对应两个权限owner(类似网站后台的超级管理员)和active(部分权限的管理员),当然还可以创建多个自由配置权限的秘钥对。在EOS网络里,谁拥有这个私钥,谁就拥有对应账号的对应权限。相同的秘钥对可以创建多个账号。

{
    msg: "succeeded"
}

申请来的测试EOS代币,将用于购买EOS网络上的CPU,NET,RAM资源(RAM为购买,CPU和NET是按抵押EOS多少,计算分配你多少资源使用)。相当于云服务器中的增加对应的硬件资源。CPU和NET与云服务中的一样,计算和网络资源。RAM相当于云服务器中的RAM+磁盘存储,比如发起交易时会临时消耗,隔一段时间(暂时未确定恢复时间的规则,应该小于三天,稍后查证后,在做补充),如果是合约存储数据,那就相当于写入了磁盘,如果数据不删除的话,会持续占用。

下面开始购买所需的资源,之前统计一般部署合约需要消耗300k左右的 RAM(包含临时消耗)。 CPU和NET基本分别抵押 0.1EOS就可以了。

由于我们要操作账号,所以我们要先创建个钱包,可以使用eosio 自带的keosd,也可以使用其他三方的app 钱包中购买资源。 我们在开发环境,所以直接使用keosd操作了,

  • 创建钱包
suroudeMacBook-Pro:eosio-wallet surou$ cleos  wallet create --to-console
Creating wallet: default
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5HxNY7tKndZp8c96afmhaHezq2CA9x1dbziYXzJcvK7sVdf7kQq"

记得备份这个钱包的解锁密码"PW5HxNY7tKndZp8c96afmhaHezq2CA9x1dbziYXzJcvK7sVdf7kQq"

  • 将上面创建的账号cryptokylinq的私钥导入新创建的钱包 (一般常规操作只需要导入active权限的私钥)
suroudeMacBook-Pro:eosio-wallet surou$ cleos wallet import
private key: imported private key for: EOS6L7pr3AaKekTs1dbratDq1PutoSdmBWJFwbLcStsnKBbJtNUws

导入完成后,购买所需的RAM,CPU,NET资源 购买10EOS的RAM

suroudeMacBook-Pro:contracts surou$ cleos -u http://kylin.fn.eosbixin.com system buyram cryptokylinq cryptokylinq "10 EOS"
executed transaction: d59370bcb3dbf1d3aaa1b5c92de23283603c6d9bfc2e38e8def5e0de8eabe6e1  128 bytes  1511 us
#         eosio <= eosio::buyram                {"payer":"cryptokylinq","receiver":"cryptokylinq","quant":"10.0000 EOS"}
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"9.9500 EOS","memo":"buy ram"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"9.9500 EOS","memo":"buy ram"}
#     eosio.ram <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"9.9500 EOS","memo":"buy ram"}
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.0500 EOS","memo":"ram fee"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.0500 EOS","memo":"ram fee"}
#  eosio.ramfee <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.0500 EOS","memo":"ram fee"}

CPU NET分别抵押10 EOS (实际使用量,一般在分别1 EOS就够了,测试网不花钱,可劲豪)

suroudeMacBook-Pro:contracts surou$ cleos -u http://kylin.fn.eosbixin.com system delegatebw cryptokylinq cryptokylinq "10 EOS" "10 EOS"
executed transaction: 4df295028046be07f669fd3dca1b496434f5ee36efb0cd6f31a738ff6e7b5602  144 bytes  2021 us
#         eosio <= eosio::delegatebw            {"from":"cryptokylinq","receiver":"cryptokylinq","stake_net_quantity":"10.0000 EOS","stake_cpu_quant...
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"20.0000 EOS","memo":"stake bandwidth"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"20.0000 EOS","memo":"stake bandwidth"}
#   eosio.stake <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"20.0000 EOS","memo":"stake bandwidth"}

此时RAM,CPU,NET资源都准备好了,相当于云服务器硬件配置都买好了 下面开始部署合约,相当于传统的Web开发,将API程序部署到云服务器

suroudeMacBook-Pro:contracts surou$ cleos -u http://kylin.fn.eosbixin.com  set contract cryptokylinq Calculator/ -p  
Reading WASM from Calculator/Calculator.wasm...
Publishing contract...
executed transaction: f0086795a65644a96e282b165e1fc146869a37cfd6a2e51528367f7594d4d21f  3056 bytes  1329 us
#         eosio <= eosio::setcode               {"account":"cryptokylinq","vmtype":0,"vmversion":0,"code":"0061736d01000000015d1060047f7e7e7e0060000...
#         eosio <= eosio::setabi                {"account":"cryptokylinq","abi":"0e656f73696f3a3a6162692f312e30000206726573756c740002046e616d65046e6...

下一步我们直接测下合约action接口,相当于直接测试云服务器的API接口

suroudeMacBook-Pro:contracts surou$ cleos -u http://kylin.fn.eosbixin.com  push action cryptokylinq doit '["cryptokylinq","1","2"]' -p cryptokylinq
executed transaction: 7c0de68f77a656f98cd54e03f37dbb00a1a325cee844614968d7d05a2668f36c  120 bytes  1834 us
#  cryptokylinq <= cryptokylinq::doit           {"from":"cryptokylinq","firstParm":1,"secoundParm":2}

查询下结果

suroudeMacBook-Pro:contracts surou$ cleos -u http://kylin.fn.eosbixin.com  get table cryptokylinq cryptokylinq results
{
  "rows": [{
      "name": "cryptokylinq",
      "iresult": 3
    }
  ],
  "more": false
}

可以看到,已经将结果写到了链上 "iresult": 3,相当于常规Web程序数据库中已存入数据 目前此DApp对比常规Web程序已经完成了,API和数据库部分,下面开始完成前端的调用

DApp前端

前端与链交互,是通过rpc进行操作的,并且eosio 官方也提供了 eosjs.js 开发库,方便操作。 html中调用的关键js代码为

import EOS from 'eosjs'
const EOS_CONFIG = {
  contractName: "cryptokylinq", // Contract name 更好理解的应该是合约的部署所在的账号名
  contractReceiver: "cryptokylinq", // User executing the contract (should be paired with private key)
  clientConfig: {
    keyProvider: '5KKu8Nmm4XUavcZFzpvdGG5Mc9Rkg6LiwxuqXkSokEHKSXKXs9b', // Your private key
    httpEndpoint: 'http://api-kylin.starteos.io', // EOS http endpoint
    chainId: '5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191'
  }
}
//执行合约
DoContract(){
let eosClient = EOS(EOS_CONFIG.clientConfig)
    eosClient.contract(EOS_CONFIG.contractName)
      .then((contract) => {
      //执行链上合约
        contract.doit(EOS_CONFIG.contractReceiver,"1","2", { authorization: [EOS_CONFIG.contractReceiver] })
          .then((res) => { console.log("Success") })
          .catch((err) => {console.log("Fail"); console.log(err) })
      })
}

查询结果

eos.getTableRows(true,"cryptokylinq","cryptokylinq","results")

返回

{
    "rows":[
        {
        "name":"cryptokylinq",
        "iresult":3
        }
    ],
        "more":false
}

至此完成了DApp从合约编写,合约部署,账号创建,合约调用,eosjs使用,一整套简单的流程。

共收到 3 条回复
Surou 将本帖设为了精华贴 09月21日 10:35

全面,简单易懂,非常适合入门👍

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册