基于指针数组实现简单的区块链 day(1)
- 一、比特币的基础知识
- 1.1 比特币
- 1.2 区块
- 1.3 区块链
- 1.4 记账
- 1. 5 钱包
- 1.6 节点
- 1.7 挖矿
- 1.8出块时间
- 1.9 出块奖励
- 1.10 比特币总量
- 1.11 区块容量
- 1.12 每秒成交量
- 1.13 单位
- 二、其他基础知识
- 2.1 挖矿(工作量证明)
- 2.2 go演示挖矿
- 2.3 地址生成规则
- 2.4 base64
- 2.5 交易
- 三、模拟简单的区块链
- 3.1 创建简单的区块
- 3.2 创建简单的区块链
- 3.3 创建创世块
- 3.4 向区块链中添加区块
- 四、模拟复杂的区块链
- 4.1 将unit64转换成byte类型
- 4.2 bytes.Join()
- 4.3 挖矿演示
- 五、git命令
- 5.1 git pull
- 5.2 git push
- 5.3 git push --set-upstream origin v2
一、比特币的基础知识
1.1 比特币
一个软件系统,每个人都可以下载使用,使用者之间不断进行交易,产生数据
1.2 区块
比特币系统使用者之间交易的数据以数据块的形式存储,最终存储在数据库中,这个包含交易数据的数据块我们称之为区块。
1.3 区块链
保存上述所有区块的数据库我们称之为区块链。
1.4 记账
将系统中的交易数据写到数据库中我们称之为记账
1. 5 钱包
- 创建私钥公钥,保存公钥,相当于钱包,可以存放多个地址
- 地址类似银行卡,私钥类似银行卡密码
- 钱包会便利账本的交易信息,来得知有没有钱
- 一个地址对应一个私钥
1.6 节点
每一个运行区块链挖矿软件的人都会便成为一个区块链网络的节点。
1.7 挖矿
节点之间竞争记账的权利就称之为挖矿。
1.8出块时间
大约10分钟出一个比特币
1.9 出块奖励
1.10 比特币总量
1.11 区块容量
1M大约容纳4000笔交易
1.12 每秒成交量
1.13 单位
sat(聪)
二、其他基础知识
2.1 挖矿(工作量证明)
sha256(区块数据+随机数据)<目标的哈希值
2.2 go演示挖矿
import (
"crypto/sha256"
"fmt"
)
func main(){
data := "helloWord"
for i:=0; i < 100; i++{
res := sha256.Sum256([]byte(data + string(i)))
fmt.Printf("%x", res[:])
fmt.Println()
}
}
2.3 地址生成规则
私钥经过椭圆曲线相乘生成公钥,在通过哈希算法得到比特币地址。
2.4 base64
0-9,a-z,A-Z,+,-
2.5 交易
- 挖矿交易
- 普通交易
三、模拟简单的区块链
3.1 创建简单的区块
type Block struct{
PreBlockHash []byte
Hash []byte
Data []byte
}
//创建block
func createBlock(preBlockHash []byte, data string) *Block{
block := Block{
PreBlockHash: preBlockHash,
Data: []byte(data),
Hash: []byte{},
}
block.SetHash()
return &block
}
// SetHash 计算当前区块的hash
func (block *Block)SetHash(){
//...的作用是把data数组打散,一个个装进preBlockHash切片中
blockInfo := append(block.PreBlockHash,block.Data...)
//hash是一个字节数组
Hash := sha256.Sum256(blockInfo)
//block.Hash作为Hash的切片
block.Hash = Hash[:]
}
func main() {
//创建一个区块
var block *Block = createBlock([]byte{}, "第一个区块")
fmt.Printf("上一区块的hash值%x:",block.PreBlockHash)
fmt.Println()
fmt.Printf("当前区块的hash值%x:",block.Hash)
fmt.Println()
fmt.Println("当前区块的数据",string(block.Data))
}
结果:
前面区块的hash值 []
当前区块的hash值328fffe02f9e57c15508352cb145aa68abcf83da69a059e975d2171b1e7f8f92
当前区块的数据 第一个区块
3.2 创建简单的区块链
- 本质上是用一个区块指针类型的数组构成的区块链
// BlockChain 区块链结构体
//本质上是区块指针类型的数组
type BlockChain struct {
blockChain []*Block
}
// CreateBlockChain 创建带有一个创世块的区块链
func CreateBlockChain() *BlockChain{
blockChain := BlockChain{
blockChain: []*Block{GenesisBlock()},
}
return &blockChain
}
3.3 创建创世块
// GenesisBlock 创建创世块,即第一个区块
func GenesisBlock() *Block{
genesisBlock := CreateBlock([]byte{},"第一个创世块,牛逼")
return genesisBlock
}
3.4 向区块链中添加区块
// AddBlock 当前区块的前一区块哈希值字段从区块链中获取
func (bc *BlockChain)AddBlock(data string){
preHash := bc.blockChain[len(bc.blockChain)-1].Hash
block := CreateBlock(preHash, data)
bc.blockChain = append(bc.blockChain, block)
}
四、模拟复杂的区块链
4.1 将unit64转换成byte类型
// Uint64ConvertByte 将uint64类型转换为[]byte{}类型
func Uint64ConvertByte(data uint64)[]byte{
var buffer bytes.Buffer
err := binary.Write(&buffer, binary.BigEndian,data)
if err != nil {
log.Panicln(err)
}
return buffer.Bytes()
}
4.2 bytes.Join()
- 将二维byte数组连接成一个一维byte数组
func (block *Block)SetHash(){
var blockInfo []byte
//...的作用是把data数组打散,一个个装进preBlockHash切片中
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Version)...)
//blockInfo = append(blockInfo, block.PreBlockHash...)
//blockInfo = append(blockInfo, block.MerkelRoot...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.TimeStamp)...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Difficulty)...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Nonce)...)
//blockInfo = append(blockInfo, block.Data...)
// 创建一个二维数组
tem := [][]byte{
Uint64ConvertByte(block.Version),
block.PreBlockHash,
block.MerkelRoot,
Uint64ConvertByte(block.TimeStamp),
Uint64ConvertByte(block.Difficulty),
Uint64ConvertByte(block.Nonce),
block.Data,
}
//将二维byte数组连接成一维byte数组
bytes.Join(tem, []byte{})
//hash是一个字节数组
Hash := sha256.Sum256(blockInfo)
//block.Hash作为Hash的切片
block.Hash = Hash[:]
}
4.3 挖矿演示
type ProofOfWork struct {
block *Block
target *big.Int
}
//CreatePOW 创建ProofOfWork
func CreatePOW(block *Block) *ProofOfWork{
pow := ProofOfWork{
block: block,
}
target := "0000100000000000000000000000000000000000000000000000000000000000"
bigNum := big.Int{}
//res是指针类型的
res, _ := bigNum.SetString(target, 16)
pow.target = res
return &pow
}
//Run 返回一个Hash值和随机数
func (pow *ProofOfWork) Run()([]byte, uint64){
tmpBigInt := &big.Int{}
//与给定的目标哈希值进行比较,小于则挖矿成功
var nonce uint64 = 0
var hash [32]byte
for{
block := pow.block
tem := [][]byte{
Uint64ConvertByte(block.Version),
block.PreBlockHash,
block.MerkelRoot,
Uint64ConvertByte(block.TimeStamp),
Uint64ConvertByte(block.Difficulty),
Uint64ConvertByte(nonce),
block.Data,
}
// blockInfo 拼装好的数据
blockInfo := bytes.Join(tem, []byte(""))
hash = sha256.Sum256(blockInfo)
tmpBigInt.SetBytes(hash[:])
res := tmpBigInt.Cmp(pow.target)
if res == -1{
break
}
nonce ++
}
return hash[:],nonce
}
五、git命令
5.1 git pull
将远程主机 origin 的 master 分支拉取过来,与本地的 brantest 分支合并。
git pull origin master:brantest
5.2 git push
git push 命用于从将本地的分支版本上传到远程并合并。
命令格式如下:
git push <远程主机名> <本地分支名>:<远程分支名>
如果本地分支名与远程分支名相同,则可以省略冒号:
git push <远程主机名> <本地分支名>
5.3 git push --set-upstream origin v2
- 设置本地分支追踪远程分支