truechain主网上线解读之fpow

背景

从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在目前公链领域实属创新,兼顾了去中心化与效率,做到了鱼与熊掌兼得。希望在后期分片技术,已经代码优化后,能更进一个台阶

参考文章

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值