区块链基础 & 公链原理

 

0x0 区块链相关概念

法币:日常生活中用到的纸币

去中心化:比特币不需要中间机构做货币的发行工作 - 货币是被「发现」的,而不是「发明」的。需要注意的是,并不是所有的区块链项目都是去中心化的

挖矿:「结点A」希望给「结点B」转钱,需要「矿工」帮A把钱「搬运」过去

广播:矿工除了能「挖矿」,还可以「广播」 - 每挖成功一次矿,就会给所有结「广播」一次

账本:每个结点都有一个「账本」

区块链:每个结点维护的「账本」就叫做「区块链」

区块:每笔转账记录消息,就叫「区块」

创世区块:区块链的第一个区块「创世区块」


矿工挖到矿之后,希望向区块链中,追加一个区块

矿工之所以能挖出来矿,是因为有人在转账

0x1 公链原理(共识算法)

PoW原理

Proof of Work - 工作量证明系统

区块参数

Nonce值:可以理解为序号

Timestamp:挖区块的时间戳

Data:交易信息

Hash:根据当前区块信息计算出来的哈希值

Pre Hash:上一个区块的哈希值

说明

有上表我们知道,参数中除Nonce和Hash,其他参数都是已知且固定的

矿工挖矿的时候,需要先把Nonce值置为1,把四个参数组合起来,计算Hash值。如果当前组合计算出来的Hash值不符合「要求」,则把Nonce累加后,再次计算新的Hash,直到符合要求

要求

比如当前难度为4,则要求计算出来的Hash值,前4个数必须为0000,才算挖矿成功

比特币难度系数有调整机制,会随着所有结点的总算力增强而增强,所以挖矿越来越难

过程模拟

https://anders.com/blockchain/blockchain.html

区块的链接

两个区块的链接方式,是通过后一个区块保存前一个区块的hash值,而链接起来的

公链

把上述步骤原理用代码实现而产生的产品

示意代码片段

type Block struct {
    PreHash   []byte
    Timestamp int64
    Data      []byte
    Hash      []byte
    Nonce     int
}

func main() {
    //创建创世区块
    var genesisBlock = CreateGenesisBlock()
    var newBlock = GenerateNextBlock(genesisBlock)
    fmt.Println("挖出的新区快Data为", newBlock.Data)
    fmt.Println("挖出的新区快Data为", hex.EncodeToString(newBlock.Hash))
}

func CreateGenesisBlock() *Block {
    var genesisBlock = &Block{[]byte{0}, time.Now().Unix(),
        []byte("ab交易1bitcoin"), nil, 0}
    //计算当前区块的hash
    genesisBlock.getBlockHash()
    return genesisBlock
}

//通过PoW挖矿的方式挖新区块
func GenerateNextBlock(oldBlock *Block) *Block {
    //假设难度系数为4,则挖的区块的hash前边必须有4个0才算挖矿成功
    var newBlock = &Block{oldBlock.Hash, time.Now().Unix(),
        []byte("bc交易"), nil, 0}
    //不断改变nonce值,最终实现当前区块的hash的0的个数与系统中要求的难度系数值一致
    nonce := 1
    for {
        var blockInfo = hex.EncodeToString(newBlock.PreHash) + hex.EncodeToString(newBlock.Data) +
            string(nonce) + string(newBlock.Timestamp)

        h := sha256.New()
        h.Write([]byte(blockInfo))
        hashed := h.Sum(nil)
        hashString := hex.EncodeToString(hashed)
        fmt.Println("挖矿中", hashString)
        if strings.HasPrefix(hashString, "0000") {
            fmt.Println("挖矿成功")

            newBlock.Hash = hashed
            return newBlock
        }
        nonce++
    }
}

func (block *Block) getBlockHash() []byte {
    //拼接区块信息
    var blockInfo = hex.EncodeToString(block.Data) + string(block.Nonce) +
        string(block.Timestamp) + hex.EncodeToString(block.PreHash)

    h := sha256.New()
    h.Write([]byte(blockInfo))
    hashed := h.Sum(nil)

    block.Hash = hashed
    return hashed
}

PoS原理

Proof of Stack 权益证明

目的:解决PoW浪费资源的问题

典型产品:以太坊

原理:谁当矿工?根据结点的「总币龄」,计算成为本次矿工的概率

举例

优势

缺点马太效应

注意

代码片段

//创建区块
type Block struct {
    Index     int
    Timestamp string
    Prehash   string
    Hash      string
    Data      int

    //矿工终端地址
    Validator string
}

//创建conn终端连接的数组
var connAddr []net.Conn

//创建节点类型
type Node struct {
    //终端的地址
    Address string
    //币领
    Coins int
}

//  保存终端的对象
var nodes []Node

//通道实现线程通信
var announcements = make(chan string)


//生成新区块
var Blockchain []Block

func main() {

    //区块链中是否有三个区块
    genesisBlock := genesisBlock()

    go func() {
        //通过矿工实现区块的挖矿
        for {
            //此代码会将for循环卡死,
            w := <- announcements

            //将新的区块,利用w旷工,添加到数组中
            generateNextBlock(genesisBlock, 100, w)
        }
    }()

    go func() {
        //每隔开10S选择一次矿工
        for {
            time.Sleep(10 * time.Second)
            winner := pickWinner()
            fmt.Println("系统通过PoS帮您选出的旷工为", winner)
            //将旷工放入到通到中
            announcements <- winner
        }
    }()

    //如何通过终端链接代码上
    netListen, _ := net.Listen("tcp", "127.0.0.1:1234")
    defer netListen.Close()

    //3,等待连接
    for {
        conn, _ := netListen.Accept()
        //将所有的链接保存到数组
        connAddr = append(connAddr, conn)
        //扫描终端
        scanbalance := bufio.NewScanner(conn)
        io.WriteString(conn, "请输入币龄")

        go addNode(scanbalance, &nodes)
    }
}

func addNode(scanbalance *bufio.Scanner, nodes *[]Node) {
    for scanbalance.Scan() {
        txt := scanbalance.Text()
        //打印终端输入的信息
        fmt.Println("您刚才从终端输入的币龄为:", txt)
        //通过时间戳创建地址
        addr := calculateHash(time.Now().String())
        cons, _ := strconv.Atoi(txt)
        node := Node{addr, cons}
        // 将链接终端对象存放到数组 - 协程不安全,建议用channel 或 加锁
        *nodes = append(*nodes, node)
        fmt.Println(nodes)
    }
}

//计算某个字符串的hash
func calculateHash(record string) string {
    h := sha256.New()
    h.Write([]byte(record))
    hashed := h.Sum(nil)
    return hex.EncodeToString(hashed)
}

//计算block的hash
func calculateBlockHash(block Block) string {
    record := block.Timestamp + string(block.Data) +
        block.Prehash + string(block.Index)
    hashCode := calculateHash(record)
    return hashCode
}

func generateNextBlock(oldBlock Block, data int, vald string) Block {
    var newBlock Block
    //设置区块高度
    newBlock.Index = oldBlock.Index + 1
    newBlock.Timestamp = time.Now().String()
    newBlock.Prehash = oldBlock.Hash
    newBlock.Data = data
    newBlock.Hash = calculateBlockHash(newBlock)
    newBlock.Validator = vald
    //添加到区块链
    Blockchain = append(Blockchain, newBlock)
    return newBlock
}

//创建创世区块
func genesisBlock() Block {
    var genesisBlock = Block{0, time.Now().String(), "",
        "", 0, ""}
    //计算genesisBlock的hash值
    genesisBlock.Hash = calculateBlockHash(genesisBlock)
    //将创世区块添加到数组
    Blockchain = append(Blockchain, genesisBlock)
    return genesisBlock
}

//通过PoS共识算法选择旷工
func pickWinner() string {
    //选择旷工,利用PoS共识算法选择旷工
    var lottyPool []string

    //根据币领把对应的旷工地址,存放到数组中
    for i := 0; i < len(nodes); i++ {
        node := nodes[i]
        for j := 0; j < node.Coins; j++ {
            lottyPool = append(lottyPool, node.Address)
        }
    }

    if len(lottyPool) != 0 {
        //通过随机值,找到准备挖矿的旷工
        rand.Seed(time.Now().Unix())
        r := rand.Intn(len(lottyPool))
        workerAddress := lottyPool[r]
        //返回旷工地址
        return workerAddress
    }

    return ""
}

DPoS原理

Delegated Proof of Stake 委托权益证明

目的:解决PoS存在的「马太效应」问题

原理:让每一个人选出可以代表自己利益的人参与到获取区块的争夺中,这样多个小股东就能够通过投票选出自己的代理人,争取自己的利益

代码片段



//实现DPoS原理
var Blockchain []*Block

//选举
type Node struct {
    Name  string //节点名字
    Votes int    // 被选举的票数
}

type Block struct {
    Index     int
    Timestamp string
    Prehash   string
    Hash      string
    Data      int

    //增加代理
    delegate *Node
}

//创建数组,保存所有的节点
var n = make([]*Node, 5)

func main() {

    //创建所有选民
    mockNodes()
    c := sortNodes()

    //下边所有的挖矿均有这三个主节点完成,三个人轮流挖矿
    g := genesisBlock()

    //创建新区块
    newBlock := generateNextBlock(g, 1)

    //模拟挖矿 - 最好使用「队列」
    newBlock.setDelete(c[0])
    newBlock = generateNextBlock(*newBlock, 2)
    newBlock.setDelete(c[1])
    newBlock = generateNextBlock(*newBlock, 3)
    newBlock.setDelete(c[2])
    newBlock = generateNextBlock(*newBlock, 4)
    newBlock.setDelete(c[0])
}

//创建节点
func mockNodes() {
    //创建随机种子
    rand.Seed(time.Now().Unix())
    node1 := Node{"node1", rand.Intn(10)}
    node2 := Node{"node2", rand.Intn(10)}
    node3 := Node{"node3", rand.Intn(10)}
    node4 := Node{"node4", rand.Intn(10)}
    node5 := Node{"node5", rand.Intn(10)}
    n[0] = &node1
    n[1] = &node2
    n[2] = &node3
    n[3] = &node4
    n[4] = &node5
}

//DPoS中选出票数最高的前n位
func sortNodes() []*Node {
    //对所有选民的票数进行排序
    for i := 0; i < 4; i++ {
        for j := 0; j < 4-i; j++ {
            if n[j].Votes < n[j+1].Votes {
                //二者的位置交换
                t := n[j]
                n[j] = n[j+1]
                n[j+1] = t
            }
        }
    }
    return n[:3]
}

func generateNextBlock(oldBlock Block, data int) *Block {
    var newBlock = Block{oldBlock.Index + 1, time.Now().String(),
        oldBlock.Hash, "", data, &Node{"", 0}}
    calculateHash(&newBlock)
    //将新的区块添加到数组
    Blockchain = append(Blockchain, &newBlock)
    return &newBlock
}

func calculateHash(block *Block) {
    record := strconv.Itoa(block.Index) + strconv.Itoa(block.Data) + block.Timestamp +
        block.Prehash
    h := sha256.New()
    h.Write([]byte(record))
    hashed := h.Sum(nil)
    block.Hash = hex.EncodeToString(hashed)
}

func (block *Block) setDelete(node *Node) {
    block.delegate = node
}

//创世区块
func genesisBlock() Block {
    genesis := Block{0, time.Now().String(), "", "", 0, &Node{"", 0}}
    calculateHash(&genesis)
    Blockchain = append(Blockchain, &genesis)
    return genesis
}



0x2 密码学补充

零知识证明

让别人知道你有某个能力,但不能告诉他你是怎么做到的

举例

A拥有B的公钥,A没有见过B,而B见过A的照片,偶然一天2人见面了,B认出了A,但A不能确定面前的人是否是B,这时B要向A证明自己是B,有2个方法:
   (一)B把自己的私钥给A,A用这个私钥对某个数据加密,然后用B的公钥解密,如果正确,则证明对方确实是B。 
   (二)A给出一个随机值,B用自己的私钥对其签名,然后把加密后的数据交给A,A用B的公钥验签,如果能够得到原来的随机值,则证明对方是B
方法(二)属于零知识证明

思考

如何让一个盲人相信我给他的两个相同的求是不同颜色的球

应用

区块链中的「零知识证明」原理:数字签名和验签的过程,就是零知识证明的过程

PKI

证书:用公钥生成证书,证书由CA颁发

PKI:是一个总称,公钥基础设施