# 实现区块链 https://jeiwan.net/ ## 原型 简化的区块模型. - block - prev_hash - data - hash - `sha256(prev_hash+data+timestamp)` - 创世区块 ## PoW - 寻找一个符合条件的hash - [Hashcash](https://en.wikipedia.org/wiki/Hashcash)算法 - 一个hash的前20位要为0. - btc中这个要求位数不停变化 - 达到每10分钟出一个块的目标. 核心代码: ```go type POW struct { b *Block target *big.Int } func NewPOW(b *Block) *POW { target := big.NewInt(1) target.Lsh(target, uint(256-TargetBits)) return &POW{b, target} } func (p *POW) Run() (int, Hash) { hash := Hash{} var hashInt big.Int nonce := 0 log.Printf("mining: %s", p.b.Data) for nonce < MaxNonce { data := p.prepare(nonce) hash = sha256.Sum256(data) hashInt.SetBytes(hash[:]) if hashInt.Cmp(p.target) == -1 { // -1 if hash < target log.Printf("pow: %x", hash) break } else { nonce++ } } return nonce, hash } func (p *POW) Validate() bool { var hashInt big.Int data := p.prepare(p.b.Nonce) hash := sha256.Sum256(data) hashInt.SetBytes(hash[:]) return hashInt.Cmp(p.target) == -1 } func (p *POW) prepare(nonce int) []byte { return bytes.Join( [][]byte{ p.b.PrevHash[:], p.b.Data, HexInt(p.b.Timestamp), HexInt(int64(TargetBits)), HexInt(int64(nonce)), }, []byte{}, ) } ``` ## 持久化 使用`bbolt`. ``` go get go.etcd.io/bbolt ``` 简单来说,Bitcoin Core 使用两个 “bucket” 来存储数据: 1. 其中一个 bucket 是 `blocks`,它存储了描述一条链中所有块的元数据 2. 另一个 bucket 是 `chainstate`,存储了一条链的状态,也就是当前所有的未花费的交易输出,和一些元数据. 只存储块,简单定义以下存储结构: - bucket: `blocks` - keys - tip: `l` - 当前最新的块hash - `` - key为块的hash, 值为块序列化后的字节数组. 定义一个命令行工具. 使用 ``` go get github.com/urfave/cli/v2 ``` 实现命令`add`, 和`print`. ```go app := cli.App{ Commands: []*cli.Command{ { Name: "add", Aliases: []string{"a"}, Usage: "add block", Action: func(ctx *cli.Context) error { c.Add(ctx.Args().First()) return nil }, }, { Name: "print", Aliases: []string{"p"}, Usage: "print all blocks", Action: func(ctx *cli.Context) error { it := c.Iterator() for { b := it.Next() log.Println(b) if b.PrevHash == ZeroHash { break } } return nil }, }, }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } ``` 试验: ``` ➜ tmpchain ./tmpchain add "send 1 btc to alice" 2022/08/20 20:23:07 mining: send 1 btc to alice 2022/08/20 20:23:07 pow: 000000693ac0aa277b3b82811e4bca217b029d2aafea153c8dc9d85b7f556c52 ➜ tmpchain ./tmpchain add "send 2 btcs to tom" 2022/08/20 20:23:18 mining: send 2 btcs to tom 2022/08/20 20:23:29 pow: 000000f7876c0c6267e0110cae45397b08eab567969960dd017b5baa49f0f186 ➜ tmpchain ./tmpchain p 2022/08/20 20:23:42 Prev: 000000693ac0aa277b3b82811e4bca217b029d2aafea153c8dc9d85b7f556c52 Data:send 2 btcs to tom Hash: 000000f7876c0c6267e0110cae45397b08eab567969960dd017b5baa49f0f186 2022/08/20 20:23:42 Prev: 000000d25ccd8758d7af3098796909a03e9947242e31ca6a923f69c38bbed307 Data:send 1 btc to alice Hash: 000000693ac0aa277b3b82811e4bca217b029d2aafea153c8dc9d85b7f556c52 2022/08/20 20:23:42 Prev: 0000000000000000000000000000000000000000000000000000000000000000 Data:Genesis Block Hash: 000000d25ccd8758d7af3098796909a03e9947242e31ca6a923f69c38bbed307 ``` ## 交易1 ![](tx.png) - `UTXO` 模型 - unspent transaction output - 交易输出 - 即实际使用的币 - 交易的输出构成了另一个交易的输入. - 构成 - value - btc中存放币的数量, 单位是_satoshi_, 一亿分之一btc - script pubkey - 交易输入 - 构成 - txid - 上一笔的交易id - vout - 上一笔交易的输出索引(一个交易有多个输出) - script sig - 创世交易 - btc - [可点击这里查看](https://blockchain.info/tx/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b?show_adv=true) - > The Times 03/Jan/2009 Chancellor on brink of second bailout for banks - - btc交易示例 - https://www.blockchain.com/btc/tx/b6f6b339b546a13822192b06ccbdd817afea5311845f769702ae2912f7d94ab5 从头开始调整区块的数据结构. - 块中不存储`data`, 存的是一组打包的交易 - `Txs []*TX` - 由于没有了data, 调整Pow的prepare方法, 给块内的所有交易取hash. - `balance` 计算 - 遍历链上区块 - 遍历区块内的多个交易 - 找到其中未被消费的`TXOutput` - 就是没有`TXInput`链接的out. - 加总其中包含的`Value` - `send(from, to, amount)`操作 - 取出所有属于`from`的未被消费的`TXOutput` - 生成一条交易,把凑够`amount`的`TXOutput`指给`to`. - 如果多个txoutput的扣额大于`amount`, 把差额生成一条交易,退给`from`. ## 地址