跳转到内容

mksz214 - 区块链入门与去中心化应用实战

第1章 课程简介与学习安排

课程导学

第2章 区块链技术的核心概念和原理

2-1 区块链的过去与未来

2-2 比特币是什么?

2-3 比特币原理

2-4 账户所有权问题

2-5 为什么记账-挖矿

2-6 共识机制

2-7 本章总结

第3章 区块链技术核心原理实现

3-1 python 实现区块链环境准备

3-2 建立项目,确定区块结构

3-3 实现区块类结构-添加时间戳

json
{
    "index": 0,  # 块索引
    "timestamp": "", # 块生成时间
    "transactions": [ # 块包含的交易
        {
            "sender": "", # 交易发送者
            "recipient": "", # 交易接收者
            "amount": "" # 交易金额
        }
    ],
    "proof": "", # 块的证明
    "previous_hash": "" # 前一个块的哈希值
}

3-3 实现区块类结构-添加交易

python
class Blockchain:
    def __init__(self):
        self.chain = [] # 链
        self.current_transactions = [] #

    def new_block(self):
        pass

    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1


    @staticmethod
    def hash(block):
        pass

    @property
    def last_block(self):
        pass

3-4 实现创建区块

python

import hashlib
import json
from time import time


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

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

    def new_block(self, proof, previous_hash = None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transcations': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block),
        }

        self.current_transactions = []
        self.chain.append(block)

        return block


    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1


    @staticmethod
    def hash(block):
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

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

3-5 实现工作量证明

python

import hashlib
import json
from time import time, sleep


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

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

    def new_block(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transcations': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block),
        }

        self.current_transactions = []
        self.chain.append(block)

        return block

    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):
        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) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof

    def valid_proof(self, last_proof: int, proof: int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        sleep(1)
        print(guess_hash)
        return guess_hash[:4] == "0000"


testPow = Blockchain()
testPow.proof_of_work(100)

3-6 添加节点通信功能

python

import hashlib
import json
from time import time, sleep

from flask import Flask


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

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

    def new_block(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transcations': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block),
        }

        self.current_transactions = []
        self.chain.append(block)

        return block

    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):
        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) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof

    def valid_proof(self, last_proof: int, proof: int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        sleep(1)
        print(guess_hash)
        return guess_hash[:4] == "0000"


# testPow = Blockchain()
# testPow.proof_of_work(100)


app = Flask(__name__)

@app.route('/index', methods=['GET'])
def index():
    return 'Hello BlockChain'

@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    return "we'all add a new transaction"

@app.route('/mine', methods=['GET'])
def mine():
    return "we'all mine a new block"


@app.route('/chain', methods=['GET'])
def full_chain():
    return "return full chain"



#
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

3-7 交易接口实现

python

import hashlib
import json
from time import time, sleep

from flask import Flask, jsonify, request


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

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

    def new_block(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transcations': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block),
        }

        self.current_transactions = []
        self.chain.append(block)

        return block

    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):
        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) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof

    def valid_proof(self, last_proof: int, proof: int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        print(guess_hash)
        return guess_hash[:4] == "0000"


app = Flask(__name__)
blockchain = Blockchain()

@app.route('/index', methods=['GET'])
def index():
    return 'Hello BlockChain'

@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    values = request.get_json()
    required = ['sender', 'recipient', 'amount']

    if values is None:
        return 'Missing values', 400

    if not all(k in values for k in required):
        return 'Missing values', 400

    index = blockchain.new_transaction(values['sender'],
                               values['recipient'],
                               values['amount'])

    response = {"message": f"Transaction will be added to Block {index}"}

    return jsonify(response), 201

@app.route('/mine', methods=['GET'])
def mine():
    return "we'all mine a new block"


@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain)
    }
    return jsonify(response), 200



#
if __name__ == '__main__':

    app.run(host='0.0.0.0', port=5000, debug=True)

3-8 挖矿接口实现

python
import hashlib
import json
import uuid
from http.client import responses
from time import time, sleep

from flask import Flask, jsonify, request


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

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

    def new_block(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block),
        }

        self.current_transactions = []
        self.chain.append(block)

        return block

    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):
        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) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof

    def valid_proof(self, last_proof: int, proof: int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        print(guess_hash)
        return guess_hash[:4] == "0000"


app = Flask(__name__)
blockchain = Blockchain()

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


@app.route('/index', methods=['GET'])
def index():
    return 'Hello BlockChain'


@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    values = request.get_json()
    required = ['sender', 'recipient', 'amount']

    if values is None:
        return 'Missing values', 400

    if not all(k in values for k in required):
        return 'Missing values', 400

    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])

    response = {"message": f"Transaction will be added to Block {index}"}

    return jsonify(response), 201


@app.route('/mine', methods=['GET'])
def mine():
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender = "0",
                               recipient = node_identifier,
                               amount = 1)

    block = blockchain.new_block(proof, None)

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }

    return jsonify(response), 200


@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain)
    }
    return jsonify(response), 200


#
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

3-9 实现注册节点

python
import hashlib
import json
import uuid
from http.client import responses
from time import time, sleep
from urllib.parse import urlparse

from flask import Flask, jsonify, request


class Blockchain:
    def __init__(self):
        self.chain = []  # 链
        self.current_transactions = []  #
        self.nodes = set()

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

    def register_node(self, address: str):
        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)

    def new_block(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block),
        }

        self.current_transactions = []
        self.chain.append(block)

        return block

    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):
        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) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof

    def valid_proof(self, last_proof: int, proof: int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        print(guess_hash)
        return guess_hash[:4] == "0000"


app = Flask(__name__)
blockchain = Blockchain()

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


@app.route('/index', methods=['GET'])
def index():
    return 'Hello BlockChain'


@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    values = request.get_json()
    required = ['sender', 'recipient', 'amount']

    if values is None:
        return 'Missing values', 400

    if not all(k in values for k in required):
        return 'Missing values', 400

    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])

    response = {"message": f"Transaction will be added to Block {index}"}

    return jsonify(response), 201


@app.route('/mine', methods=['GET'])
def mine():
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender = "0",
                               recipient = node_identifier,
                               amount = 1)

    block = blockchain.new_block(proof, None)

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }

    return jsonify(response), 200


@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain)
    }
    return jsonify(response), 200



@app.route('/nodes/register', methods=['POST'])
def register_nodes():
    # { "nodes": ["http://127.0.0.2:5000"] }
    values = request.get_json()

    # 检查请求是否包含有效的JSON
    if values is None:
        return "Error: Please supply a valid JSON payload", 400

    nodes = values.get('nodes')

    if nodes is None:
        return "Error: Please supply a valid list of nodes", 400

    for node in nodes:
        blockchain.register_node(node)

    response = {
        'message': 'New nodes have been added',
        'total_nodes': list(blockchain.nodes),
    }
    return jsonify(response), 201



#
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

3-10 实现共识机制-1

python
import hashlib
import json
import uuid
from http.client import responses
from time import time, sleep
from urllib.parse import urlparse

import requests
from flask import Flask, jsonify, request


class Blockchain:
    def __init__(self):
        self.chain = []  # 链
        self.current_transactions = []  #
        self.nodes = set()

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

    def register_node(self, address: str):
        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)

    def valid_chain(self, chain) -> bool:
        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")

            if block['previous_hash'] != self.hash(last_block):
                return False

            if not self.valid_proof(last_block['proof'], block['proof']):
                return False

            last_block = block
            current_index += 1

        return True


    def resolve_conflicts(self) -> bool:

        neighbours = self.nodes

        max_length = len(self.chain)
        new_chain = None

        for node in neighbours:
            print(f'{node}')
            response = requests.get(f'http://{node}/chain')
            if response.status_code == 200:
                length = response.json()['length']
                chain = response.json()['chain']

                if length > max_length and self.valid_chain(chain):
                    max_length = length
                    new_chain = chain

        if new_chain:
            self.chain = new_chain
            return True

        return False


    def new_block(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block),
        }

        self.current_transactions = []
        self.chain.append(block)

        return block

    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):
        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) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof

    def valid_proof(self, last_proof: int, proof: int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        print(guess_hash)
        return guess_hash[:4] == "0000"


app = Flask(__name__)
blockchain = Blockchain()

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


@app.route('/index', methods=['GET'])
def index():
    return 'Hello BlockChain'


@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    values = request.get_json()
    required = ['sender', 'recipient', 'amount']

    if values is None:
        return 'Missing values', 400

    if not all(k in values for k in required):
        return 'Missing values', 400

    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])

    response = {"message": f"Transaction will be added to Block {index}"}

    return jsonify(response), 201


@app.route('/mine', methods=['GET'])
def mine():
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender = "0",
                               recipient = node_identifier,
                               amount = 1)

    block = blockchain.new_block(proof, None)

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }

    return jsonify(response), 200


@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain)
    }
    return jsonify(response), 200



@app.route('/nodes/register', methods=['POST'])
def register_nodes():
    # { "nodes": ["http://127.0.0.2:5000"] }
    values = request.get_json()

    # 检查请求是否包含有效的JSON
    if values is None:
        return "Error: Please supply a valid JSON payload", 400

    nodes = values.get('nodes')

    if nodes is None:
        return "Error: Please supply a valid list of nodes", 400

    for node in nodes:
        blockchain.register_node(node)

    response = {
        'message': 'New nodes have been added',
        'total_nodes': list(blockchain.nodes),
    }
    return jsonify(response), 201



#
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

3-11 实现共识机制-2

python
import hashlib
import json
import uuid
from http.client import responses
from time import time, sleep
from urllib.parse import urlparse

import requests
from flask import Flask, jsonify, request
from argparse import ArgumentParser


class Blockchain:
    def __init__(self):
        self.chain = []  # 链
        self.current_transactions = []  #
        self.nodes = set()

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

    def register_node(self, address: str):
        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)

    def valid_chain(self, chain) -> bool:
        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")

            if block['previous_hash'] != self.hash(last_block):
                return False

            if not self.valid_proof(last_block['proof'], block['proof']):
                return False

            last_block = block
            current_index += 1

        return True


    def resolve_conflicts(self) -> bool:

        neighbours = self.nodes

        max_length = len(self.chain)
        new_chain = None

        for node in neighbours:
            print(f'{node}')
            response = requests.get(f'http://{node}/chain')
            if response.status_code == 200:
                length = response.json()['length']
                chain = response.json()['chain']

                if length > max_length and self.valid_chain(chain):
                    max_length = length
                    new_chain = chain

        if new_chain:
            self.chain = new_chain
            return True

        return False


    def new_block(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block),
        }

        self.current_transactions = []
        self.chain.append(block)

        return block

    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):
        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) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof

    def valid_proof(self, last_proof: int, proof: int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        print(guess_hash)
        return guess_hash[:4] == "0000"


app = Flask(__name__)
blockchain = Blockchain()

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


@app.route('/index', methods=['GET'])
def index():
    return 'Hello BlockChain'


@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    values = request.get_json()
    required = ['sender', 'recipient', 'amount']

    if values is None:
        return 'Missing values', 400

    if not all(k in values for k in required):
        return 'Missing values', 400

    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])

    response = {"message": f"Transaction will be added to Block {index}"}

    return jsonify(response), 201


@app.route('/mine', methods=['GET'])
def mine():
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender = "0",
                               recipient = node_identifier,
                               amount = 1)

    block = blockchain.new_block(proof, None)

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }

    return jsonify(response), 200


@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain)
    }
    return jsonify(response), 200



@app.route('/nodes/register', methods=['POST'])
def register_nodes():
    # { "nodes": ["http://127.0.0.2:5000"] }
    values = request.get_json()

    # 检查请求是否包含有效的JSON
    if values is None:
        return "Error: Please supply a valid JSON payload", 400

    nodes = values.get('nodes')

    if nodes is None:
        return "Error: Please supply a valid list of nodes", 400

    for node in nodes:
        blockchain.register_node(node)

    response = {
        'message': 'New nodes have been added',
        'total_nodes': list(blockchain.nodes),
    }
    return jsonify(response), 201

@app.route('/nodes/resolve', methods=['GET'])
def consensus():
    replaced = blockchain.resolve_conflicts()
    if replaced:
        response = {
            'message': 'Our chain was replaced',
            'new_chain': blockchain.chain
        }
    else:
        response = {
            'message': 'Our chain is authoritative',
            'chain': blockchain.chain
        }

    return jsonify(response), 200



#
if __name__ == '__main__':
    parser = ArgumentParser()
    # -p --port
    parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen to')
    args = parser.parse_args()
    port = args.port

    app.run(host='0.0.0.0', port=port, debug=True)

现在所有的工作已经完成了, 我们给节点发送交易 让节点去挖矿,让节点间去解决冲突, 中间前一步还有一个是注册节点,所有这些都已经完成了, 我们一个区块链的雏形。

第4章 以太坊入门

第5章 智能合约编程语言-solidity

5-1 本章概述

5-2 solidity台约文件讲解

solidity_for_import.sol

solidity
pragma solidity ^0.4.0;

// This is a test Contact
contract ForImport {

}

solidity.sol

solidity
pragma solidity ^0.4.0;

import "solidity _for_import.sol";


// This is a test Contact

contract Test {
    uint a;

    function setA(unit x) public {
        a = x;
        emit Set_A(x);
    }

    event Set_A(unit a);

    struct Position {
        int lat;
        int lng;
    }

    address public ownerAddr;

    // 函数修改器
    modifier owner () {
        require(msg.sender == ownerAddr);
        _;
    }

    function mine() public owner {
        A += 1;
    }
}

5-3 solidity类型讲解-bool

值类型 引用类型

值类型

  • 布尔类型(Booleans)
solidity

contract Test {
    uint a;
    bool boola = true;
    bool boolb = false;

    function testbool1() public returns (bool) {
        return boola && boolb;
    }

     function testbool2() public returns (bool) {
        return boola || boolb;
    }
}
  • 整型(lntegers)

    • int/uint 关键字uint8 到 uint256(以8步进)

    定长浮点型(Fixed Point Numbers)

  • 定长字节数组(Fixed-size byte arrays)

  • 有理数和整型常量(Rational and Integer Literals)

  • 字符串常量(String literals)

  • 十六进制常量(Hexadecimal literals)

  • 枚举(Enums)

  • 函数类型(Function Types)

  • 地址类型(Address)

  • 地址常量(Address Literals)

5-4 solidity类型讲解-整形

solidity
pragma solidity ^0.4.0;

contract Test {

    uint a;
    int256 b = 20;
    int256 c = 30;

    function testadd() public view returns (int){
        if(b > c){
            return b + c;
        }else if(b == c){
            return b * c;
        }else {
            return b >> 2;  // b / 2  ** 2
        }
    }

    function testaShift() public view returns (int){
            return b << 1; // b * 2 ** 1
    }

}

5-5 solidity类型讲解-常量

solidity
    // 有理数和整形常量
    function testLiterals() public view returns (int){
  //       return 1 + 1; // 常量
  //       return 1.0 + 1e10; // 常量
           return 1.0 + 1.5e10; // 常量  不会有溢出
    }

    // 字符串常量
    function testStringLiterals() public view returns (string){
        return "abc";
    }

    // 十六进制常量
    function testHexLiterals() public view returns (bytes2, bytes1, bytes1){
        bytes2 a = hex"abcb";
        return (a, a[0], a[1]);
    }

5-6 solidity类型讲解-地址类型

address:表示一个账户地址(20字节)

0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2

成员

  • 属性:balance
  • 函数:transfer()
solidity
contract AddrTest {

    function deposit() public payable {

    }

    function getBalance() public view returns (uint) {
        return this.balance;
    }

    function transferEther(address towho) public {
        towho.transfer(10);
    }

}

5-7 solidity类型讲解-数组

数据位置(Data location)

memory、storage

数组(Arrays)

  • T[k]:元素类型为T,固定长度为k的数组
  • T[]:元素类型为T,长度动态调整
  • bytes string 是一种特殊的数组string 可转为 bytes, bytes 类似byte[]

成员属性:

  • length函数:
  • push()
solidity

pragma solidity ^0.4.16;

contract ArrayTest {

    uint[] public u = [1,2,3,4,5];

    string s = "abcdefg";

    function h() public  returns(uint) {
        u.push(4);
//      return bytes(s)[1];
        return u.length;
    }

    function newM(uint len) public returns(uint[]) {
        uint[] memory a = new uint[](len);

        bytes memory b = new bytes(len);
        a[6] = 8;

        return a.length;
    }

    // 数组是参数
    function g(uint[3] _data) public view {

    }

}

5-8 solidity类型讲解-结构体和映射

结构体(Structs)

struct Person {}

映射(Mappings)

mapping(address => uint)

public balances;

solidity
    struct Funder {
        address addr;
        uint amount;
    }

    Funder funder;

    function newFunder() public {
        funder = Funder({addr: msg.sender, amount: 10 });
    }

    mapping(address => uint) public balances;

    function updateBalance(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }

5-9 solidity类型讲解-区块及交易属性

  • 全局变量和函数

    • 有关区块和交易
    • 有关错误处理
    • 有关数字及加密功能
    • 有关地址和合约
  • 区块和交易属性

    • msg.sender(address)
    • msg.value (uint)
    • block.coinbase (address)
    • block.difficulty (uint)
    • block.number(uint)
    • block.timestamp (uint)
    • now (uint)
    • tx.gasprice (uint)
solidity
    constructor() payable {

    }

    function testApi () public payable returns (uint) {
//        return msg.sender;
//        return msg.value;    (uint)
//        return block.coinbase;   (address)
//        return block.difficulty;  (uint)
    }

5-10 solidity错误处理

错误处理

  • 什么是错误处理
    • 指在程序发生错误时的处理方式
  • 处理方式
    • 回退状态
  • 如何处理 assert、require
solidity
pragma solidity >=0.7.0 <0.9.0;

contract Sharer {

    function sendHalf(address addr) public payable  returns (uint blance){
        // require 判断外部的条件
        require(msg.value % 2 == 0);

        uint balanceBeforeTranfer = address(this).balance;

        payable(addr).transfer(msg.value / 2);

        // assert 判断内部的逻辑
        assert(address(this).balance == balanceBeforeTranfer - msg.value / 2);
        
        return address(this).balance;
        
    }

}

5-11 solidity参数

  • 函数参数
    • 输入参数
    • 输出参数
    • 命名参数
    • 参数解构
solidity
pragma solidity >=0.7.0 <0.9.0;

contract Test {
    function simpleInput(uint a, uint b) public pure returns (uint sum) {
        sum = a + b;
        return sum;
    }

    function testSimpleInput() public pure returns (uint sum) {
        sum = simpleInput(1, 3);
        return sum;
    }
}

解构

solidity
    function f() public pure returns(uint, bool, uint) {
        return (7, true, 2);
    }

    function g() public pure {
        // 显式声明类型并解构赋值
        (uint x, bool y, uint z) = f();
        // 交换 x 和 z 的值(可选)
        (x, z) = (z, x);
    }
}

5-12 solidity控制结构

solidity
    function testWhile() public view returns(uint, uint) {
        uint i = 0;
        uint sumOfOdd = 0;
        uint sumOfEven = 0;

        while(true) {
            i++;

            if ( i % 2 == 0 ) {
              sumOfEven += i;
            } else{
              sumOfOdd += i;
            }

            if(i > 10){
               break;
            }

        }

        sumOfOdd > 20 ? sumOfOdd + 10 : sumOfOdd;

        return (sumOfOdd, sumOfEven);

    }

5-13 solidity可见性

  • 可见性
    • public 公开函数是合约接口的一部分,可以通过内部,或者消息来进行调用。对于public类型的状态变量,会自动创建一个访问器。 函数默认可见性是public
    • private 私有函数和状态变量仅在当前合约中可以访问,在继承的合约内,不可访问。
    • externa 外部函数是合约接口的一部分,只能使用消息调用
    • internal 函数和状态变量只能通过内部访问。如在当前合约中调用,或继承的合约里调用。 状态变量默认的可见性是interna

public函数可以从合约内部和外部被调用;internal函数只能在当前合约内部或继承合约中被调用;external函数只能从合约外部被调用,不能在合约内部直接调用(除非使用this.func()方式)。

关于使用场景,具体建议:public通常用于DApp接口和合约逻辑外调;internal适合可继承的内部工具函数和逻辑封装;external则主要用于面向外部用户的接口或跨合约调用。

关于gas开销的问题,external函数通常比public函数更省gas,因为不需要处理内部函数调用。

python
pragma solidity >=0.7.0 <0.9.0;

contract Test {
    uint public data;

    function f(uint a) private pure returns (uint) {  // 移除命名返回值
        return a + 1;
    }

    function setData(uint a) internal virtual {  // 添加 virtual
        data = a;
    }
    
    function exSetData(uint a) external {
        data = a;
    }

    function testsetData() public {
        setData(1);
        this.exSetData(1); 
    }

    function abc() public {
        testsetData();  // 直接调用
    }
}

contract Test2 is Test {
    function setData(uint a) internal override {  // 添加 override
        data = a;
    }
}

contract D {
    function readData() public {
        Test test = new Test();
        test.exSetData(1);  // 合法调用(external)
    }
}
特性publicexternalinternal
可见范围合约内部 + 外部仅外部 (合约或用户)合约内部 + 继承合约
内部调用直接调用 (func())不可直接调用直接调用 (func())
外部调用支持 (contract.func())支持 (contract.func())不可外部调用
继承访问子合约可调用子合约不可直接调用子合约可调用
Gas 开销较高 (复制参数到内存)较低 (参数用 calldata)中等
典型场景通用接口、需内外调用的逻辑跨合约调用、用户交易入口内部工具函数、可继承逻辑

5-14 solidity函数

  • 函数
    • 构造函数
    • 视图函数(constant/view)
    • 纯函数(pure)
    • 回退函数 ((一个合约,只能有一个无名函数))

第6章 区块链去中心化应用开发

6-1 合约实战-简单代币开发

solidity
pragma solidity >=0.7.0 <0.9.0;

contract MyToken {

    mapping(address => uint256) public balanceOf;

    constructor(uint256 initSupply) public {
       balanceOf[msg.sender] = initSupply;
    }

    function transfer(address _to, uint256 _value) public {
        
        require(balanceOf[msg.sender] >= _value);
        require(balanceOf[_to] + _value >= balanceOf[_to]);

        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;

    }

}

6-2 合约卖战-实现标准代币接口

solidity
pragma solidity ^0.8.0; // 使用新版Solidity内置溢出检查


contract ERC20 {

    string public name;
    string public symbol;
    uint8 public decimals;
    uint public totalSupply;

    function balance0f(address _owner) view returns (uint256 balance);
    function transfer(address _to, uint256 _value) returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
    function approve(address _spender, uint256 _value)returns (bool success);    
    function allowance(address _owner, address _spender) view returns (uint256 remaining);

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);


}

6-3 合约卖战-实现标准代币实现

solidity
pragma solidity ^0.8.0; // 使用新版Solidity内置溢出检查


contract ERC20 {

    string public name;
    string public symbol;
    uint8 public decimals;
    uint public totalSupply;

    function balance0f(address _owner) view returns (uint256 balance);
    function transfer(address _to, uint256 _value) returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
    function approve(address _spender, uint256 _value)returns (bool success);    
    function allowance(address _owner, address _spender) view returns (uint256 remaining);

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);


}

contract ERC20Interface is ERC20 {
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) public allowed;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    constructor() {
        name = "MUKE Token";
        symbol = "IMOOC";
        decimals = 18; // 标准小数位
        totalSupply = 10000000 * 10**decimals;
        _balances[msg.sender] = totalSupply; // 初始化分配
    }

    function balanceOf(address owner) public view returns (uint256) {
        return _balances[owner];
    }

    function transfer(address to, uint256 value) public returns (bool) {
        require(to != address(0));
        require(_balances[msg.sender] >= value);
        _balances[msg.sender] -= value;
        _balances[to] += value;
        emit Transfer(msg.sender, to, value);
        return true;
    }

    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        require(to != address(0));
        require(_balances[from] >= value);
        require(allowed[from][msg.sender] >= value);
        allowed[from][msg.sender] -= value;
        _balances[from] -= value;
        _balances[to] += value;
        emit Transfer(from, to, value);
        return true;
    }

    function approve(address spender, uint256 value) public returns (bool) {
        allowed[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;
    }
    
}

6-4 DAPP实战- 应用介绍及前置知识

  • 应用背景

Pete有一个宠物店,有16只宠物,他想开发一个去中心化应用,让大家来领养宠物。

  • 知识点
  • HTML、JavaScript、Css
  • Web 服务器
  • 智能合约
  • Web3.js
  • 开发流程
  • 新建项目(初始化 )
  • 合约编写
  • 合约编译、部署、测试
  • 与合约交互
  • 开发工具
  • VS Code/Atom/Sublime
  • Truffle/Ganache
  • MetaMask

6-5 DAPP实战- 使用truffle创建项目

npm
npm install -g truffle
truffle unbox pet_shop

6-6 DAPP实战- 宠物领养合约编写

  • Adoption.sol
solidity
pragma solidity >=0.4.22 <0.9.0;

contract Adoption {

    address[16] public adopters;

    // 领养宠物
    function adopt(uint petId) public returns (uint) {
        require(petId >= 0 && petId <= 15);
        adopters[petId] = msg.sender;
        return petId;
    }

    // 获取领养者信息
    function getAdopters() public view returns (address[16] memory) {
        return adopters;
    }

    // 获取宠物信息
    function getPet(uint petId) public view returns (address) {
        require(petId >= 0 && petId <= 15);
        return adopters[petId];
        //return petId;
    }

    // 把领养者的信息 和 对应的宠物 关联起来
}
  • Migrations.sol
solidity
pragma solidity >=0.4.22 <0.9.0;

contract Migrations {
  address public owner = msg.sender;
  uint public last_completed_migration;

  modifier restricted() {
    require(
      msg.sender == owner,
      "This function is restricted to the contract's owner"
    );
    _;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }
}

6-7 DAPP实战- 合约部署

delpoy_contracts.js

javascript
var Adoption = artifacts.require("./Adoption");

module.exports = function(deployer) {
  deployer.deploy(Adoption);
};

安装 ganache

  • 配置网络 truffle.js
javascript
MediaSourceHandle.exports = {
  networks: {
    development: {
      host: "localhost",
      port: 7545,
      network_id: "*" // Match any network id
    }
  },
  compilers: {
    solc: {
      version: "0.5.0"
    }
  }
}
npm
 truffle compile
 truffle migrate

6-8 DAPP实战- 合约测试用例

test/TestAdoption.sol

solidity
pragma solidity >=0.4.22 <0.9.0;


import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";

contract TestAdoption {

  Adoption adoption = Adoption(DeployedAddresses.Adoption());

  // Test case
  function testUserCanAdoptPet() public {
    uint returnedId = adoption.adopt(8);

    uint expected = 8;
    Assert.equal(returnedId, expected, "Adoption of pet ID 8 should return 8");
  }

  // Test case
  function testGetAdopterAddressByPetId() public {
    // Given
    // address expected = msg.sender;
    address expected = address(this);

    // When
    address actual = adoption.adopters(8);

    // Then
    Assert.equal(actual, expected, "Owner of pet ID 8 should be msg.sender");
  }


  // Test case
  function testGetAdopterAddressByPetIdInArray() public {
    // Given
    // address expected = this;
    address expected = address(this);


    // When
    address[16] memory adopters = adoption.getAdopters();

    // Then
    Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded");   
  }

}

执行测试的命令

truffle test

6-9 DAPP实战- 初始化web 环境

6-10 DAPP实战-页面编写

6-11 DAPP实战- UI如何与智能合约交互介绍

6-12 DAPP实战- 应用中初始化Web3及合约

6-13 DAPP实战- 实现合约交豆

app.js

javascript
App = {
  web3Provider: null,
  contracts: {},

  init: async function() {
    // Load pets.
    $.getJSON('../pets.json', function(data) {
      var petsRow = $('#petsRow');
      var petTemplate = $('#petTemplate');

      for (i = 0; i < data.length; i ++) {
        petTemplate.find('.panel-title').text(data[i].name);
        petTemplate.find('img').attr('src', data[i].picture);
        petTemplate.find('.pet-breed').text(data[i].breed);
        petTemplate.find('.pet-age').text(data[i].age);
        petTemplate.find('.pet-location').text(data[i].location);
        petTemplate.find('.btn-adopt').attr('data-id', data[i].id);

        petsRow.append(petTemplate.html());
      }
    });

    return await App.initWeb3();
  },

  initWeb3: async function() {
    /*
     * Replace me...
     */
    if (typeof web3 !== 'undefined') {
      App.web3Provider = web3.currentProvider;
    }else {
      // If no injected web3 instance is detected, fall back to Ganache
      App.web3Provider = new Web3.providers.HttpProvider('http://127.0.0.1:7545');
    }

    web3 = new Web3(App.web3Provider);

    return App.initContract();
  },

  initContract: function() {
    /*
     * Replace me...
     */
    $.getJSON('Adoption.json', function(data) {
      var AdoptionArtifact = data;
      App.contracts.Adoption = TruffleContract(AdoptionArtifact);

      App.contracts.Adoption.setProvider(App.web3Provider);

      return App.markAdopted();
    })

    return App.bindEvents();
  },

  bindEvents: function() {
    $(document).on('click', '.btn-adopt', App.handleAdopt);
  },

  markAdopted: function(adopter, account) {
    /*
     * Replace me...
     */
    var adoptionInstance;
    App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;
      return adoptionInstance.getAdopters.call();
    }).then(function(adopters) {
      for (i = 0; i < adopters.length; i++) {
        if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
          $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
        }
      }
    }).catch(function(err) {
      console.log(err.message);
    })
  },

  handleAdopt: function(event) {
    event.preventDefault();

    var petId = parseInt($(event.target).data('id'));

    /*
     * Replace me...
     */
    web3.eth.getAccounts().then(function(error, accounts) {
      var account = accounts[0];
      App.contracts.Adoption.deployed().then(function(instance) {
        adoptionInstance = instance;
        return adoptionInstance.adopt(petId, {from: account});
      }).then(function(result) {
        return App.markAdopted();
      }).catch(function(err) {
        console.log(err.message);
      })
    })
  }

};

$(function() {
  $(window).load(function() {
    App.init();
  });
});

第7章 第7章 去中心化应用运行

  • 源码地址:

https://github.com/hufanglei/tomorrow.git

基于 MIT 许可发布