用代码理解区块链(与虚拟货币无关,请勿划等号)

/ 0评 / 0

其实区块链这个东西,一直说得多火热,从比特币什么开始,就有这么一说,实际上,区块链不等于比特币,这里介绍得区块链也不能用于盈利,只是一个概念介绍,让你知道,何为区块链,实际上,区块链用途很广,电子合同验证应该是最广泛用途了,区块链由于是链式储存,想毁掉中间某个区块难度非常大,因为验算难度低,计算难度高,可以说是一人之力,推翻一切.

我不写代码,但是可以从前人的代码中得到实践,这个项目就是:https://github.com/dvf/blockchain

(其实我是因为看到这个项目,特地来写这篇东西的,算是自己的笔记.)

首先,一个区块应长什么样子,这里随便举个例子.

{

  "index": 100,

  "previous_hash": "775cb2c329f33aef0d7cc74055be6faf41fc154efb67a541af55840736012ed8",

  "proof": 52202,

  "timestamp": 1569852153.8177216,

  "transactions": [

    {

      "amount": 1,

      "recipient": "5bc02d1e9be645519afd3711056af559",

      "sender": "6af93232e71bcbe3833d140ec94195f3"

    }

  ]

}

index => 区块号

previous_hash => 上一个hash,这就是链接的关键.

proof => 工作量凭证,其实是增加挖矿难度(其实不需要极高增加难度,并不一定就能增加安全性,但是破解时候需要填充这些proof)

timestamp => 时间戳

transactions => 交易信息(当然,里面是信息是可以随便定义的,不一定要长这样,也可以是份PDF二进制什么都行,这里举例的刚好只是个金额交易的简单系统.)

transactions.sender => 举例的发送者

transactions.recipient => 举例的接收者

transactions.amount => 举例的交易金额

看到示例的Python脚本里面,这么新建一个区块.

在函数new_transaction中向列表中添加一笔交易之后,它返回值是本次交易的index,该index会被添加到下一个待挖掘区块,后面在用户提交交易时也会用到,挖的时候就会把上一级区块hash提取出来,附上本区块信息,作为新区块,意味着,越是往后区块,越容易被破解篡改拉~

def new_block(self, proof, previous_hash):

    """

    Create a new Block in the Blockchain

    :param proof: The proof given by the Proof of Work algorithm

    :param previous_hash: Hash of previous Block

    :return: New Block

    """



    block = {

        'index': len(self.chain) + 1,

        'timestamp': time(),

        'transactions': self.current_transactions,

        'proof': proof,

        'previous_hash': previous_hash or self.hash(self.chain[-1]),

    }



    # Reset the current list of transactions

    self.current_transactions = []



    self.chain.append(block)

    return block



def new_transaction(self, sender, recipient, amount):

    """

    Creates a new transaction to go into the next mined Block

    :param sender: Address of the Sender

    :param recipient: Address of the Recipient

    :param amount: Amount

    :return: The index of the Block that will hold this transaction

    """

    self.current_transactions.append({

        'sender': sender,

        'recipient': recipient,

        'amount': amount,

    })



    return self.last_block['index'] + 1

在代码一开始,创建的第一个区块(这条链的第一个区块),后续的每一个区块,都依赖此区块.

self.new_block(previous_hash='1', proof=100)

下面就到POW,在说明POW之前,先说说怎么破解一个区块链.

比如下面有一条链,Hash如下:

b3d6 -> 6569 -> 35b3 -> de9e -> 0add -> 4ab0 -> 65b4 -> 22e8

现在你想破坏第三个数据,目前第三个数据计算出hash是35b3,你数据破坏后,计算出是55b4,由于第四个数据,de9e所在区块包含35b3的信息,所以你要重算de9e这个区块的实际hash,可能变成了d466,要想完美破解,必须推翻到链最后.

那计算下一个块的Hash有多难,如果计算需要1秒,推翻一条10个数据的链,需要10秒完成,如此类推.当然,不建议把推翻难度无限提高,首先我们不是为了浪费机器算力,我们只是用来保证交易信息,选择一个合适的阈值就行了,我们不是用来挖什么赚钱虚拟币,区块链的正途,是用来验证信息而不是搞什么交易虚拟币的.

话说回来,POW是一种简便的,增加运算能力,提高破解难度的方法.比特币里叫他Hashcash,我这里就叫他增加破解难度的%^#$%^$的算法好了.

比如这个算法里面,必须计算一个hash(x * y) = ........0000,x是已知,y是待求(还不如叫待猜),使得hash末尾是0000(4条0).

def proof_of_work(self, last_block):

    """

    Simple Proof of Work Algorithm:

        - Find a number p' such that hash(pp') contains leading 4 zeroes

        - Where p is the previous proof, and p' is the new proof

        

    :param last_block: <dict> last Block

    :return: <int>

    """



    last_proof = last_block['proof']

    last_hash = self.hash(last_block)



    proof = 0

    while self.valid_proof(last_proof, proof, last_hash) is False:

        proof += 1



    return proof



@staticmethod

def valid_proof(last_proof, proof, last_hash):

    """

    Validates the Proof

    :param last_proof: <int> Previous Proof

    :param proof: <int> Current Proof

    :param last_hash: <str> The hash of the Previous Block

    :return: <bool> True if correct, False if not.

    """



    guess = f'{last_proof}{proof}{last_hash}'.encode()

    guess_hash = hashlib.sha256(guess).hexdigest()

    return guess_hash[:4] == "0000"

这个算法的巧妙之处在于,想找到这样的y很困难(不知道多少次hash),但是想验证这个y对不对,只要一次hash.上述演示代码中.

把{last_proof}{proof}{last_hash}链接一起.其中last_proof和last_hash是上一个区块的.所以,需要破解的人,同样需要解决这个区块的proof,想想就很费力.

到现在,这条链上,既有工作量,又满足了区块链的特性.算是初步完成,那么,就得来验证,幸运的是,这个程序,竟然还做了API,简直是太方便了.

安装这个代码很简单,把代码git clone下来,然后切换到他的目录里,用pipenv install就可以了,注意Python版本不能太低,当然不一定是Python 3.6~

然后按照他说的用这样方法,指定端口启动,完事.

pipenv run python blockchain.py -p 5001

API举例(你要自己替换成你自己IP哈):

建议下载个POSTMAN测试,只有需要POST的API,才会携带数据,数据都是JSON数据.

创建交易示例.

{

	"sender": "720fb6b46da04400a21556fbccec6cd4",

	"recipient": "5bc02d1e9be645519afd3711056af559",

	"amount": 5

}

创建节点关联:

{

	"nodes": ["http://127.0.0.1:5002"]

}

下面创建一个交易.

接下来挖一下这个块.

然后获取链上所有数据.

返回结果包含2个结果,目前链上就是2个结果.

{

  "chain": [

    {

      "index": 1,

      "previous_hash": "1",

      "proof": 100,

      "timestamp": 1569856786.486463,

      "transactions": []

    },

    {

      "index": 2,

      "previous_hash": "ebf45554844b4df3d2cded78c8a96e79a939c9a6578c77896b59989c5e9ab017",

      "proof": 43956,

      "timestamp": 1569856951.0676649,

      "transactions": [

        {

          "amount": 5,

          "recipient": "5bc02d1e9be645519afd3711056af559",

          "sender": "720fb6b46da04400a21556fbccec6cd4"

        },

        {

          "amount": 1,

          "recipient": "1c8c93ea836d4edc887bb2d6d8de0867",

          "sender": "0"

        }

      ]

    }

  ],

  "length": 2

}

比如我挖出一条比较长的链. {last_proof}{proof}{last_hash} 132739 132141 7f35e5f292fc453df2cf082fda30196ec4b31e398e35b2dca8586c21d06ac333

{

    "index": 13,

    "previous_hash": "7f35e5f292fc453df2cf082fda30196ec4b31e398e35b2dca8586c21d06ac333",

    "proof": 132739,

    "timestamp": 1569857231.1991785,

    "transactions": [

    {

        "amount": 1,

        "recipient": "1c8c93ea836d4edc887bb2d6d8de0867",

        "sender": "0"

    }

    ]

},

{

    "index": 14,

    "previous_hash": "ba273eb96abcd4ff0089eced2716edbeff040ba193ba5c920cf77830f7c8e688",

    "proof": 132141,

    "timestamp": 1569857232.3286073,

    "transactions": [

    {

        "amount": 1,

        "recipient": "1c8c93ea836d4edc887bb2d6d8de0867",

        "sender": "0"

    }

    ]

},

{

    "index": 15,

    "previous_hash": "82a683554c5d99ab2da48138b9984817ee1849a972d5b4764670e50c5a0cf88d",

    "proof": 107165,

    "timestamp": 1569857233.110304,

    "transactions": [

    {

        "amount": 1,

        "recipient": "1c8c93ea836d4edc887bb2d6d8de0867",

        "sender": "0"

    }

    ]

}

比如我验证14区块对不对,我们用15区块来做的话,首先,14区块的proof是132141,15区块的proof是107165,上一个hash是82a683554c5d99ab2da48138b9984817ee1849a972d5b4764670e50c5a0cf88d,那么这个验证hash原文是13214110716582a683554c5d99ab2da48138b9984817ee1849a972d5b4764670e50c5a0cf88d,密文是0000d7916d02aa408758f3a05cc6d3bf6c8ca6fbb782fbde3b5d82eaa930f4a4,前面4个是0,满足计算,所以这是有效的.如这种情况需要破坏14区块,计算发现15区块写的previous_hash不符合14区块当前内容的摘要,失败.

如果15区块还没产生,验证14区块,则需要满足刚才说的proof链接字符串后,hash有4个0在前面,用13区块的全部内容的摘要+13区块的proof+14区块的proof满足要求,但是这个修改起来太容易,所以一般是用后面数据校验前面.你这个块虽然是合法块,但是要做到不可篡改,还需要更多的块.

在这里,大家应该明白,越长越难破解了.

现在引入一个新的风险,就是假设,有人就是有这样超级牛逼的机器,甚至他修改算法,入侵到你的机器,随便改数据都是合法的,那么怎么办?

那就多个机器呗,我们可以继续用这个程序,用别的端口创建.

比如我现在有2台假想的机器.

端口5001机器已经挖到32长度,5002是后加入,于是使用加入API,加入一台机器,同理,5002也应该加入5001.

就像我们应该维护一个机器列表.

这时候,我们用/nodes/resolve的API,来尝试同步,假设,我现在用的是机器5001,这个明显更长链,他同步的话,应该自己就是大佬(最有话语权的人,有效数据最多.)

但是如果更少数据的人去做这个同步,则需要跟大佬同步.

维护一个总表,每个人都都需要定期同步,则变成分布式了.

另外这里要说几句: