背景
从2017年11月启动至今,经过历时近一年的研究、开发与测试,初链主网Beta版于新加坡时间2018年09月28日08:00正式上线,在此之前,07:56分PBFT委员会第一次共识出块和TrueChain fPOW创世区块被挖出。
从turechain官方的接受中我们可以了解初链TrueChain主网Beta版,亮点纷呈:
- 全球首家实现无许可(Permissionless)环境下的混合共识公链,科学采用双链结构,并已在美国获得先行专利
- 全球首家在工程上实现将水果链整合到PoW,发布自己的FPOW机制的公链
- 原创性的推出TrueHash,实现不依赖手动调整,从而根本上抗ASIC的挖矿算法
- 原创性实现安全的混合共识PBFT委员会的随机选举机制
- 在尚未实现Sharding技术情况下,在内测阶段的每秒钟交易速度TPS均值达到1200左右,峰值达到1900左右
其中第二点讲到初链是全球首家在工程上实现将水果链整合到PoW,发布自己的FPOW机制的公链。很多人可能就问了,什么是水果链子,什么是fpow。本文将从这个角度进行一定解读。
水果链
FruitChains是由康奈尔大学教授Rafael Pass,副教授Elaine Shi两个人共同创建的,FruitChains是基于中本聪的比特币来做的改善,所以也是基于比特币链协议来实现的,和比特币不一样的地方在于加入了一个Fruit的概念,Fruit类似Block也需要POW,只是Fruit的计算难度比较低,并且水果是挂在最近的Blcok下面,交易信息都保存在Fruit里面。
fpow
初链的fpow共识,便是基于水果链(FruitChains)论文的工程化实现。其中snailchain便是基于fpow共识
//go:generate gencodec -type SnailHeader -field-override headerMarshaling -out gen_header_json.go
// Header represents a block header in the Ethereum truechain.
type SnailHeader struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
PointerHash common.Hash `json:"pointerHash" gencodec:"required"`
PointerNumber *big.Int `json:"pointerNumber" gencodec:"required"`
//水果块的哈希
FruitsHash common.Hash `json:"fruitsHash" gencodec:"required"`
FastHash common.Hash `json:"fastHash" gencodec:"required"`
FastNumber *big.Int `json:"fastNumber" gencodec:"required"`
SignHash common.Hash `json:"signHash" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
FruitDifficulty *big.Int `json:"fruitDifficulty" gencodec:"required"`
Number *big.Int `json:"number" gencodec:"required"`
Publickey []byte `json:"publicKey" gencodec:"required"`
ToElect bool `json:"toElect" gencodec:"required"`
Time *big.Int `json:"timestamp" gencodec:"required"`
Extra []byte `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
Nonce BlockNonce `json:"nonce" gencodec:"required"`
Fruit bool
//存放水果块的水果池
}
type SnailBody struct {
Fruits []*SnailBlock //水果块集合
Signs []*PbftSign //拜占庭共识签名
}
// Block represents an entire block in the Ethereum blockchain.
type SnailBlock struct {
header *SnailHeader //区块头
fruits SnailBlocks //水果块
signs PbftSigns //拜占庭共识签名
uncles []*SnailHeader
// caches
hash atomic.Value
size atomic.Value
difficulty atomic.Value
// Td is used by package core to store the total difficulty
// of the chain up to and including the block.
td *big.Int
// These fields are used by package etrue to track
// inter-peer block relay.
ReceivedAt time.Time
ReceivedFrom interface{}
}
从上述snailchain的区块结构相关代码中我们可以看到,snailblock中有许多水果块的相关信息。
SnailHeader中用记录水果块的哈希FruitsHash。
Fruitbool,水果块将交易信息打包完会放入Fruit bool
SnailBody中的Fruits[]*SnailBlock也可见snail主体部分有水果块组成
为什么用fpow代替pow
1
在pow共识体系中,以btc为代表,矿工在挖矿的过程中存在私自挖矿问题。什么私自挖矿问题呢?
一个矿工或矿池不向网络的其余部分公布和分发一个有效的解。自私的矿工然后继续挖掘下一个区块,继续保持他的领先地位。当网络的其余部分即将赶上自私的矿工时,他才向网络释放已解出的区块。结果是,他的链和工作量证明更长和更困难,因此,网络的其余部分采用他的区块解,他获得了区块奖励。
turechain是这样解决的,看代码
func (m *Minerva) VerifyFreshness(fruit, block *types.SnailHeader, canonical bool) error {
var headerNumber *big.Int
if block == nil {
// when block is nil, is used to verify new fruits for next block
headerNumber = new(big.Int).Add(m.sbc.CurrentHeader().Number, common.Big1)
} else {
headerNumber = block.Number
}
// check freshness
pointer := m.sbc.GetHeaderByNumber(fruit.PointerNumber.Uint64())
if pointer == nil {
return types.ErrSnailHeightNotYet
}
if canonical {
if pointer.Hash() != fruit.PointerHash {
log.Debug("VerifyFreshness get pointer failed.", "fruit", fruit.FastNumber, "pointerNumber", fruit.PointerNumber, "pointerHash", fruit.PointerHash,
"fruitNumber", fruit.Number, "pointer", pointer.Hash())
return consensus.ErrUnknownPointer
}
} else {
pointer = m.sbc.GetHeader(fruit.PointerHash, fruit.PointerNumber.Uint64())
if pointer == nil {
return consensus.ErrUnknownPointer
}
}
freshNumber := new(big.Int).Sub(headerNumber, pointer.Number)
if freshNumber.Cmp(params.FruitFreshness) > 0 {
log.Debug("VerifyFreshness failed.", "fruit sb", fruit.Number, "fruit fb", fruit.FastNumber, "poiner", pointer.Number, "current", headerNumber)
return consensus.ErrFreshness
}
return nil
}
//master/miner/worker.go
func (env *Work) Fruit(fruit *types.SnailBlock, bc *chain.SnailBlockChain, engine consensus.Engine) error {
err := engine.VerifyFreshness(fruit.Header(), env.header, true)
if err != nil {
log.Debug("commitFruit verify freshness error", "err", err, "fruit", fruit.FastNumber(), "pointer", fruit.PointNumber(), "block", env.header.Number)
return err
}
return nil
}
从上述代码中我们可以了解下流程,commitFruit()这个函数的工作在于确认水果块,判断水果块是否可用。在函数实现里我们可以看到调用了一个VerifyFreshness的方法,从字面意思我们可以了解这是一个验证Freshness的方法,所以我们大概可以猜到Freshness可能是个防止私自挖矿的关键熟悉。接下来我们看看VerifyFreshness的相关代码。
func (m *Minerva) VerifyFreshness(fruit, block *types.SnailHeader, canonical bool) error {
var headerNumber *big.Int
if block == nil {
// when block is nil, is used to verify new fruits for next block
headerNumber = new(big.Int).Add(m.sbc.CurrentHeader().Number, common.Big1)
} else {
headerNumber = block.Number
}
// check freshness
pointer := m.sbc.GetHeaderByNumber(fruit.PointerNumber.Uint64())
if pointer == nil {
return types.ErrSnailHeightNotYet
}
if canonical {
if pointer.Hash() != fruit.PointerHash {
log.Debug("VerifyFreshness get pointer failed.", "fruit", fruit.FastNumber, "pointerNumber", fruit.PointerNumber, "pointerHash", fruit.PointerHash,
"fruitNumber", fruit.Number, "pointer", pointer.Hash())
return consensus.ErrUnknownPointer
}
} else {
pointer = m.sbc.GetHeader(fruit.PointerHash, fruit.PointerNumber.Uint64())
if pointer == nil {
return consensus.ErrUnknownPointer
}
}
freshNumber := new(big.Int).Sub(headerNumber, pointer.Number)
if freshNumber.Cmp(params.FruitFreshness) > 0 {
log.Debug("VerifyFreshness failed.", "fruit sb", fruit.Number, "fruit fb", fruit.FastNumber, "poiner", pointer.Number, "current", headerNumber)
return consensus.ErrFreshness
}
return nil
}
所以由于Block里面包含了Fruit并且要求Fruit是最近的(新鲜度),使得攻击者私自扣留的水果失效,无法通过分叉长度抢占区块共识,也就不会出现私自挖矿的情况。这便是水果的意义所在。
2
水果链是轻节点挖矿难度较低,所以不需要投入大量的挖矿设备、电力和带宽,普通参与者使用普通的计算机,甚至于使用手机就能实现挖矿,所以无需加入矿池,节点更加分散,独立化,就不会出现矿场胁持带来51%的攻击问题。挖掘Fruit难度非常小,提高TPS能力并提高响应速度,这样交易费用不稳定的问题也可以解决。
func CalcFruitDifficulty(config *params.ChainConfig, time uint64, fastTime uint64, pointer *types.SnailHeader) *big.Int {
diff := new(big.Int).Div(pointer.Difficulty, params.FruitBlockRatio)
delta := time - fastTime
if delta > 20 {
diff = new(big.Int).Div(diff, big.NewInt(2))
} else if delta > 10 && delta <= 20 {
diff = new(big.Int).Mul(diff, big.NewInt(2))
diff = new(big.Int).Div(diff, big.NewInt(3))
}
if diff.Cmp(params.MinimumFruitDifficulty) < 0 {
diff.Set(params.MinimumFruitDifficulty)
}
//log.Debug("CalcFruitDifficulty", "delta", delta, "diff", diff)
return diff
}
该代码就用于设置fruit挖矿难度,并使其低于正常挖矿难度
总结
truechain共识中的fpow在目前公链领域实属创新,兼顾了去中心化与效率,做到了鱼与熊掌兼得。希望在后期分片技术,已经代码优化后,能更进一个台阶
参考文章