<jupyter_start><jupyter_text>Learning by Doing* this is a A Step-by-Step Guide For Beginners into practical building of blockchain.* using explanation and [this code](https://github.com/snaga/blockchain) that was published in [ブロックチェ－ンを構築しながら学ぶ | プログラミング | POSTD](http://postd.cc/learn-blockchains-by-building-one/) * This branch using also Jupyter interface to explain blockchain.* To understand the mechanism of the BC, some theorethical backgroung of cryptography is required.  Implementation Creating a Blockchain Class<jupyter_code>from time import time
import hashlib
import json

class Blockchain(object):
    def __init__(self):
        self.current_transactions = []
        self.chain = []

        # Create the genesis block
        self.new_block(previous_hash=1, proof=100)

    def new_block(self, proof, previous_hash=None):
        """
        Create a new Block in the Blockchain
        :param proof: <int> The proof given by the Proof of Work algorithm
        :param previous_hash: (Optional) <str> Hash of previous Block
        :return: <dict> 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: <str> Address of the Sender
        :param recipient: <str> Address of the Recipient
        :param amount: <int> Amount
        :return: <int> 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

    @staticmethod
    def hash(block):
        """
        Creates a SHA-256 hash of a Block
        :param block: <dict> Block
        :return: <str>
        """
        # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

    @property
    def last_block(self):
        return self.chain[-1]

    def proof_of_work(self, last_proof):
        """
        Simple Proof of Work Algorithm:
         - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
         - p is the previous proof, and p' is the new proof
        :param last_proof: <int>
        :return: <int>
        """
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1
        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        """
        Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
        :param last_proof: <int> Previous Proof
        :param proof: <int> Current Proof
        :return: <bool> True if correct, False if not.
        """
        guess = ('%d%d' % (last_proof, proof)).encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"<jupyter_output><empty_output><jupyter_text>Adding a Mining Process<jupyter_code>from uuid import uuid4

node_identifier = str(uuid4()).replace('-', '')

def mine(blockchain):
    global node_identifier
    
    # We run the proof of work algorithm to get the next proof...
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    # We must receive a reward for finding the proof.
    # The sender is "0" to signify that this node has mined a new coin.
    blockchain.new_transaction(
        sender="0",
        recipient=node_identifier,
        amount=1,
    )

    # Forge the new Block by adding it to the chain
    previous_hash = blockchain.hash(last_block)
    block = blockchain.new_block(proof, previous_hash)

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }
    return response<jupyter_output><empty_output><jupyter_text>Geting the whole chain<jupyter_code>def full_chain(blockchain):
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain),
    }
    return response<jupyter_output><empty_output><jupyter_text>moving blockchainUse pprint to make the display easier to see.<jupyter_code>import pprint
pp = pprint.PrettyPrinter(indent=2)<jupyter_output><empty_output><jupyter_text>Identifier indicating its own node is as follows.<jupyter_code>node_identifier<jupyter_output><empty_output><jupyter_text>Once instantiated, the first block is created.The first block is:* Index 1* No previous hash (1)* The length of the chain naturally "1"<jupyter_code>b = Blockchain()
pp.pprint(full_chain(b))<jupyter_output>{ 'chain': [ { 'index': 1,
               'previous_hash': 1,
               'proof': 100,
               'timestamp': 1549965508.7086954,
               'transactions': []}],
  'length': 1}<jupyter_text>mining of one block<jupyter_code>newblock = mine(b)
pp.pprint(newblock)<jupyter_output>{ 'index': 2,
  'message': 'New Block Forged',
  'previous_hash': 'bcd0be7401c4c516a9844c467c3c710e96039dd99985a4de454f2f9235e98485',
  'proof': 35293,
  'transactions': [ { 'amount': 1,
                      'recipient': 'd9970bd3cc594dee99c11f1baecbdaf9',
                      'sender': '0'}]}<jupyter_text>transactions, Only the transaction of mining is described.Mining transactions:* If sender is 0* recipient is the identifier of its own node* amount is 1Transactions are recorded as transactions. Look at the contents of the whole block chain at this time。<jupyter_code>pp.pprint(full_chain(b))<jupyter_output>{ 'chain': [ { 'index': 1,
               'previous_hash': 1,
               'proof': 100,
               'timestamp': 1549965508.7086954,
               'transactions': []},
             { 'index': 2,
               'previous_hash': 'bcd0be7401c4c516a9844c467c3c710e96039dd99985a4de454f2f9235e98485',
               'proof': 35293,
               'timestamp': 1549965541.6423428,
               'transactions': [ { 'amount': 1,
                                   'recipient': 'd<PASSWORD>',
                                   'sender': '0'}]}],
  'length': 2}<jupyter_text>As a new transaction (transaction), there are blocks containing only mining results. Let's add a new transaction here.* Sender as 'foo'* Set recipient to 'bar'* amount 10Transactions.<jupyter_code>index = b.new_transaction('foo', 'bar', 10)<jupyter_output><empty_output><jupyter_text>At this time (index of block) is 3. The above transaction is stored in this block.<jupyter_code>print(index)<jupyter_output>3<jupyter_text>At this time, looking at the chain as a whole, the transaction added above is not yet registered in the chain.<jupyter_code>pp.pprint(full_chain(b))<jupyter_output>{ 'chain': [ { 'index': 1,
               'previous_hash': 1,
               'proof': 100,
               'timestamp': 1549965508.7086954,
               'transactions': []},
             { 'index': 2,
               'previous_hash': 'bcd0be7401c4c516a9844c467c3c710e96039dd99985a4de454f2f9235e98485',
               'proof': 35293,
               'timestamp': 1549965541.6423428,
               'transactions': [ { 'amount': 1,
                                   'recipient': 'd<PASSWORD>',
                                   'sender': '0'}]}],
  'length': 2}<jupyter_text>Mine and add a new block.<jupyter_code>newblock = mine(b)<jupyter_output><empty_output><jupyter_text>The third block is created, and the information of the previously created transaction and the information of mining are saved.<jupyter_code>pp.pprint(newblock)<jupyter_output>{ 'index': 2,
  'message': 'New Block Forged',
  'previous_hash': 'bcd0be7401c4c516a9844c467c3c710e96039dd99985a4de454f2f9235e98485',
  'proof': 35293,
  'transactions': [ { 'amount': 1,
                      'recipient': 'd<PASSWORD>',
                      'sender': '0'}]}<jupyter_text>The state of the whole chain at this time is as follows.<jupyter_code>pp.pprint(full_chain(b))<jupyter_output>{ 'chain': [ { 'index': 1,
               'previous_hash': 1,
               'proof': 100,
               'timestamp': 1549965508.7086954,
               'transactions': []},
             { 'index': 2,
               'previous_hash': 'bcd0be7401c4c516a9844c467c3c710e96039dd99985a4de454f2f9235e98485',
               'proof': 35293,
               'timestamp': 1549965541.6423428,
               'transactions': [ { 'amount': 1,
                                   'recipient': 'd<PASSWORD>',
                                   'sender': '0'}]}],
  'length': 2}<jupyter_text>Decentralized 分散合意 Blockchain2 Implementing classesImplement Blockchain2 class incorporating consensus algorithm.At the same time, the node identifier was held as a class member, and when mining it was modified to use it. (In order to handle block chains of multiple nodes)(In fact it seems better to define the Node class and have the Blockchain 2 class as has - a as a member, but register_node () or resolve_conflicts () in the original version has been introduced to the Blockchain class, so follow it I will do.<jupyter_code>import copy

BlockchainNeighbours = {}

class Blockchain2(Blockchain):
    def __init__(self, node_identifier):
        super().__init__()
        self.nodes = set()
        self.node_identifier = node_identifier
        
    def register_node(self, node_identifier):
        """
        Add a new node to the list of nodes
        :node_identifier: <str> Node identifier of the neighbour node.
        :return: None
        """
        self.nodes.add(node_identifier)

    def valid_chain(self, chain):
        """
        Determine if a given blockchain is valid
        :param chain: <list> A blockchain
        :return: <bool> True if valid, False if not
        """

        last_block = chain[0]
        current_index = 1

        while current_index < len(chain):
            block = chain[current_index]
#            print(f'{last_block}')
#            print(f'{block}')
#            print("\n-----------\n")
            # Check that the hash of the block is correct
            if block['previous_hash'] != self.hash(last_block):
                return False

            # Check that the Proof of Work is correct
            if not self.valid_proof(last_block['proof'], block['proof']):
                return False

            last_block = block
            current_index += 1

        return True

    def resolve_conflicts(self):
        """
        This is our Consensus Algorithm, it resolves conflicts
        by replacing our chain with the longest one in the network.
        :return: <bool> True if our chain was replaced, False if not
        """
        neighbours = self.nodes
        new_chain = None

        # We're only looking for chains longer than ours
        max_length = len(self.chain)

        # Grab and verify the chains from all the nodes in our network
        for node in neighbours:
            blockchain = BlockchainNeighbours[node]
            print('node id: %s, len: %d' % (blockchain.node_identifier, len(blockchain.chain)))

            # Check if the length is longer and the chain is valid
            if len(blockchain.chain) > max_length and self.valid_chain(blockchain.chain):
                max_length = len(blockchain.chain)
                new_chain = blockchain

        # Replace our chain if we discovered a new, valid chain longer than ours
        if new_chain:
            print("Replacing `%s' <- `%s'" % (self.node_identifier, new_chain.node_identifier))
            self.chain = copy.copy(new_chain.chain)
            return True

        return False
def mine2(blockchain):
    # We run the proof of work algorithm to get the next proof...
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    # We must receive a reward for finding the proof.
    # The sender is "0" to signify that this node has mined a new coin.
    blockchain.new_transaction(
        sender="0",
        recipient=blockchain.node_identifier,
        amount=1,
    )

    # Forge the new Block by adding it to the chain
    previous_hash = blockchain.hash(last_block)
    block = blockchain.new_block(proof, previous_hash)

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }
    return response<jupyter_output><empty_output><jupyter_text>Create multi-node Blockchain Create a blockchain for three nodes and register it as a neighboring node.<jupyter_code># Create block chains for three nodes with node identifiers 'foo', 'bar', 'buz'.
foo = Blockchain2('foo')
bar = Blockchain2('bar')
buz = Blockchain2('buz')

# Register in the list of neighboring nodes
BlockchainNeighbours['foo'] = foo
BlockchainNeighbours['bar'] = bar
BlockchainNeighbours['buz'] = buz

# 'bar', 'buz' registered as 'foo' node neighbor
foo.register_node('bar')
foo.register_node('buz')

# Register neighbors for 'bar', 'buz' nodes
bar.register_node('foo')
bar.register_node('buz')

buz.register_node('foo')
buz.register_node('bar')<jupyter_output><empty_output><jupyter_text>In the initial state, the chain of all nodes has a length of 1.<jupyter_code>print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))<jupyter_output>foo: 1, bar: 1, buz: 1<jupyter_text>Even if you try to resolve the conflict in the initial state, there is no node with chain longer than the foo node, so the chain of foo node is not changed.<jupyter_code>foo.resolve_conflicts()<jupyter_output>node id: buz, len: 1
node id: bar, len: 1<jupyter_text>Expansion of block in some nodes Next, mining on the bar node and adding one block.<jupyter_code>pp.pprint(mine2(bar))<jupyter_output>{ 'index': 2,
  'message': 'New Block Forged',
  'previous_hash': '8788b57301aba5522ac6c464ccb3118f854ea02d94ae79e59c150b9d2f70438a',
  'proof': 35293,
  'transactions': [{'amount': 1, 'recipient': 'bar', 'sender': '0'}]}<jupyter_text>Then the length of the bar node is 2 only.<jupyter_code>print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))
pp.pprint(foo.chain)
pp.pprint(bar.chain)
pp.pprint(buz.chain)<jupyter_output>[ { 'index': 1,
    'previous_hash': 1,
    'proof': 100,
    'timestamp': 1549968320.5750966,
    'transactions': []}]<jupyter_text>Eliminate conflicts between nodes In this state trying to resolve the conflict on the foo node, the chain of foo nodes is overwritten with a chain of (longer) bar nodes.<jupyter_code>foo.resolve_conflicts()<jupyter_output>node id: buz, len: 1
node id: bar, len: 2
Replacing `foo' <- `bar'<jupyter_text>When the resolution of the conflict is completed, the chain length of the foo node becomes 2.<jupyter_code>print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))<jupyter_output>foo: 2, bar: 2, buz: 1<jupyter_text>When the content of the chain is different for each node Next, consider the case where the content of the chain differs for each node.Here we see the case where the contents of the foo node and the buz node are different.<jupyter_code># buz Align the contents of nodes
buz.resolve_conflicts()
print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))<jupyter_output>node id: foo, len: 2
node id: bar, len: 2
Replacing `buz' <- `foo'
foo: 2, bar: 2, buz: 2<jupyter_text>Here, two blocks at foo node, one block at buz node, and blocks holding different transactions are added.<jupyter_code>foo.new_transaction('AAA', 'BBB', 123)
mine2(foo)
foo.new_transaction('CCC', 'DDD', 456)
mine2(foo)

buz.new_transaction('EEE', 'FFF', 789)
mine2(buz)

print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))<jupyter_output>foo: 4, bar: 2, buz: 3<jupyter_text>The content of the chain of foo node and buz node at this time is as follows. You can see that the contents are different from the middle.<jupyter_code>pp.pprint(foo.chain)
pp.pprint(buz.chain)<jupyter_output>[ { 'index': 1,
    'previous_hash': 1,
    'proof': 100,
    'timestamp': 1549968320.5750966,
    'transactions': []},
  { 'index': 2,
    'previous_hash': '8788b57301aba5522ac6c464ccb3118f854ea02d94ae79e59c150b9d2f70438a',
    'proof': 35293,
    'timestamp': 1549968380.794671,
    'transactions': [{'amount': 1, 'recipient': 'bar', 'sender': '0'}]},
  { 'index': 3,
    'previous_hash': '04a510eb7f3a5ae6331acef120ba4f182891f175a8101ffb8a1247f01391eb27',
    'proof': 35089,
    'timestamp': 1549968538.7675357,
    'transactions': [ {'amount': 789, 'recipient': 'FFF', 'sender': 'EEE'},
                      {'amount': 1, 'recipient': 'buz', 'sender': '0'}]}]<jupyter_text>Even if trying to resolve the conflict at the foo node in this state, the content of the chain does not change.<jupyter_code>print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))
print(foo.resolve_conflicts())
print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))<jupyter_output>foo: 4, bar: 2, buz: 3
node id: buz, len: 3
node id: bar, len: 2
False
foo: 4, bar: 2, buz: 3<jupyter_text>On the other hand, if you resolve the conflict at the buz node, the content of the chain is rewritten.<jupyter_code>print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))
print(buz.resolve_conflicts())
print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))<jupyter_output>foo: 4, bar: 2, buz: 3
node id: foo, len: 4
node id: bar, len: 2
Replacing `buz' <- `foo'
True
foo: 4, bar: 2, buz: 4<jupyter_text>Consequently, the chain of buz nodes is discarded and overwritten with a chain of foo nodes.<jupyter_code>pp.pprint(foo.chain)
pp.pprint(buz.chain)<jupyter_output>[ { 'index': 1,
    'previous_hash': 1,
    'proof': 100,
    'timestamp': 1549968320.5750966,
    'transactions': []},
  { 'index': 2,
    'previous_hash': '8788b57301aba5522ac6c464ccb3118f854ea02d94ae79e59c150b9d2f70438a',
    'proof': 35293,
    'timestamp': 1549968380.794671,
    'transactions': [{'amount': 1, 'recipient': 'bar', 'sender': '0'}]},
  { 'index': 3,
    'previous_hash': '04a510eb7f3a5ae6331acef120ba4f182891f175a8101ffb8a1247f01391eb27',
    'proof': 35089,
    'timestamp': 1549968538.542543,
    'transactions': [ {'amount': 123, 'recipient': 'BBB', 'sender': 'AAA'},
                      {'amount': 1, 'recipient': 'foo', 'sender': '0'}]},
  { 'index': 4,
    'previous_hash': 'f45c47824d82b6ac19334f49173a3dfbec3ded72bcb01f1f8614cb426d9eeb4d',
    'proof': 119678,
    'timestamp': 1549968538.717537,
    'transactions': [ {'amount': 456, 'recipient': 'DDD', 'sender': 'CCC'},
                      {'amount': 1, 'recipient': 'foo', 'sender': '0'}]}]
{'fraction_non_alphanumeric': 0.12071280991735538, 'fraction_numerical': 0.05025826446280992, 'mean_word_length': 3.2116597781161627, 'pattern_counts': {'":': 0, '<': 121, '<?xml version=': 0, '>': 117, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '28', 'dir': 'jupyter-structured-clean-dedup', 'id': '323179', 'license': 'MIT', 'n_tokens_mistral': 6915, 'n_tokens_neox': 6192, 'n_words': 1772, 'path': 'chapter01_basic/blockchain_01.ipynb', 'repo_name': '2D/blockchain-guide', 'size': '19444'}