javascript实现小型区块链功能
作者:TiMiGerry 发布时间:2024-04-18 09:29:10
区块链概念
狭义:区块链是一种按照时间顺序将数据区块以顺序相连的方式组合成的一种链式数据结构,并以密码方式保证的不可篡改和不可伪造的分布式账本。
一、挖矿(产生新区块)
首先,区块链是由每一个区块联系而形成的,在产生新区块之前必须先有一个最初始的区块,这个区块也叫创世区块。通过这个创世区块,不停地通过变化随机数(nonce)来计算出符合条件的区块。以下是创世区块基本信息:
const initBlock = {
index: 0,
data: 'hey,this is a block chain',
previousHash: '0',
timestamp: '1551806536961',
nonce: 80490,
hash: '0000352fb27dd1141fa7265833190a53e5776b1111e275db0d9a77bf840081e6'
};
index:是指每个区块的序号
data: 这里存放着区块中所有的信息,例如转账,余额等数据
previousHash: 指的是上一个区块的hash值,创世区块没有上一个,显示0即可
timestamp:指的是创建这个区块的时间
nonce:这个是随机数,挖矿就是通过不停变换这个nonce来计算出符合条件的哈希。
hash: 本区块的hash值,通过前面5个字段的信息进行hash运算得出的值。
接着,通过不停的hash运算计算出符合条件的哈希,即挖矿。挖矿也可以调节难度的大小,例如算出的哈希值必须前3位数必须为1或者末3位数必须为1等等,这个可以自行的去定义,只要最后留一个控制的开关,方便控制即可。可以在定义一个变量
哈希的计算:
.createHash('sha256')
.update(index + data + previousHash + timestamp + nonce)
.digest('hex')
_that.difficulty = 3 // 即前3位或者末3位数必须为1,数量越多难度越大
生成了符合条件的hash之后,则产生了新的区块,但是还要对这个区块进行校验看看是否有效,因为可能这是一个被篡改的非法的区块,也有可能和这个链没有任何关系的区块而仅仅只是符合上述哈希的规则而已。所以,需要进行一下校验,,前后区块的有效性。
isValidaBlock(newBlock,lastBlock) {
if (newBlock.index !== lastBlock.index+1) return false
if (newBlock.previousHash !== lastBlock.hash) return false
if (newBlock.timestamp <= lastBlock.timestamp) return false
if (newBlock.hash.slice(1 ,_that.difficulty) !== '1'.repeat(_that.difficulty)) return false
if (newBlock.hash !== this.computeHashForBlock(newBlock)) return false //确保随机数正确
// 都满足则返回true
return true
}
除了上面的校验之外,还需要使用上面这个函数对整一个chain进行一个每一个块的校验,以保证每一个块的信息是正确的,是没有被篡改过的是合法的。
二、构建P2P网络
区块链的网络是去中心化的,即没有中心服务器的网络,客户端不需要依赖中心服务器来获取或者处理数据。区块链网络中,有这许许多多的节点,每个节点都是一个独立的成员,他们既是客户端也是服务器,节点与节点直接都是点对点进行连接(peer-to-peer),不需要通过某一个中心服务器进行中转,所以,信息安全的角度来说,点对点的连接方式对信息私密性是非常可靠的。
虽然,区块链是通过点对点的连接方式进行数据传输,但是,在这之前还需要一个东西作为引导,这个就是种子节点。因为,两个节点之间他们可能不是处在同一个域下,他们之间想要联系,必须有一方知道对方的ip和端口,这样才能和对方联系上。节点ip和端口号,在这个节点创建出来之后,种子节点就会发给它在这个区块链中所有节点的ip和端口号同时记录下这个新伙伴的ip和端口号。
那么,新的节点拿到了这一份"通讯录"之后,就会给这个"通讯录"中的所有小伙伴发个消息,告诉他们有一位新的小伙伴加入,之后,其他节点收到了这个信息,也会在自己的"通讯录"中加上新伙伴的ip和端口号,相当于加入了白名单。这样新的节点接下来就可以和任意的的节点进行通信了。
下面用代码演示一下:
(res)=>{
_that.remotePeerInfo = res.data.data //1
_that.addPeersList(res.peersList) //2
_that.boardCast(_that.remotePeerInfo) //3
_that.blockChainUpdate(blockChain,blockData) //4
}
addPeersList(peers) {
peers.forEach(peer => {
if (!_that.peers.find(v => _that.isEqualPeer(peer, v))) {
_that.peers.push(peer)
}
})
}
boardCast(remotePeerInfo) {
this.peers.forEach(v => {
this.send(action, v.port, v.address)
})
}
blockChainUpdate(blockChain,blockData){
if(newChain.length === 1 ){
return
}
if(_that.isValidaChain(newChain) && newChain.length>_that.blockchain.length){
_that.blockchain = Object.assign({}, newChain)
}else{
console.log('error')
return
}
if (trans.every(v => _that.isValidTransfer(v))) {
_that.data = trans
}
}
1.保存种子节点传来的此新节点的信息包括ip和端口号,因为,新节点的ip和端口号是会有改变的情况。
2.接受种子节点传来的节点列表,将列表的节点遍历检查一下,没有相同的就写进列表中。
3.将新节点的信息广播到所有的节点上,同时接受到信息的节点更新一下节点列表
4.将区块链上信息同步一份都本地,同时对种子节点传来的blockchain进行每个区块的信息
三、转账交易
BTC的交易模型是使用的是UTXO
而这个小型区块链的交易模型使用的是最简单的方法。
区块链中"现金”,它是一个虚拟的东西就是一个字符串,来源于挖矿。每次挖矿成功都会有一定的奖励,得到的这些“钱”就可以在区块链网络中自由的转账交易。
在区块链中,进行记录转账交易的时候是需要一个加密的算法,把所有的信息进行加密之后再push到新区块中的data中,从而完成一笔新交易的记录。以BTC为例,BTC的加密算法是使用elliptic这个加密算法,elliptic是一个非对称性的加密算法,非对称的加密算法的特点就是,私钥是惟一的,只有拥有者才可以和他私钥对应的公钥进行校验 。 nodejs也有对应的库在github上搜索elliptic即可。
{
"privateKey": "34a425df3eb1f22fb6cb74b0e7298b16ffd7f3fb",
"publicKey": "ac208623a38d2906b090dbcf3a09378dfe79b77bf39c2b753ef98ea94fe08dc3995a1bd05c917"
}
上面是一个生成好的密钥对格式,仅作为展示,我删减了一部分长度。
使用银行卡进行转账交易的时候,会有一个转出的账号和一个转入的账号,在区块链中的记账也会有这个账号,这个账号就是上面使用生成的密钥对中的公钥,公钥就是地址,或者说公钥代表的就是自己的钱包。
校验的方法,首先使用字段“from”,“to”,“amount”的参数进行sign签名,然后在每次挖矿(记账)的时候,则使用verify(),通过前面的三个参数,和sig进行校验
verify(type,data){
swtich(type){
case 'sign':
const bufferMsg = Buffer.from(`${data.from}-${data.to}-${data.amount}`)
let signature = Buffer.from(keypair.sign(bufferMsg).toDER()).toString('hex')
this.signature = signature
break;
case 'verify':
const keypairTemp = ec.keyFromPublic(pub, 'hex')
const bufferMsg = Buffer.from(`${data.from}-${data.to}-${data.amount}`)
this.keypair = keypairTemp.verify(bufferMsg, sig)
break;
default;
}
}
转帐的时候需要3步,分别是校验转出账户是否有足够的金额,转出账户就是本地公钥。如有则进行记账并且使用两个地址、金额、时间,还有签名加密打包,之后进行全节点广播。其他节点收到这个信息之后第一件事也是对新区块的有效性做一个校验,通过校验之后就会写入data中。
transfer(data) {
const timestamp = new Date().getTime()
const sig = rsa.sign({data.from, data.to, data.amount , timestamp})
const sigTrans = {data.from, data.to, data.amount ,timestamp, sig }
// 非创世区块
if (trans.from !== '0') {
// 检验余额
if (!(_that.blance < amount)) { //_that.blance 当前账户余额
//全节点广播
_that.send('trans', sigTrans)
}else{
console.log('not enough blance')
return
}
}
this.data.push(sigTrans)
return sigTrans
}
其他节点收到消息之后,先进行去重校验,然后再更新数据。
四、查询余额
这个链的查询方法比较简单,就是将区块中的每一条交易的信息进行校验和匹配,满足条件的就进行增减,同时忽略精度上的问题。
this.blance = blance(address)
blance(address) {
let blance = 0;
this.blockchain.forEach(block => {
block.data.forEach(trans => {
if (address == trans.from) {
blance -= trans.amount
}
if (address == trans.to) {
blance += trans.amount
}
})
});
return blance
}
至此,区块链的最简单的功能就实现完毕。
总结
来源:https://segmentfault.com/a/1190000018744344


猜你喜欢
- 对于网站设计者而言,时常处理大批量的文件是难免的,特别是图片和一些文本文本文件,更是经常处理。而由于网站大量文件的关系,对于同类
- 【flask-socektio】之前不知道在哪个场合下提到过如何从web后台向前台推送消息。听闻了反向ajax技术这种模式之后,大呼神奇,试
- 使用conda安装requirement.txt的扩展包当你在GitHub上下载了代码时,可以看到有一个requirements.txt文件
- 需求是小程序做头部做导航分类的效果顶部用 scroll-view 组件横向滚动,类似tab选项卡的效果,内容用类似模板方式引用,可重复利用&
- 关于django中的APPEND_SLASHAPPEND_SLASH 它是啥?看变量名大概能知道做什么,就是添加斜线,用路由系统那里。路由文
- 前面的话一般的程序语言,表示空的只有null,但javascript的设计者Brendan Eich却设计了一个undefined,这无疑增
- 前言:Python 是一种脚本语言,相比 C/C++ 这样的编译语言,在效率和性能方面存在一些不足。但是,有很多时候,Python 的效率并
- 一、无组件上传的原理我还是一点一点用一个实例来说明的吧,客户端HTML如下。要浏览上传附件,我们通过<input type="
- 类的代码: define('QR_MODE_NUL', -1); define('QR_MODE_NUM',
- python安装pywifi1.Windows安装:在Dos窗口中输入以下命令:pip install pywifi如果找不到pip命令,那
- Dreamweaver 2004 除了可以插入 Flash SWF 動畫、Flash 文字和 Flash 按鈕以外,這次又新增加了一個叫做
- 英文的文档在这里,详细全面,本文仅为自己的学习笔记,只是试图通过转述加深自己的学习,不详细不全面。由于浏览器之间的差异,所以在JS中监听事件
- php遍历目录和文件的场景在很多时候都能用到,遍历目录方法的方法有好几种,那么应该使用
- 我写过一个外部模块扩展,现在开始看PHP源码中的mysql扩展,它是可以被集成到PHP内部的,所以应该算是内置的扩展了。 该扩展需要用到my
- 字符画是一种由字母、标点或其他字符组成的图画,它产生于互联网时代,在聊天软件中使用较多,本文我们看一下如何将自己喜欢的图片转成字符画。静态图
- 1.官网下载Pycharm community版如pycharm-community-2017.3.1.tar.gz。2. #解压tar.g
- 一、Django中路由的作用URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间
- 大家都知道select的优先权比较高,CSS不宜控制,而且还能遮挡层的正常显示!那么我们就来模拟一个!这样样式就可以随心所欲了(若您看不到效
- 最近在做zabbix的数据库MySQL5.6升级5.7时,出现主从延迟问题,这个问题困扰了很久没有解决,昨天终于解决了,整理了一下整个排查过
- //Paul Tero, July 2001 //http://www.tero.co.uk/des/ // //Optimised for