什么是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: "*"
},
rinkeby:{
provider:function(){
return new webProvider(PRIVATE_KEY, API_URL)
},
network_id: "*" ,
gas: 4500000,
}
}
};
编写智能合约
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();
},
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();
},
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>
<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链接,选择上传过合约的那个账户;然后输入姓名和年龄点击更新】 最终项目的目录结构为:
|