iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 7
1
自我挑戰組

區塊鏈報明牌系列 第 7

[區塊鏈報明牌]Day 7 比特幣論文(6)-自幹區塊鏈網路run起來-報明牌鏈!

  • 分享至 

  • xImage
  •  

聖誕節竟然還要寫code,只好在今天run起自己的區塊鏈網路,浪費電力報復社會/images/emoticon/emoticon03.gif

既然這次的鐵人賽題目取叫區塊鏈報明牌,為了不要太名不符實,就把這個區塊鏈叫做報明牌鏈吧!接下來幾天會在根據比特幣論文繼續完善,並加入一些額外功能,來完成在Day1說的第一部分目標。

請注意這只是個學習性質為主的demo,離真正能當作實務應用等級的區塊鏈差非常遠,真正的實作大家可以參考官方bitcoin的原始碼

Peer to Peer

先說一下目前的區塊鏈網路雖然可以work,不過還不符合比特幣論文中所題的網路流程,我本來想說在聖誕節搞場大的,結果今天寫不完,讓我在花個幾天完善它。

身為一個碼農要做P2P網路自然要先找library來用,google了一下發現了一個叫pyp2p的library,範例還寫的超簡單的,看來今天能輕鬆解決了,喔不跑不起來,看一下狀態已經不再開發。又找到了一個pydevp2p,而且還是乙太幣python實作用的P2P library,結果文件狀態是fail的,範例也寫得很複雜,難道我今天要糗大了嗎/images/emoticon/emoticon06.gif?最後找到了Berry College的The P2P Framework,500行的程式碼實現的P2P框架,終於看得懂了,碼農的日常就是這樣吧。


The P2P Framework架構(圖源參考自[1])

這個library功能很簡單,其實也是server-client架構,只是每個節點同時都是server跟client,server收其他所有節點的請求、client也跟所有節點要服務。

因為這裡的實作沒有Tracker之類紀錄網路上IP的東西,所以直接把網路中的node手動加進來:

# 在自己的電腦上的port開個節點
# 'xxx.xxx.xxx.xxx'填P2P網路中的其他ip:port
# 也可以在自己的電腦上開兩個port來用
# 開不起來可能是因為port有程式在用可以換一個
# 或檢查看看防火牆之類的設定

node = BTPeer(0, 4444)
node.addpeer(1, 'xxx.xxx.xxx.xxx', 4444)
node.addpeer(2, 'xxx.xxx.xxx.xxx', 4445)
node.addpeer(3, 'xxx.xxx.xxx.xxx', 4446)
....

修改先前的兩個函式broadcast跟trade,讓他們可以在P2P網路上廣播:

# 之前的所有str()都改用json.dumps(),方便傳遞資料

# broadcast 修改為 block_broadcast
# 將區塊昭告天下
def block_broadcast(self, new_block):
    
    todelete = []
    for pid in self.peers:
        isconnected = False
        try:
            self.__debug( 'Check live %s' % pid )
            host,port = self.peers[pid]
            peerconn = BTPeerConnection( pid, host, port, debug=self.debug )
            peerconn.senddata( 'REBL', json.dumps(new_block, encoding='latin1') )
            isconnected = True
        except:
            todelete.append( pid )
        if isconnected:
            peerconn.close()

    self.peerlock.acquire()
    try:
        for pid in todelete: 
            if pid in self.peers: del self.peers[pid]
    finally:
        self.peerlock.release()


# 談好一筆交易了
def trade_broadcast(self, pre_tran, data, pre_sk, now_vk):
        
    transaction = {
        'owner_vk': now_vk,
        'preowner_vk': pre_tran['owner_vk'],
        'data': data,
        'signature': ecdsa.SigningKey.from_pem(pre_sk).sign(str(pre_tran)+now_vk)
    }           

    try:
        # 交易談好了
        # 現在確認上一個transaction的擁有者有沒有偷改他以前的交易內容

        ecdsa.VerifyingKey.from_pem(pre_tran['owner_vk']).verify(transaction['signature'], str(pre_tran) + now_vk)
        todelete = []
        for pid in self.peers:
            isconnected = False
            try:
                self.__debug( 'Check live %s' % pid )
                host,port = self.peers[pid]
                peerconn = BTPeerConnection( pid, host, port, debug=self.debug )
                peerconn.senddata( 'RETR',  json.dumps(transaction, encoding='latin1'))
                isconnected = True
            except:
                todelete.append( pid )
            if isconnected:
                peerconn.close()

        self.peerlock.acquire()
        try:
            for pid in todelete: 
                if pid in self.peers: del self.peers[pid]
        finally:
            self.peerlock.release()
    except:
        print('有騙子?!')


修改add_tran為recv_tran,並把broadcast原本的一部分功能放到recv_block內:

# recv_tran
def recv_tran( self , peerconn, msgdata):

    block = {
        'prev_Hash': None,
        'nonce': None,
        'timestamp': int(time.time()), 
        'Txs': []
    }
    block['Txs'].append(json.loads(msgdata))
    self.proof_of_work(block)

# recv_block
def recv_block( self , peerconn, msgdata):
    new_block = json.loads(msgdata)

    # 先確定區塊的合法性
    # 才能確定區塊是不是算力大的一方來的
    if new_block['timestamp'] < self.blockchain[-1]['timestamp']:
        print("node %s:區塊不合法,應按照時間生成" % self.myid)
        return

    if new_block['pre_Hash'] != hashlib.sha256(str(self.blockchain[-1])).hexdigest():
        print("node %s:區塊不合法,前一個hash值對不起來" % self.myid)
        return

    if hashlib.sha256(str(new_block).decode('utf-8')).hexdigest()[0:4] != '0000':
        print("node %s:區塊不合法,hash值不符合proof of work的規定" % self.myid)
        return

    check_legel = True
    # 第j的區塊的第k個transaction
    for j in range(len(self.blockchain)):
        for k in range(len(self.blockchain[j]['Txs'])):
            for l in range(len(new_block['Txs'])):
                if self.blockchain[j]['Txs'][k]['preowner_vk'].decode('utf-8') == new_block['Txs'][l]['preowner_vk'].decode('utf-8'):
                    check_legel = False


    if check_legel == True:
        self.blockchain.append(new_block)
        print("node %s:紀錄了新block中的所有交易" % self.myid)
    else:
        print("node %s:抱歉,這筆交易不合規定,不能重複交易" % self.myid)


將上面的function都註冊到node的recv_block裡

self.addhandler('RETR', self.recv_tran)
self.addhandler('REBL', self.recv_block)

self.blockchain = [Genesis_block]

現在區塊鏈網路動起來!:


# 該node只發起交易之後其實沒參與網路運作
node = BTPeer(0, 4444)
node.addpeer(1, 'xxx.xxx.xxx.xxx', 4444)
node.addpeer(2, 'xxx.xxx.xxx.xxx', 4445)
node.addpeer(3, 'xxx.xxx.xxx.xxx', 4446)

# 不厭其煩的強調,真實情境不要把在用的私鑰放到瀏覽器上
node.trade_broadcast(Genesis_block['Txs'][0], '1000萬拿去收好', '-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIGqXRqNjZns1TJ1CfayizUPcpZop00KWWj0+fOy/WwqtoAcGBSuBBAAK\noUQDQgAEZUBDWMgG3dTAzKcvMbw1IkJiLbtFq/AyLIMsKpz2v2mc3e3QJUM/scUR\nMzoXPDSPftfU2CT6f4K0saWZsstAWg==\n-----END EC PRIVATE KEY-----', '')

另外兩個node的狀況:

# 假設是4445port的node先計算完成


node = BTPeer(0, 4445).mainloop()
node.addpeer(1, 'xxx.xxx.xxx.xxx', 4444)
node.addpeer(2, 'xxx.xxx.xxx.xxx', 4445)
node.addpeer(3, 'xxx.xxx.xxx.xxx', 4446)
node.mainloop()
'''
4445先計算完成
node xxx.xxx.xxx.xxx:4445 計算合法區塊成功
node xxx.xxx.xxx.xxx:4445:紀錄了新block中的所有交易

4446算完的廣播進來了 可是來不及了
node xxx.xxx.xxx.xxx:4445:區塊不合法,前一個hash值對不起來
'''

node = BTPeer(0, 4446).mainloop()
node.addpeer(1, 'xxx.xxx.xxx.xxx', 4444)
node.addpeer(2, 'xxx.xxx.xxx.xxx', 4445)
node.addpeer(3, 'xxx.xxx.xxx.xxx', 4446)
node.mainloop()
'''
收到4445來的廣播
node xxx.xxx.xxx.xxx:4446:紀錄了新block中的所有交易

4446計算完成 可是來不及了
node xxx.xxx.xxx.xxx:4446 計算合法區塊成功
node xxx.xxx.xxx.xxx:4446:區塊不合法,前一個hash值對不起來
'''

github放有完整的範例程式提供,可以直接執行參考,可以把debug設成1以方便理解流程。

跟大家分享一個小故事,python的創始人Guido van Rossum就是為了打發聖誕節才開始寫python的,雖然我這種渣渣不能比啦,不過箇中的喜悅想必就是如此吧,帶著哀傷的喜悅阿/images/emoticon/emoticon10.gif

相關參考資源:

《Bitcoin: A Peer-to-Peer Electronic Cash System》
https://bitcoin.org/bitcoin.pdf
[1]The P2P Framework
http://cs.berry.edu/~nhamid/p2p/framework-python.html


上一篇
[區塊鏈報明牌]Day 6 比特幣論文(5)-Proof of Work
下一篇
[區塊鏈報明牌]Day 8 比特幣論文(7)-網路流程 & 硬分叉
系列文
區塊鏈報明牌30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言