Skip to content

Commit

Permalink
bugfix: check the distance for each workshare
Browse files Browse the repository at this point in the history
  • Loading branch information
gameofpointers committed Sep 4, 2024
1 parent 15de623 commit fdb7fab
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 71 deletions.
5 changes: 5 additions & 0 deletions consensus/blake3pow/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ func (blake3pow *Blake3pow) VerifyUncles(chain consensus.ChainReader, block *typ
return err
}

_, err = chain.WorkShareDistance(block, uncle)
if err != nil {
return err
}

// Verify the block's difficulty based on its timestamp and parent's difficulty
// difficulty adjustment can only be checked in zone
if nodeCtx == common.ZONE_CTX {
Expand Down
39 changes: 4 additions & 35 deletions consensus/blake3pow/poem.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,47 +196,16 @@ func (blake3pow *Blake3pow) WorkShareLogS(chain consensus.ChainHeaderReader, wo
} else {
wsEntropy = new(big.Int).Set(blake3pow.IntrinsicLogS(powHash))
}
var distance int64 = 0
// Discount 2) applies to all shares regardless of the weight
// a workshare cannot reference another workshare, it has to be either a block or an uncle
// check that the parent hash referenced by the workshare is an uncle or a canonical block
// then if its an uncle, traverse back until we hit a canonical block, other wise, use that
// as a reference to calculate the distance
parent := chain.GetBlockByHash(ws.ParentHash())
if parent == nil {
return big.NewInt(0), errors.New("error finding the parent of the work share")
}
// checking if the parent is an uncle
canonicalBlockForParentNum := chain.GetHeaderByNumber(parent.NumberU64(common.ZONE_CTX))
if canonicalBlockForParentNum == nil {
return big.NewInt(0), errors.New("cannot find a canonical block for the parent number")
}
// If this check passes, the parent block is not a canonical block, we have to trace back
if canonicalBlockForParentNum.Hash() != parent.Hash() {
var prevBlock *types.WorkObject
var uncleDist int64 = 0
for {
uncleDist++
prevBlock = chain.GetBlockByHash(parent.Hash())
if prevBlock == nil {
return big.NewInt(0), errors.New("cannot find a parent block of an uncle")
}
blockForPrevBlockNumber := chain.GetHeaderByNumber(prevBlock.NumberU64(common.ZONE_CTX))
if blockForPrevBlockNumber == nil {
return big.NewInt(0), errors.New("cannot find a canonical block for the uncle block number")
}
if prevBlock.Hash() == blockForPrevBlockNumber.Hash() {
break
}
if uncleDist > int64(params.WorkSharesInclusionDepth) {
return big.NewInt(0), errors.New("uncle referenced by the workshare is more than WorkShareInclusionDepth distance")
}
}
distance = int64(wo.NumberU64(common.ZONE_CTX)-prevBlock.NumberU64(common.ZONE_CTX)) + uncleDist - 1
} else {
distance = int64(wo.NumberU64(common.ZONE_CTX)-parent.NumberU64(common.ZONE_CTX)) - 1
distance, err := chain.WorkShareDistance(wo, ws)
if err != nil {
return big.NewInt(0), err
}
wsEntropy = new(big.Int).Div(wsEntropy, new(big.Int).Exp(big.NewInt(2), big.NewInt(distance), nil))
wsEntropy = new(big.Int).Div(wsEntropy, new(big.Int).Exp(big.NewInt(2), distance, nil))
// Add the entropy into the total entropy once the discount calculation is done
totalWsEntropy.Add(totalWsEntropy, wsEntropy)

Expand Down
4 changes: 4 additions & 0 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ type ChainHeaderReader interface {

// WriteAddressOutpoints writes the address outpoints to the database
WriteAddressOutpoints(outpointsMap map[string]map[string]*types.OutpointAndDenomination) error

// WorkShareDistance calculates the geodesic distance between the
// workshare and the workobject in which that workshare is included.
WorkShareDistance(wo *types.WorkObject, ws *types.WorkObjectHeader) (*big.Int, error)
}

// ChainReader defines a small collection of methods needed to access the local
Expand Down
5 changes: 5 additions & 0 deletions consensus/progpow/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ func (progpow *Progpow) VerifyUncles(chain consensus.ChainReader, block *types.W
return err
}

_, err = chain.WorkShareDistance(block, uncle)
if err != nil {
return err
}

// Verify the block's difficulty based on its timestamp and parent's difficulty
// difficulty adjustment can only be checked in zone
if nodeCtx == common.ZONE_CTX {
Expand Down
39 changes: 4 additions & 35 deletions consensus/progpow/poem.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,47 +194,16 @@ func (progpow *Progpow) WorkShareLogS(chain consensus.ChainHeaderReader, wo *typ
} else {
wsEntropy = new(big.Int).Set(progpow.IntrinsicLogS(powHash))
}
var distance int64 = 0
// Discount 2) applies to all shares regardless of the weight
// a workshare cannot reference another workshare, it has to be either a block or an uncle
// check that the parent hash referenced by the workshare is an uncle or a canonical block
// then if its an uncle, traverse back until we hit a canonical block, other wise, use that
// as a reference to calculate the distance
parent := chain.GetBlockByHash(ws.ParentHash())
if parent == nil {
return big.NewInt(0), errors.New("error finding the parent of the work share")
}
// checking if the parent is an uncle
canonicalBlockForParentNum := chain.GetHeaderByNumber(parent.NumberU64(common.ZONE_CTX))
if canonicalBlockForParentNum == nil {
return big.NewInt(0), errors.New("cannot find a canonical block for the parent number")
}
// If this check passes, the parent block is not a canonical block, we have to trace back
if canonicalBlockForParentNum.Hash() != parent.Hash() {
var prevBlock *types.WorkObject
var uncleDist int64 = 0
for {
uncleDist++
prevBlock = chain.GetBlockByHash(parent.Hash())
if prevBlock == nil {
return big.NewInt(0), errors.New("cannot find a parent block of an uncle")
}
blockForPrevBlockNumber := chain.GetHeaderByNumber(prevBlock.NumberU64(common.ZONE_CTX))
if blockForPrevBlockNumber == nil {
return big.NewInt(0), errors.New("cannot find a canonical block for the uncle block number")
}
if prevBlock.Hash() == blockForPrevBlockNumber.Hash() {
break
}
if uncleDist > int64(params.WorkSharesInclusionDepth) {
return big.NewInt(0), errors.New("uncle referenced by the workshare is more than WorkShareInclusionDepth distance")
}
}
distance = int64(wo.NumberU64(common.ZONE_CTX)-prevBlock.NumberU64(common.ZONE_CTX)) + uncleDist - 1
} else {
distance = int64(wo.NumberU64(common.ZONE_CTX)-parent.NumberU64(common.ZONE_CTX)) - 1
distance, err := chain.WorkShareDistance(wo, ws)
if err != nil {
return big.NewInt(0), err
}
wsEntropy = new(big.Int).Div(wsEntropy, new(big.Int).Exp(big.NewInt(2), big.NewInt(distance), nil))
wsEntropy = new(big.Int).Div(wsEntropy, new(big.Int).Exp(big.NewInt(2), distance, nil))
// Add the entropy into the total entropy once the discount calculation is done
totalWsEntropy.Add(totalWsEntropy, wsEntropy)
}
Expand Down
6 changes: 6 additions & 0 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,12 @@ func (c *Core) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCano
return c.sl.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
}

// WorkShareDistance calculates the geodesic distance between the
// workshare and the workobject in which that workshare is included.
func (c *Core) WorkShareDistance(wo *types.WorkObject, ws *types.WorkObjectHeader) (*big.Int, error) {
return c.sl.hc.WorkShareDistance(wo, ws)
}

// Genesis retrieves the chain's genesis block.
func (c *Core) Genesis() *types.WorkObject {
return c.GetBlockByHash(c.sl.hc.genesisHeader.Hash())
Expand Down
51 changes: 51 additions & 0 deletions core/headerchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,57 @@ func (hc *HeaderChain) findCommonAncestor(header *types.WorkObject) *types.WorkO
}

}
func (hc *HeaderChain) WorkShareDistance(wo *types.WorkObject, ws *types.WorkObjectHeader) (*big.Int, error) {
current := wo
// Create a list of ancestor blocks to the work object
ancestors := make(map[common.Hash]struct{})
for i := 0; i < params.WorkSharesInclusionDepth; i++ {
parent := hc.GetBlockByHash(current.ParentHash(common.ZONE_CTX))
if parent == nil {
return big.NewInt(0), errors.New("error finding the parent")
}
ancestors[parent.Hash()] = struct{}{}
current = parent
}

// checks if the wo is in the ancestors list
checkInAncestorsList := func(wo *types.WorkObject) bool {
if len(ancestors) > 0 {
_, exists := ancestors[wo.Hash()]
return exists
} else {
return false
}
}

var distance int64 = 0
// trace back from the workshare and check if any of the parents exist in
// the ancestors list
parentHash := ws.ParentHash()
for {
parent := hc.GetBlockByHash(parentHash)
if parent == nil {
return big.NewInt(0), errors.New("error finding the parent")
}
if checkInAncestorsList(parent) {
distance += int64(wo.NumberU64(common.ZONE_CTX) - parent.NumberU64(common.ZONE_CTX) - 1)
break
}
distance++
// If distance is greater than the WorkSharesInclusionDepth, exit the for loop
if distance > int64(params.WorkSharesInclusionDepth) {
break
}
parentHash = parent.ParentHash(common.ZONE_CTX)
}

// If distance is greater than the WorkSharesInclusionDepth, reject the workshare
if distance > int64(params.WorkSharesInclusionDepth) {
return big.NewInt(0), errors.New("workshare is at distance more than WorkSharesInclusionDepth")
}

return big.NewInt(distance), nil
}

func (hc *HeaderChain) AddPendingEtxs(pEtxs types.PendingEtxs) error {
if !pEtxs.IsValid(trie.NewStackTrie(nil)) && !hc.IsGenesisHash(pEtxs.Header.Hash()) {
Expand Down
4 changes: 4 additions & 0 deletions core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,10 @@ func (w *worker) commitUncle(env *environment, uncle *types.WorkObjectHeader) er
if err != nil {
workShare = true
}
_, err = w.hc.WorkShareDistance(env.wo, uncle)
if err != nil {
return err
}
if !workShare && (env.wo.ParentHash(w.hc.NodeCtx()) == uncle.ParentHash()) {
return errors.New("uncle is sibling")
}
Expand Down
4 changes: 3 additions & 1 deletion internal/quaiapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,12 @@ type Backend interface {
GetMaxTxInWorkShare() uint64
GetExpansionNumber() uint8
SuggestFinalityDepth(ctx context.Context, qiValue *big.Int, correlatedRisk *big.Int) (*big.Int, error)
WorkShareDistance(wo *types.WorkObject, ws *types.WorkObjectHeader) (*big.Int, error)

consensus.ChainHeaderReader

BadHashExistsInChain() bool
IsBlockHashABadHash(hash common.Hash) bool
consensus.ChainHeaderReader

// Validator methods that checks the sanity of the Body
SanityCheckWorkObjectBlockViewBody(wo *types.WorkObject) error
Expand Down
4 changes: 4 additions & 0 deletions quai/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,10 @@ func (b *QuaiAPIBackend) SanityCheckWorkObjectShareViewBody(wo *types.WorkObject
return b.quai.core.SanityCheckWorkObjectShareViewBody(wo)
}

func (b *QuaiAPIBackend) WorkShareDistance(wo *types.WorkObject, ws *types.WorkObjectHeader) (*big.Int, error) {
return b.quai.core.WorkShareDistance(wo, ws)
}

// ///////////////////////////
// /////// P2P ///////////////
// ///////////////////////////
Expand Down

0 comments on commit fdb7fab

Please sign in to comment.