https://jeiwan.net/
简化的区块模型.
block
prev_hash
data
hash
sha256(prev_hash+data+timestamp)
创世区块
寻找一个符合条件的hash
Hashcash算法
一个hash的前20位要为0.
btc中这个要求位数不停变化
达到每10分钟出一个块的目标.
核心代码:
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” 来存储数据:
其中一个 bucket 是 blocks
,它存储了描述一条链中所有块的元数据
另一个 bucket 是 chainstate
,存储了一条链的状态,也就是当前所有的未花费的交易输出,和一些元数据.
只存储块,简单定义以下存储结构:
bucket: blocks
keys
tip: l
当前最新的块hash
<hash, json(block)>
key为块的hash, 值为块序列化后的字节数组.
定义一个命令行工具. 使用
go get github.com/urfave/cli/v2
实现命令add
, 和print
.
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
UTXO
模型
unspent transaction output
交易输出
即实际使用的币
交易的输出构成了另一个交易的输入.
构成
value
btc中存放币的数量, 单位是_satoshi_, 一亿分之一btc
script pubkey
交易输入
构成
txid
上一笔的交易id
vout
上一笔交易的输出索引(一个交易有多个输出)
script sig
创世交易
btc
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
.