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 实现区块类结构-添加时间戳
{
"index": 0, # 块索引
"timestamp": "", # 块生成时间
"transactions": [ # 块包含的交易
{
"sender": "", # 交易发送者
"recipient": "", # 交易接收者
"amount": "" # 交易金额
}
],
"proof": "", # 块的证明
"previous_hash": "" # 前一个块的哈希值
}3-3 实现区块类结构-添加交易
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):
pass3-4 实现创建区块
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 实现工作量证明
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 添加节点通信功能
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 交易接口实现
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 挖矿接口实现
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 实现注册节点
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
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
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
pragma solidity ^0.4.0;
// This is a test Contact
contract ForImport {
}solidity.sol
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)
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类型讲解-整形
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类型讲解-常量
// 有理数和整形常量
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()
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()
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;
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)
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
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参数
- 函数参数
- 输入参数
- 输出参数
- 命名参数
- 参数解构
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;
}
}解构
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控制结构
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,因为不需要处理内部函数调用。
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)
}
}| 特性 | public | external | internal |
|---|---|---|---|
| 可见范围 | 合约内部 + 外部 | 仅外部 (合约或用户) | 合约内部 + 继承合约 |
| 内部调用 | 直接调用 (func()) | 不可直接调用 | 直接调用 (func()) |
| 外部调用 | 支持 (contract.func()) | 支持 (contract.func()) | 不可外部调用 |
| 继承访问 | 子合约可调用 | 子合约不可直接调用 | 子合约可调用 |
| Gas 开销 | 较高 (复制参数到内存) | 较低 (参数用 calldata) | 中等 |
| 典型场景 | 通用接口、需内外调用的逻辑 | 跨合约调用、用户交易入口 | 内部工具函数、可继承逻辑 |
5-14 solidity函数
- 函数
- 构造函数
- 视图函数(constant/view)
- 纯函数(pure)
- 回退函数 ((一个合约,只能有一个无名函数))
第6章 区块链去中心化应用开发
6-1 合约实战-简单代币开发
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 合约卖战-实现标准代币接口
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 合约卖战-实现标准代币实现
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 install -g truffle
truffle unbox pet_shop6-6 DAPP实战- 宠物领养合约编写
- Adoption.sol
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
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
var Adoption = artifacts.require("./Adoption");
module.exports = function(deployer) {
deployer.deploy(Adoption);
};安装 ganache
- 配置网络 truffle.js
MediaSourceHandle.exports = {
networks: {
development: {
host: "localhost",
port: 7545,
network_id: "*" // Match any network id
}
},
compilers: {
solc: {
version: "0.5.0"
}
}
} truffle compile
truffle migrate6-8 DAPP实战- 合约测试用例
test/TestAdoption.sol
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
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章 去中心化应用运行
- 源码地址: