基于 web3.js ipfs 实现的dapp

创建项目
1.truffle unbox webpack
创建完成后
app文件夹用来存放 前台页面
contracts 用来存放合约代码
migrations 用来部署项目
node_modules 依赖文件
test 测试
sha3 跟改为 keccak256
keccak256(abi.encodePacked(_amount, _secret));
1通过 truffle framework和Solidity实现合约代码,并将其部署到truffle develop 自带的测试网络中,摒弃在truffle console中自由交互
使用ipfs通过命令安装并与之交互
后端实现完成后,构建web前端与合约喝ipfs进行交互
安装mongodb 并设计数据库结构来存储参评
数据库启动后 实现监听合约事件的nodejs 服务端代码,将请求记录到控制台,然后执行将产品插入数据库
我们将更新我的们的前端,从数据库而不是区块链中查找产品
实现托管合同和相应的前端,参与者可以向买方/卖方发放或者退款
在contracts 目录下创建 EcommerceStore.sol文件
pragma solidity ^0.4.17;
pragma solidity ^0.4.17;

contract EcommerceStroe {
enum ProductStatus{
// 开始买
Open,
//结束
Sold,
//没人买
Unsold
}
//产品
enum ProductCondition{
New,
Used
}

uint public productIndex;
//通过卖家的钱包地址找到卖家的详细信息
mapping(address =>mapping(uint => Product)) stores;
//卖家的钱包地址
mapping(uint => address ) productIdInStore;
//存取投标的相关信息
struct Bid{
address bidder;
uint productId;
uint value;
bool revealed;

}
struct Product{
uint id; // 产品id
string name; // 产品名字
string category;// 分类
string imageLink;//图片的hash
string descLink;//图片描述信息的hash
uint auctionStartTime;//开始竞标的时间
uint auctionEndTime;//竞标结束的时间
uint startPrice;//拍卖价格
address highestBidder;//赢家的钱包地址
uint highestBid;//赢家竞标的价格
uint secondHighestBid;//第二高的这个人的地址
uint totalBids;//一共有多少人参与竞标
ProductStatus status; //产品的状态
ProductCondition condition;// 是新的还是旧的
//存取投标人的信息
mapping(address =>mapping(bytes32 => Bid)) bids;
}
//构造函数
function EcommerceStore() public{
productIndex = 0;
}
/* 添加产品到区块链/
function addProductToStore(string _name, string _category, string _imageLink, string _descLink, uint _auctionStartTime, uint _auctionEndTime, uint _startPrice, uint _productCondition) public {
//断言 其实时间是否小于竞标时间
require(_auctionStartTime < _auctionEndTime);
//产品id直接让他自+1
productIndex += 1;
Product memory product = Product(productIndex, _name, _category, _imageLink, _descLink, _auctionStartTime, _auctionEndTime, _startPrice, 0, 0, 0, 0, ProductStatus.Open, ProductCondition(_productCondition));
stores[msg.sender][productIndex] = product;
productIdInStore[productIndex] = msg.sender;
}
/
通过产品ID读取产品信息 */
function getProduct(uint _productId)view public returns(uint, string, string, string, string, uint, uint, uint, ProductStatus, ProductCondition) {
Product memory product = stores[productIdInStore[_productId]][_productId];
//元组
return (product.id, product.name, product.category, product.imageLink, product.descLink, product.auctionStartTime, product.auctionEndTime, product.startPrice, product.status, product.condition);
}
//投奔
function bid(uint _productId, bytes32 _bid) payable public returns(bool) {
Product storage product = stores[productIdInStore[_productId]][_productId];
//当前时间必须大于产品开始竞标的时间
require(now >= product.auctionStartTime);
//当前时间必须小于产品结束竞标的时间
require(now <= product.auctionEndTime);
require(msg.value > product.startPrice);
//投奔的时候信息为‘’
require(product.bids[msg.sender][_bid].bidder == 0);
//存储竞标人的信息msg.value 用于迷惑别人的价格
product.bids[msg.sender][_bid] = Bid(msg.sender, _productId, msg.value, false);
//统计投标人的数量
product.totalBids += 1;
return true;
}
//投标公告
function revealBid(uint _productId, string _amount, string _secret) public {
Product storage product = stores[productIdInStore[_productId]][_productId];
require(now > product.auctionEndTime);
bytes32 sealedBid = sha3(_amount,_secret);
//msg.sender 投标人的钱包地址
Bid memory bidInfo = product.bids[msg.sender][sealedBid];
require(bidInfo.bidder > 0);
require(bidInfo.revealed == false);

 uint refund;//退款
 //将字符串转化成uint 类型
 uint amount = stringToUint(_amount);

if (bidInfo.value < amount) {

 refund = bidInfo.value;

} else {

 if (address(product.highestBidder) == 0) {
   product.highestBidder = msg.sender;
   product.highestBid = amount;
   product.secondHighestBid = product.startPrice;
   refund = bidInfo.value - amount;
 } else {
   if (amount > product.highestBid) {
     product.secondHighestBid = product.highestBid;
     // 退款
     product.highestBidder.transfer(product.highestBid);
     product.highestBidder = msg.sender;
     product.highestBid = amount;
     refund = bidInfo.value - amount;
   } else if (amount > product.secondHighestBid) {
     product.secondHighestBid = amount;
     refund = amount;
   } else {
     refund = amount;
   }
 }
 if (refund > 0) {
   msg.sender.transfer(refund);
   //表示公告成功
   product.bids[msg.sender][sealedBid].revealed = true;
 }

}
}

function highestBidderInfo(uint _productId) view public returns(address, uint, uint) {
Product memory product = stores[productIdInStore[_productId]][_productId];
return (product.highestBidder, product.highestBid, product.secondHighestBid);
}
//查询当前产品投标人的数量
function totalBids(uint _productId) view public returns(uint) {
Product memory product = stores[productIdInStore[_productId]][_productId];
return product.totalBids;
}
// 将字符串转换成uint类型
function stringToUint(string s) pure private returns(uint) {
bytes memory b = bytes(s);
uint result = 0;
for (uint i = 0; i < b.length; i++) {
if (b[i] >= 48 && b[i] <= 57) {
result = result * 10 + (uint(b[i]) - 48);
}
}
return result;
}
}

部署项目
在migrations下 2_deploy_contracts.js 修改代码
var EcommerceStroe = artifacts.require('./EcommerceStroe.sol')
module.exports = function (deployer) {
deployer.deploy(EcommerceStroe)
}
启动命令行到根目录
truffle deveplop
进行编译
compile
部署合约
migrate
添加启始价格
amt_1 =web3.toWei(1,'ether')
获取当前时间
current_time = Math.round(new Date() /1000);
EcommerceStore.deployed().then(function(i){i.addProductToStore('iphone 6s','Cell Phones & Accessories','imagelink','desclink',current_time,current_time +200, amt_1,0).then(function(f) {console.log(f)})});
查看添加的合约
var c
EcommerceStore.deployed().then((i)=>{ c=i});
//查看值
c.productIndex()
查看产品信息
EcommerceStore.deployed().then(function(i){i.getProduct.call(1).then(functino(f){console.log(f)})})
安装依赖包 对文件进行加密
插件 安装
第一种:npm install --save-dev ethereumjs-util
安装依赖包 对文件进行加密
找到package.json
在“webpack-dev-server” 下添加
第二种:"webpack-dev-server":"^2.3.0"
打开控制台
当前目录下 truffle develop
进行编译
compile
进行部署
migrate --reset
产品集成ipfs
找到package.json
在“webpack-dev-server” 下添加
"ipfs-api":"14.1.2"
把ipfs集成到项目里
在app/javascripts
import "../stylesheets/app.css";

import {
default as Web3
} from 'web3';
import {
default as contract
} from 'truffle-contract'
import ecommerce_store_artifacts from '../../build/contracts/EcommerceStore.json'

var EcommerceStore = contract(ecommerce_store_artifacts);

const ipfsAPI = require('ipfs-api');
const ethUtil = require('ethereumjs-util');

const ipfs = ipfsAPI({
host: 'localhost',
port: '5001',
protocol: 'http'
});

window.App = {
start: function() {
var self = this;
EcommerceStore.setProvider(web3.currentProvider);
renderStore();

var reader;

$("#product-image").change(function(event) {
  console.log("选择图片");
  const file = event.target.files[0]
  reader = new window.FileReader()
  reader.readAsArrayBuffer(file)
});

$("#add-item-to-store").submit(function(event) {
  console.log("保存数据到ipfs和区块链");
  const req = $("#add-item-to-store").serialize();
  let params = JSON.parse('{"' + req.replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}');
  let decodedParams = {}
  Object.keys(params).forEach(function(v) {
    decodedParams[v] = decodeURIComponent(decodeURI(params[v]));
  });
  saveProduct(reader, decodedParams);
  event.preventDefault();
});

if ($("#product-details").length > 0) {
  console.log("window.location");
  console.log(window.location);
  console.log("Search Params = " + new URLSearchParams(window.location))
  let productId = new URLSearchParams(window.location.search).get('Id');
  console.log(productId);
  renderProductDetails(productId);
}

$("#bidding").submit(function(event) {
  $("#msg").hide();
  let amount = $("#bid-amount").val();
  let sendAmount = $("#bid-send-amount").val();
  let secretText = $("#secret-text").val();
  let sealedBid = '0x' + ethUtil.sha3(web3.toWei(amount, 'ether') + secretText).toString('hex');
  let productId = $("#product-id").val();
  console.log(sealedBid + " for " + productId);
  EcommerceStore.deployed().then(function(i) {
    i.bid(parseInt(productId), sealedBid, {
      value: web3.toWei(sendAmount),
      from: web3.eth.accounts[0],
      gas: 440000
    }).then(
      function(f) {
        $("#msg").html("Your bid has been successfully submitted!");
        $("#msg").show();
        console.log(f)
      }
    )
  });
  event.preventDefault();
});

$("#revealing").submit(function(event) {
  $("#msg").hide();
  let amount = $("#actual-amount").val();
  let secretText = $("#reveal-secret-text").val();
  let productId = $("#product-id").val();
  EcommerceStore.deployed().then(function(i) {
    i.revealBid(parseInt(productId), web3.toWei(amount).toString(), secretText, {
      from: web3.eth.accounts[0],
      gas: 440000
    }).then(
      function(f) {
        $("#msg").show();
        $("#msg").html("Your bid has been successfully revealed!");
        console.log(f)
      }
    )
  });
  event.preventDefault();
});

},
};

function renderProductDetails(productId) {
console.log("renderProductDetails");
console.log(productId);
EcommerceStore.deployed().then(function(i) {
i.getProduct(productId).then(function(p) {
console.log("getProduct");
console.log(p[4]);
let content = "";
ipfs.cat(p[4]).then((stream) => {

    console.log(stream);
    let content = Utf8ArrayToStr(stream);
    $("#product-desc").append("
" + content + "
"); }); $("#product-image").append(""); $("#product-price").html(displayPrice(p[7])); $("#product-name").html(p[1].name); $("#product-auction-end").html(displayEndHours(p[6])); $("#product-id").val(p[0]); $("#revealing, #bidding").hide(); let currentTime = getCurrentTimeInSeconds(); if (currentTime < p[6]) { $("#bidding").show(); } else if (currentTime - (60) < p[6]) { $("#revealing").show(); } })

})
}

function Utf8ArrayToStr(array) {
var out, i, len, c;
var char2, char3;

out = "";
len = array.length;
i = 0;
while (i < len) {
c = array[i++];
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12:
case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
default:
break;
}
}

return out;
}

function getCurrentTimeInSeconds() {
return Math.round(new Date() / 1000);
}

function displayPrice(amt) {
return 'Ξ' + web3.fromWei(amt, 'ether');
}

function displayEndHours(seconds) {
let current_time = getCurrentTimeInSeconds()
let remaining_seconds = seconds - current_time;

if (remaining_seconds <= 0) {
return "Auction has ended";
}

let days = Math.trunc(remaining_seconds / (24 * 60 * 60));

remaining_seconds -= days * 24 * 60 * 60
let hours = Math.trunc(remaining_seconds / (60 * 60));

remaining_seconds -= hours * 60 * 60

let minutes = Math.trunc(remaining_seconds / 60);

if (days > 0) {
return "Auction ends in " + days + " days, " + hours + ", hours, " + minutes + " minutes";
} else if (hours > 0) {
return "Auction ends in " + hours + " hours, " + minutes + " minutes ";
} else if (minutes > 0) {
return "Auction ends in " + minutes + " minutes ";
} else {
return "Auction ends in " + remaining_seconds + " seconds";
}
}

function saveProduct(reader, decodedParams) {
let imageId, descId;
saveImageOnIpfs(reader).then(function(id) {
imageId = id;
saveTextBlobOnIpfs(decodedParams["product-description"]).then(function(id) {
descId = id;
saveProductToBlockchain(decodedParams, imageId, descId);
})
})
}

function saveProductToBlockchain(params, imageId, descId) {
console.log(params);
let auctionStartTime = Date.parse(params["product-auction-start"]) / 1000;
let auctionEndTime = auctionStartTime + parseInt(params["product-auction-end"]) * 24 * 60 * 60

EcommerceStore.deployed().then(function(i) {
i.addProductToStore(params["product-name"], params["product-category"], imageId, descId, auctionStartTime,
auctionEndTime, web3.toWei(params["product-price"], 'ether'), parseInt(params["product-condition"]), {
from: web3.eth.accounts[0],
gas: 440000
}).then(function(f) {
console.log(f);
("#msg").html("Your product was successfully added to your store!");
})
});
}

function saveImageOnIpfs(reader) {
return new Promise(function(resolve, reject) {
const buffer = Buffer.from(reader.result);
ipfs.add(buffer)
.then((response) => {
console.log(response)
resolve(response[0].hash);
}).catch((err) => {
console.error(err)
reject(err);
})
})
}

function saveTextBlobOnIpfs(blob) {
return new Promise(function(resolve, reject) {
const descBuffer = Buffer.from(blob, 'utf-8');
ipfs.add(descBuffer)
.then((response) => {
console.log(response)
resolve(response[0].hash);
}).catch((err) => {
console.error(err)
reject(err);
})
})
}

function renderStore() {
EcommerceStore.deployed().then(function(i) {
i.getProduct.call(1).then(function(p) {
("#product-list").append(buildProduct(p, 2));
});
i.getProduct.call(3).then(function(p) {
("#product-list").append(buildProduct(p, 4));
});
i.getProduct.call(5).then(function(p) {
("#product-list").append(buildProduct(p, 6));
});
i.getProduct.call(7).then(function(p) {
$("#product-list").append(buildProduct(p, 7));
});
});
}

function buildProduct(product, id) {
console.log("buildProduct");
console.log(id);
let node = $("

");
node.addClass("col-sm-3 text-center col-margin-bottom-1");
node.append("");
node.append("
" + product[1] + "
");
node.append("
" + product[2] + "
");
node.append("
" + new Date(product[5] * 1000) + "
");
node.append("
" + new Date(product[6] * 1000) + "
");
node.append("
Ether " + product[7] + "
");
node.append("Show")
return node;
};

window.addEventListener('load', function() {

if (typeof web3 !== 'undefined') {
console.warn("Using web3 detected from external source. If you find that your accounts don't appear or you have 0 MetaCoin, ensure you've configured that source properly. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask")

window.web3 = new Web3(web3.currentProvider);

} else {
console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

App.start();
});
在根目录创建种子文件seed.js
Eutil = require('ethereumjs-util');
EcommerceStore = artifacts.require("./EcommerceStore.sol");
module.exports = function(callback) {
current_time = Math.round(new Date() / 1000);
amt_1 = web3.toWei(1, 'ether');
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 5', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 200, 2amt_1, 0).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 5s', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 400, 3
amt_1, 1).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 6', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 14, amt_1, 0).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 6s', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 86400, 4amt_1, 1).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 7', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 86400, 5
amt_1, 1).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('Jeans', 'Clothing, Shoes & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 86400 + 86400 + 86400, 5*amt_1, 1).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.productIndex.call().then(function(f){console.log(f)})});
}

编译
compile
migrate
var c
c = EcommerceStore.deployed().then((i)=>{c = i});
c.productIndex();
//插入6条数据
exec seed.js
//再次查询数据
c.productIndex();
BigNumber { s: 1, e: 0, c: [ 6 ] }

前端页面
app/index.html



Decentralized Ecommerce Store







Ecommerce Store


Total Products:

List Item


Categories







Products To Buy








Products In Reveal Stage











app/stylesheets/app.css
.col-margin-bottom-1 {
margin-bottom: 1em;
}

a {
cursor: pointer;
}

.navbar.main {
background: #5f6d7f;
color: white;
}

.navbar.main .list.nav-link {
background: #01bf86;
}

.navbar.main a {
color: white;
}

.navbar ul {
list-style: none;
}

.side-bar.col-sm-2 {
margin-left: 1em;
border-right: 1px solid #5f6d7f;
}

product-list.row {

margin-left: 0;
}

product-list .row,

product-finalize-list {

margin-top: 2em;
}

.row h2,
.categories a,

categories a {

color: #5f6d7f;
}

product-list a,

product-reveal-list a,

product-list .row > div {

color: #01bf86;
}

.container-fluid .row {
margin-left: 0px;
}

product-list img,

product-reveal-list img,

product-finalize-list img {

width: 150px;
height: 150px;
}

.container-fluid .title {
font-size: 1.2em;
font-weight: bold;
margin-top: 1em;
}

product-list .details,

.container-fluid .title,

product-finalize-list a {

color: #5f6d7f;
}

.product .details {
text-decoration: underline;
}

.time {
font-style: italic;
}

product-finalize-list .row > div{

color: #e76e6e;
}

product-details-page {

color: #5f6d7f;
background-color: #e9ebef;
}

product-details-page h4 {

margin-top: 0px;
font-size: 20px;
}

product-details-page h4.align {

margin-left: 15px;
font-style: italic;
}

product-details-page #product-price {

color: #01bf86;
}

product-details-page .bid-details,

product-details-page #product-desc {

padding: 2em;
}

product-details-page #product-image {

padding: 2em 1em;
/border: 1px solid #b2bcc8;/
border-radius: 10px;
background-color: white;
}

product-details-page h1 {

color: #5f6d7f;
}

product-details-page h2.product-name {

color: #01bf86;
margin-top: 0;
margin-bottom: 1em
/font-size: 1.5em;/
}

product-details-page #product-auction-end {

color: #e76e6e;
font-style: italic;
font-size: 17px;
}

product-details-page #product-reveal {

margin-top: 3em;
}

product-details-page button.form-submit {

background-color: #01bf86;
color: white;
}

product-details-page #product-desc {

padding: 2em;
}

npm run dev 启动项目

你可能感兴趣的:(基于 web3.js ipfs 实现的dapp)