快捷搜索:
来自 新京葡娱乐场网址 2019-11-01 11:14 的文章
当前位置: 67677新澳门手机版 > 新京葡娱乐场网址 > 正文

行代码达成二个简单易行的区块链,写三个区块

挖矿

人们现在可以将新的交易添加到待处理交易的列表中。但无论如何,我们需要将他们清理掉并移入实际的区块中。为此,我们来创建一个minePendingTransactions()方法。这个方法不仅会挖掘所有待交易的新区块,而且还会向采矿者发送奖励。

minePendingTransactions(miningRewardAddress) { // 用所有待交易来创建新的区块并且开挖.. let block = new Block(Date.now(), this.pendingTransactions); block.mineBlock(this.difficulty); // 将新挖的看矿加入到链上 this.chain.push(block); // 重置待处理交易列表并且发送奖励 this.pendingTransactions = [ new Transaction(null, miningRewardAddress, this.miningReward) ]; }

1
2
3
4
5
6
7
8
9
10
11
12
13
minePendingTransactions(miningRewardAddress) {
  // 用所有待交易来创建新的区块并且开挖..
  let block = new Block(Date.now(), this.pendingTransactions);
  block.mineBlock(this.difficulty);
 
  // 将新挖的看矿加入到链上
  this.chain.push(block);
 
  // 重置待处理交易列表并且发送奖励
  this.pendingTransactions = [
      new Transaction(null, miningRewardAddress, this.miningReward)
  ];
}

请注意,该方法采用了参数miningRewardAddress。如果你开始挖矿,你可以将你的钱包地址传递给此方法。一旦成功挖到矿,系统将创建一个新的交易来给你挖矿奖励(在这个栗子里是100枚币)。

有一点需要注意的是,在这个栗子中,我们将所有待处理交易一并添加到一个区块中。但实际上,由于区块的大小是有限制的,所以这是行不通的。在比特币里,一个区块的大小大概是2Mb。如果有更多的交易能够挤进一个区块,那么矿工可以选择哪些交易达成哪些交易不达成(通常情况下费用更高的交易容易获胜)。

与其他结点的通信

结点的本质是和其他结点共享和同步区块链,下面的规则能保证网络同步。

  • 当一个结点生成一个新块时,它会在网络上散布这个块。
  • 当一个节点连接新peer时,它会查询最新的block。
  • 当一个结点遇到一个块,其index大于当前所有块的index时,它会添加这个块到它当前的链中,或者到整个区块链中查询这个块。

图片 1

如图为当节点遵循前文所述协议时会发生的一些典型通信场景

我没有采用自动发现peer的工具。peers的位置(URL)必须是手动添加的。

调整我们的区块链

当前的最大任务:调整我们的区块链来适应这些新变化。我们需要做的第一件事就是存储待处理交易的地方。

正如你所知道的,由于POW,区块链可以稳定的创建区块。在比特币的场景下,难度被设置成大约每10分钟创建一个新区块。但是,是可以在创造两个区块之间提交新的交易。

为了做到这一点,首先需要改变我们区块链的构造函数,以便他可以存储待处理的交易。我们还将创造一个新的属性,用于定义矿工获得多少钱作为奖励:

class Blockchain{ constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 5; // 在区块产生之间存储交易的地方 this.pendingTransactions = []; // 挖矿回报 this.miningReward = 100; } }

1
2
3
4
5
6
7
8
9
10
11
12
class Blockchain{
  constructor() {
    this.chain = [this.createGenesisBlock()];
    this.difficulty = 5;
 
    // 在区块产生之间存储交易的地方
    this.pendingTransactions = [];
 
    // 挖矿回报
    this.miningReward = 100;
  }
}

下一步,我们将调整我们的addBlock()方法。不过我的调整是指删掉并重写它!我们将不再允许人们直接为链上添加区块。相反,他们必须将交易添加至下一个区块中。而且我们将addBlock()更名为createTransaction(),这看起来更语义化:

createTransaction(transaction) { // 这里应该有一些校验! // 推入待处理交易数组 this.pendingTransactions.push(transaction); }

1
2
3
4
5
6
createTransaction(transaction) {
  // 这里应该有一些校验!
 
  // 推入待处理交易数组
  this.pendingTransactions.push(transaction);
}

本文由 伯乐在线 - 欣仔 翻译。未经许可,禁止转载!
英文出处:Lauri Hartikka。欢迎加入翻译组。

Part1:实现一个基本的区块链

块的生成

要生成一个块,必须知道前一个块的哈希值,然后创造其余所需的内容(= index, hash, data and timestamp)。块的data部分是由终端用户所提供的。

var generateNextBlock = (blockData) => { var previousBlock = getLatestBlock(); var nextIndex = previousBlock.index 1; var nextTimestamp = new Date().getTime() / 1000; var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData); return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash); };

1
2
3
4
5
6
7
var generateNextBlock = (blockData) => {
    var previousBlock = getLatestBlock();
    var nextIndex = previousBlock.index 1;
    var nextTimestamp = new Date().getTime() / 1000;
    var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData);
    return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash);
};

创造一个链

现在我们可以在Blockchain类中将区块链接起来了!下面是用JavaScript实现的代码:

class Blockchain{ constructor() { this.chain = [this.createGenesisBlock()]; } createGenesisBlock() { return new Block(0, "01/01/2017", "Genesis block", "0"); } getLatestBlock() { return this.chain[this.chain.length - 1]; } addBlock(newBlock) { newBlock.previousHash = this.getLatestBlock().hash; newBlock.hash = newBlock.calculateHash(); this.chain.push(newBlock); } isChainValid() { for (let i = 1; i < this.chain.length; i ){ const currentBlock = this.chain[i]; const previousBlock = this.chain[i - 1]; if (currentBlock.hash !== currentBlock.calculateHash()) { return false; } if (currentBlock.previousHash !== previousBlock.hash) { return false; } } return true; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Blockchain{
  constructor() {
    this.chain = [this.createGenesisBlock()];
  }
 
  createGenesisBlock() {
    return new Block(0, "01/01/2017", "Genesis block", "0");
  }
 
  getLatestBlock() {
    return this.chain[this.chain.length - 1];
  }
 
  addBlock(newBlock) {
    newBlock.previousHash = this.getLatestBlock().hash;
    newBlock.hash = newBlock.calculateHash();
    this.chain.push(newBlock);
  }
 
  isChainValid() {
    for (let i = 1; i < this.chain.length; i ){
      const currentBlock = this.chain[i];
      const previousBlock = this.chain[i - 1];
 
      if (currentBlock.hash !== currentBlock.calculateHash()) {
        return false;
      }
 
      if (currentBlock.previousHash !== previousBlock.hash) {
        return false;
      }
    }
    return true;
  }
}

在构造函数里,我通过创建一个包含创世块的数组来初始化整个链。第一个区块是特殊的,因为它不能指向前一个区块。我还添加了下面两个方法:

  • getLatestBlock()返回我们区块链上最新的区块。
  • addBlock()负责将新的区块添加到我们的链上。为此,我们将前一个区块的hash添加到我们新的区块中。这样我们就可以保持整个链的完整性。因为只要我们变更了最新区块的内容,我们就需要重新计算它的hash。当计算完成后,我将把这个区块推进链里(一个数组)。

最后,我创建一个isChainValid()来确保没有人篡改过区块链。它会遍历所有的区块来检查每个区块的hash是否正确。它会通过比较previousHash来检查每个区块是否指向正确的上一个区块。如果一切都没有问题它会返回true否则会返回false

确认块的完整性

在任何时候都必须能确认一个区块或者一整条链的区块是否完整。在我们从其他节点接收到新的区块,并需要决定接受或拒绝它们时,这一点尤为重要。

var isValidNewBlock = (newBlock, previousBlock) => { if (previousBlock.index 1 !== newBlock.index) { console.log('invalid index'); return false; } else if (previousBlock.hash !== newBlock.previousHash) { console.log('invalid previoushash'); return false; } else if (calculateHashForBlock(newBlock) !== newBlock.hash) { console.log('invalid hash: ' calculateHashForBlock(newBlock) ' ' newBlock.hash); return false; } return true; };

1
2
3
4
5
6
7
8
9
10
11
12
13
var isValidNewBlock = (newBlock, previousBlock) => {
    if (previousBlock.index 1 !== newBlock.index) {
        console.log('invalid index');
        return false;
    } else if (previousBlock.hash !== newBlock.previousHash) {
        console.log('invalid previoushash');
        return false;
    } else if (calculateHashForBlock(newBlock) !== newBlock.hash) {
        console.log('invalid hash: ' calculateHashForBlock(newBlock) ' ' newBlock.hash);
        return false;
    }
    return true;
};

使用区块链

我们的区块链类已经写完啦,可以真正的开始使用它了!

let savjeeCoin = new Blockchain(); savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 })); savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));

1
2
3
let savjeeCoin = new Blockchain();
savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 }));
savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));

在这里我仅仅是创建了一个区块链的实例,并且命名它为SavjeeCoin!之后我在链上添加了一些区块。区块里可以包含任何你想要放的数据,不过在上面的代码里,我选择添加了一个带有amount属性的对象。

体系结构

需要指出的是,节点实际上展现了两个web服务器:一个(HTTP服务器)是让用户控制节点,另一个(Websocket HTTP服务器)。

图片 2

NaiveChain的主要组成部分

Part3:交易与挖矿奖励

在前面两部分我们创建了一个简单的区块链,并且加入了POW来抵御攻击。然而我们在途中也偷了懒:我们的区块链只能在一个区块中存储一笔交易,而且矿工没有奖励。现在,让我们解决这个问题!

关于作者:欣仔

图片 3

假装会写代码的伪程序员~ 个人主页 · 我的文章 · 12 ·   

实现POW

我们该如何实现呢?我们先来修改我们区块类并在其构造函数中添加Nonce变量。我会初始化它并将其值设置为0。

constructor(index, timestamp, data, previousHash = '') { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = this.calculateHash(); this.nonce = 0; }

1
2
3
4
5
6
7
8
constructor(index, timestamp, data, previousHash = '') {
  this.index = index;
  this.previousHash = previousHash;
  this.timestamp = timestamp;
  this.data = data;
  this.hash = this.calculateHash();
  this.nonce = 0;
}

我们还需要一个新的方法来增加Nonce,直到我们获得一个有效hash。强调一下,这是由难度决定的。所以我们会收到作为参数的难度。

mineBlock(difficulty) { while (this.hash.substring(0, difficulty) !== Array(difficulty 1).join("0")) { this.nonce ; this.hash = this.calculateHash(); } console.log("BLOCK MINED: " this.hash); }

1
2
3
4
5
6
7
mineBlock(difficulty) {
    while (this.hash.substring(0, difficulty) !== Array(difficulty 1).join("0")) {
        this.nonce ;
        this.hash = this.calculateHash();
    }
    console.log("BLOCK MINED: " this.hash);
}

最后,我们还需要更改一下calculateHash()函数。因为目前他还没有使用Nonce来计算hash。

calculateHash() { return SHA256(this.index this.previousHash this.timestamp JSON.stringify(this.data) this.nonce ).toString(); }

1
2
3
4
5
6
7
8
calculateHash() {
  return SHA256(this.index
    this.previousHash
    this.timestamp
    JSON.stringify(this.data)
    this.nonce
  ).toString();
}

将它们结合在一起,你会得到如下所示的区块类:

class Block { constructor(index, timestamp, data, previousHash = '') { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = this.calculateHash(); this.nonce = 0; } calculateHash() { return SHA256(this.index this.previousHash this.timestamp JSON.stringify(this.data) this.nonce).toString(); } mineBlock(difficulty) { while (this.hash.substring(0, difficulty) !== Array(difficulty 1).join("0")) { this.nonce ; this.hash = this.calculateHash(); } console.log("BLOCK MINED: " this.hash); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Block {
  constructor(index, timestamp, data, previousHash = '') {
    this.index = index;
    this.previousHash = previousHash;
    this.timestamp = timestamp;
    this.data = data;
    this.hash = this.calculateHash();
    this.nonce = 0;
  }
 
  calculateHash() {
    return SHA256(this.index this.previousHash this.timestamp JSON.stringify(this.data) this.nonce).toString();
  }
 
  mineBlock(difficulty) {
    while (this.hash.substring(0, difficulty) !== Array(difficulty 1).join("0")) {
      this.nonce ;
      this.hash = this.calculateHash();
    }
    console.log("BLOCK MINED: " this.hash);
  }
}

选择最长的链

任何时候在链中都应该只有一组明确的块。万一冲突了(例如:两个结点都生成了72号块时),会选择有最大数目的块的链。

图片 4

var replaceChain = (newBlocks) => { if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) { console.log('Received blockchain is valid. Replacing current blockchain with received blockchain'); blockchain = newBlocks; broadcast(responseLatestMsg()); } else { console.log('Received blockchain invalid'); } };

1
2
3
4
5
6
7
8
9
var replaceChain = (newBlocks) => {
    if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) {
        console.log('Received blockchain is valid. Replacing current blockchain with received blockchain');
        blockchain = newBlocks;
        broadcast(responseLatestMsg());
    } else {
        console.log('Received blockchain invalid');
    }
};

免责声明

就像之前说的:这绝不是一个完整的区块链。它仍然缺少很多功能(像P2P网路)。这只是为了说明区块链的工作原理。

并且:由于单线程的原因,用JavaScript来挖矿并不快。

结点控制

在某种程度上用户必须能够控制结点。这一点通过搭建一个HTTP服务器可以实现。

var initHttpServer = () => { var app = express(); app.use(bodyParser.json()); app.get('/blocks', (req, res) => res.send(JSON.stringify(blockchain))); app.post('/mineBlock', (req, res) => { var newBlock = generateNextBlock(req.body.data); addBlock(newBlock); broadcast(responseLatestMsg()); console.log('block added: ' JSON.stringify(newBlock)); res.send(); }); app.get('/peers', (req, res) => { res.send(sockets.map(s => s._socket.remoteAddress

  • ':' s._socket.remotePort)); }); app.post('/addPeer', (req, res) => { connectToPeers([req.body.peer]); res.send(); }); app.listen(http_port, () => console.log('Listening http on port: ' http_port)); };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var initHttpServer = () => {
    var app = express();
    app.use(bodyParser.json());
 
    app.get('/blocks', (req, res) => res.send(JSON.stringify(blockchain)));
    app.post('/mineBlock', (req, res) => {
        var newBlock = generateNextBlock(req.body.data);
        addBlock(newBlock);
        broadcast(responseLatestMsg());
        console.log('block added: ' JSON.stringify(newBlock));
        res.send();
    });
    app.get('/peers', (req, res) => {
        res.send(sockets.map(s => s._socket.remoteAddress ':' s._socket.remotePort));
    });
    app.post('/addPeer', (req, res) => {
        connectToPeers([req.body.peer]);
        res.send();
    });
    app.listen(http_port, () => console.log('Listening http on port: ' http_port));
};

用户可以用下面的方法和结点互动:

  • 列出所有的块
  • 用用户提供的内容创建一个新的块
  • 列出或者新增peers

下面这个Curl的例子就是最直接的控制结点的方法:

#get all blocks from the node curl

1
2
#get all blocks from the node
curl http://localhost:3001/blocks

修改区块链

现在,我们的区块已经拥有Nonce并且可以被开采了,我们还需要确保我们的区块链支持这种新的行为。让我们先在区块链中添加一个新的属性来跟踪整条链的难度。我会将它设置为2(这意味着区块的hash必须以2个0开头)。

constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 2; }

1
2
3
4
constructor() {
  this.chain = [this.createGenesisBlock()];
  this.difficulty = 2;
}

现在剩下要做的就是改变addBlock()方法,以便在将其添加到链中之前确保实际挖到该区块。下面我们将难度传给区块。

addBlock(newBlock) { newBlock.previousHash = this.getLatestBlock().hash; newBlock.mineBlock(this.difficulty); this.chain.push(newBlock); }

1
2
3
4
5
addBlock(newBlock) {
  newBlock.previousHash = this.getLatestBlock().hash;
  newBlock.mineBlock(this.difficulty);
  this.chain.push(newBlock);
}

大功告成!我们的区块链现在拥有了POW来抵御攻击了。

总结

创造 NaiveChain 的目的是为了示范和学习,因为它并没有“挖矿”算法(PoS of PoW),不能被用于公用网络,但是它实现了区块链运作的基本特性。

你可以在 Github 库中查看更多的技术细节。 

打赏支持我翻译更多好文章,谢谢!

打赏译者

结论

这个小栗子还远未达到完成的程度。它还没有实现POW(工作量证明机制)或P2P网络来与其它矿工来进行交流。

但他确实证明了区块链的工作原理。许多人认为原理会非常复杂,但这篇文章证明了区块链的基本概念是非常容易理解和实现的。

块哈希

为了保存完整的数据,必须哈希区块。SHA-256会对块的内容进行加密,记录这个值应该和“挖矿”毫无关系,因为这里不需要解决工作量证明的问题。

var calculateHash = (index, previousHash, timestamp, data) => { return CryptoJS.SHA256(index previousHash timestamp data).toString(); };

1
2
3
var calculateHash = (index, previousHash, timestamp, data) => {
    return CryptoJS.SHA256(index previousHash timestamp data).toString();
};

局限性与结论

现在我们的区块链已经可以在一个区块上存储多笔交易,并且可以为矿工带来回报。

不过,还是有一些不足:发送货币是,我们不检查发起人是否有足够的余额来实际进行交易。然而,这其实是一件容易解决的事情。我们也没有创建一个新的钱包和签名交易(传统上用公钥/私钥加密完成)。

块结构

第一个逻辑步骤是决定块结构。为了保证事情尽可能的简单,我们只选择最必要的部分:index(下标)、timestamp(时间戳)、data(数据)、hash(哈希值)和 previous hash(前置哈希值)。

图片 5

这个块中必须能找到前一个块的哈希值,以此来保证整条链的完整性。

class Block { constructor(index, previousHash, timestamp, data, hash) { this.index = index; this.previousHash = previousHash.toString(); this.timestamp = timestamp; this.data = data; this.hash = hash.toString(); } }

1
2
3
4
5
6
7
8
9
class Block {
    constructor(index, previousHash, timestamp, data, hash) {
        this.index = index;
        this.previousHash = previousHash.toString();
        this.timestamp = timestamp;
        this.data = data;
        this.hash = hash.toString();
    }
}

测试

现在让我们来测试一下我们的区块链,看看在POW下添加一个新区块会有什么效果。我将会使用之前的代码。我们将创建一个新的区块链实例然后往里添加2个区块。

let savjeeCoin = new Blockchain(); console.log('Mining block 1'); savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 })); console.log('Mining block 2'); savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));

1
2
3
4
5
6
7
let savjeeCoin = new Blockchain();
 
console.log('Mining block 1');
savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 }));
 
console.log('Mining block 2');
savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));

如果你运行了上面的代码,你会发现添加新区块依旧非常快。这是因为目前的难度只有2(或者你的电脑性能非常好)。

如果你创建了一个难度为5的区块链实例,你会发现你的电脑会花费大概十秒钟来挖矿。随着难度的提升,你的防御攻击的保护程度越高。

区块链的基础概念很简单:一个分布式数据库,存储一个不断加长的 list,list 中包含着许多有序的记录。然而,在通常情况下,当我们谈到区块链的时候也会谈起使用区块链来解决的问题,这两者很容易混淆。像流行的比特币和以太坊这样基于区块链的项目就是这样。“区块链”这个术语通常和像交易、智能合约、加密货币这样的概念紧紧联系在一起。

本文由67677新澳门手机版发布于新京葡娱乐场网址,转载请注明出处:行代码达成二个简单易行的区块链,写三个区块

关键词: