diff --git a/consensus/blake3pow/consensus.go b/consensus/blake3pow/consensus.go index 64f04234ba..1364a2c866 100644 --- a/consensus/blake3pow/consensus.go +++ b/consensus/blake3pow/consensus.go @@ -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 { diff --git a/consensus/blake3pow/poem.go b/consensus/blake3pow/poem.go index 3bd250bfcf..0dea95f5ee 100644 --- a/consensus/blake3pow/poem.go +++ b/consensus/blake3pow/poem.go @@ -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) diff --git a/consensus/consensus.go b/consensus/consensus.go index 7877eda6b3..1c7aff72fc 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -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 diff --git a/consensus/progpow/consensus.go b/consensus/progpow/consensus.go index b012e092c8..27b9fb3e83 100644 --- a/consensus/progpow/consensus.go +++ b/consensus/progpow/consensus.go @@ -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 { diff --git a/consensus/progpow/poem.go b/consensus/progpow/poem.go index 202c389053..0b06cca8a9 100644 --- a/consensus/progpow/poem.go +++ b/consensus/progpow/poem.go @@ -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) } diff --git a/core/core.go b/core/core.go index 498bca2223..c044089db5 100644 --- a/core/core.go +++ b/core/core.go @@ -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()) diff --git a/core/headerchain.go b/core/headerchain.go index 4b49739c34..ccbd0eca75 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -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()) { diff --git a/core/worker.go b/core/worker.go index 0e8dd6cdbd..52e4c91dda 100644 --- a/core/worker.go +++ b/core/worker.go @@ -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") } diff --git a/internal/quaiapi/backend.go b/internal/quaiapi/backend.go index 92d97529c1..3550964ed6 100644 --- a/internal/quaiapi/backend.go +++ b/internal/quaiapi/backend.go @@ -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 diff --git a/quai/api_backend.go b/quai/api_backend.go index faae199886..f10f0a25d1 100644 --- a/quai/api_backend.go +++ b/quai/api_backend.go @@ -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 /////////////// // ///////////////////////////