NodeJS区块链实践(2)与区块链交互,基于区块链的web服务

前面我们搭建了一个简单的私链,这一节我们看看如何与区块链进行交互。

我们这里使用Express来搭建我们的服务端应用。

Express

Express是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

使用 Express 可以快速地搭建一个完整功能的网站。

const bodyParser = require('body-parser');  //HTTP请求体解析中间件
const express = require('express')
const app = express()
app.use(bodyParser.json());

app.listen(8000, () => console.log('app listening on:localhost:8000...\nblockchain is running...'))

这是一个基于node.js的基本服务框架,下面用它来链接我们的区块链。

// import block and blockchain object
let Block = require("./block").block
let Blockchain = require("./blockchain").blockchain

// create blockchain
let blockchain = Blockchain().instance;

第一步、获取权限

在我们要在改变区块链上的数据之前,首先要获得账户权限。此时,我们需要post我们的地址,用来获得许可。这个许可不是永久的,需要在一定时间内完成上传数据,否则需要重新申请权限。

// story requests in app locals
// validateReq的key上address,value上数组,数组的第一个元素是认证窗口剩余时间,第二个元素是时间戳
app.locals.validateReq = {};

// 对validateReq的每个address的时间窗口进行倒计时
let count = function(obj) {
  let countDown = function(){
    Object.keys(obj).forEach(key => {  // 返回一个所有元素为字符串的数组
      obj[key][0]--;
      if(obj[key][0] == 0) {
        delete obj[key]; // 删除对象属性
      }
    });
  }
  setInterval(countDown, 1000);
}
// auto run count
count(app.locals.validateReq);

app.post('/requestValidation',(req,res) => {
   let { validateReq } = req.app.locals;  // 中间件使用locals可以使用req.app.locals
   let { address } = req.body;
   let validationWindow;
   let timeStamp;

   if(validateReq[address]) {
     validationWindow = validateReq[address][0];
     timeStamp = validateReq[address][1];
   } else {
     validationWindow = 300;  // 初始化认证窗口为300秒
     timeStamp =  new Date().getTime().toString().slice(0,-3);
     validateReq[address] = [validationWindow, timeStamp]
   }

   res.send({
     "address":address,
     "requestTimeStamp":timeStamp,
     "message":`${address}:${timeStamp}:starRegistry`,
     "validationWindow":validationWindow
   })

})

第二步,验证信息签名

这里我们需要用到比特币里的签名和验证的方法,所以需要引入bitcoinjs-lib和bitcoin-message两个库。上传数据时,我们要使用bitcoin-core客户端来生成签名,我们的web app会验证我们上传的签名是否正确。如果正确,才能上传数据,添加新区块。

const bitcoin = require("bitcoinjs-lib");
const bitcoinMessage = require("bitcoinjs-message");

// story requests in app locals
app.locals.validateReq = {};
app.locals.validateAddress = {};

app.post('/message-signature/validate', (req, res) => {
  let { validateReq, validateAddress } = req.app.locals;
  let { address, signature } = req.body;  // 申请验证时需要有签名信息

  if(validateReq[address]) {
    let timeStamp = validateReq[address][1];
    let message = `${address}:${timeStamp}:starRegistry`;
    let validationWindow = validateReq[address][0];

    // 对签名进行验证,成功则标记为valid
    let registerStar = bitcoinMessage.verify(message, address, signature);
    let messageSignature;

    if(registerStar){
      messageSignature = 'valid';
      validateAddress[address] = true;
    } else {
      messageSignature = 'invalid'
    }

    res.send({
      "registerStar": registerStar,
      "status": {
         "address": address,
         "requestTimeStamp": timeStamp,
         "message": message,
         "validationWindow": validationWindow,
         "messageSignature": messageSignature
        }
    })
  } else {
    res.send( `Address ${address} not found`)
  }
})

第三步,添加新区块

我们通过web服务来添加新的区块,我们这里的区块body储存的数据结构如下:

{
  "address": "142BDCeSGbXjWKaAnYXbMpZ6sbrSAo3DpZ",
  "star": {
    "dec": "14° 20'\'' 12.2",
    "ra": "13h 34m 1.3s",
    "story": "a test star"
  }

上面数据包括上传的账户地址以及star的坐标以及story,现在让我们通过一个post请求来上传数据。

app.post('/block', (req, res) => {
  let { address, star } = req.body;
  let { validateAddress } = req.app.locals;
  
  // 匹配所有双字节字符
  let reg = /[\x00-\xff]+/g;
  if(star && star.ra && star.dec && reg.test(star.story) && Buffer.byteLength(star.story) <= 500) {
    if (validateAddress[address]) {
      let { ra, dec } = star;
      let story = Buffer(star.story).toString('hex'); //strTohex(star.story)
      let body = {
        address,
        star:{ra, dec, story}
      }

      let newBlock = new Block(body)
      blockchain.addBlock(newBlock).then(()=>{
        blockchain.getBlockHeight().then((value) => {
          blockchain.getBlockByHeight(value).then((value) => {
            res.send(value)
          })
        })
      });
      // 删除认证的账号
      delete validateAddress[address];
      delete validateReq[address];
    } else {
      res.send(`Address ${address} not found in validated addresses`)
    }
  } else {
    res.send('incorret star contents')
  }
})

现在,我们可以实现通过height来获取区块,即访问http://localhost:8000/block/{height}来获得区块数据的返回值。

app.get('/block/:height', (req, res) => {
  let { height } = req.params;
  blockchain.getBlock(height).then((value) => {
    if(value.height != 0){
      value.body.star.storyDecoded = Buffer(value.body.star.story, 'hex').toString(); //hexTostring(value.body.star.story)
      res.send(value)
    } else {
      res.send(value)
    }
  }).catch(err => res.send(`The current block height is less than ${height}`))
})

然后通过hash来获取区块,即访问http://localhost:8000/block/{hash}来获得区块数据的返回值。

app.get('/block/:hash', (req, res) => {
  let { hash } = req.params;
  blockchain.getBlockByHash(hash).then((value) => {
    if(value.body.star){
       value.body.star.storyDecoded = Buffer(value.body.star.story, 'hex').toString();
       res.send(value)
    } else {
       res.send(value)
    }
  }).catch(err => {res.send( `hash ${hash} not found in blockchain` )})
})

至此,我们实现了在web端与区块链的交互。

 

你可能感兴趣的:(nodejs区块链)