Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Region and Prime blocks using Prime and Region Entropy Threshold #1031

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions common/big.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import "math/big"

// Common big integers often used
var (
Big1 = big.NewInt(1)
Big2 = big.NewInt(2)
Big3 = big.NewInt(3)
Big0 = big.NewInt(0)
Big32 = big.NewInt(32)
Big256 = big.NewInt(256)
Big257 = big.NewInt(257)
Big0 = big.NewInt(0)
Big1 = big.NewInt(1)
Big2 = big.NewInt(2)
Big3 = big.NewInt(3)
Big4 = big.NewInt(4)
Big32 = big.NewInt(32)
Big256 = big.NewInt(256)
Big257 = big.NewInt(257)
Big2e256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
)

func BigBitsToBits(original *big.Int) *big.Int {
Expand All @@ -43,3 +45,10 @@ func BigBitsArrayToBitsArray(original []*big.Int) []*big.Int {

return bitsArray
}

func MaxBigInt(x, y *big.Int) *big.Int {
if x.Cmp(y) > 0 {
return x
}
return y
}
76 changes: 76 additions & 0 deletions consensus/blake3pow/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var (
big8 = big.NewInt(8)
big9 = big.NewInt(9)
big10 = big.NewInt(10)
big20 = big.NewInt(20)
big32 = big.NewInt(32)
bigMinus99 = big.NewInt(-99)
big2e256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) // 2^256
Expand Down Expand Up @@ -280,11 +281,35 @@ func (blake3pow *Blake3pow) verifyHeader(chain consensus.ChainHeaderReader, head
if common.Big0.Cmp(header.ParentDeltaS()) != 0 {
return fmt.Errorf("invalid parent delta s: have %v, want %v", header.ParentDeltaS(), common.Big0)
}
// If parent block is dom, validate the prime difficulty
if nodeCtx == common.REGION_CTX {
primeEntropyThreshold, err := blake3pow.CalcPrimeEntropyThreshold(chain, parent)
if err != nil {
return err
}
if header.PrimeEntropyThreshold(parent.Location().SubIndex()).Cmp(primeEntropyThreshold) != 0 {
return fmt.Errorf("invalid prime difficulty pd: have %v, want %v", header.PrimeEntropyThreshold(parent.Location().SubIndex()), primeEntropyThreshold)
}
}
} else {
parentDeltaS := blake3pow.DeltaLogS(parent)
if parentDeltaS.Cmp(header.ParentDeltaS()) != 0 {
return fmt.Errorf("invalid parent delta s: have %v, want %v", header.ParentDeltaS(), parentDeltaS)
}
if nodeCtx == common.REGION_CTX {
// if parent is not a dom block, no adjustment to the prime or region difficulty will be made
for i := 0; i < common.NumZonesInRegion; i++ {
if header.PrimeEntropyThreshold(i).Cmp(parent.PrimeEntropyThreshold(i)) != 0 {
return fmt.Errorf("invalid prime difficulty pd: have %v, want %v at index %v", header.PrimeEntropyThreshold(i), parent.PrimeEntropyThreshold(i), i)
}
}
}
if nodeCtx == common.ZONE_CTX {
if header.PrimeEntropyThreshold(common.NodeLocation.Zone()).Cmp(parent.PrimeEntropyThreshold(common.NodeLocation.Zone())) != 0 {
return fmt.Errorf("invalid prime difficulty pd: have %v, want %v at index %v", header.PrimeEntropyThreshold(common.NodeLocation.Zone()), parent.PrimeEntropyThreshold(common.NodeLocation.Zone()), common.NodeLocation.Zone())
}
}

}
}

Expand Down Expand Up @@ -376,6 +401,57 @@ func (blake3pow *Blake3pow) CalcDifficulty(chain consensus.ChainHeaderReader, pa
return x
}

// CalcPrimeDifficultyThreshold calculates the difficulty that a block must meet
// to become a region block. This function needs to have a controller so that the
// liveliness of the slices can balance even if the hash rate of the slice varies.
// This will also cause the production of the prime blocks to naturally diverge
// with time reducing the uncle rate. The controller is built to adjust the
// number of zone blocks it takes to produce a prime block. This is done based on
// the prior number of blocks to reach threshold which is than multiplied by the
// current difficulty to establish the threshold. The controller adjust the block
// threshold value and is a simple form of a bang-bang controller which is all
// that is needed to ensure liveliness of the slices in prime overtime. If the
// slice is not sufficiently lively 20 zone blocks are subtracted from the
// threshold. If it is too lively 20 blocks are added to the threshold.
func (blake3pow *Blake3pow) CalcPrimeEntropyThreshold(chain consensus.ChainHeaderReader, parent *types.Header) (*big.Int, error) {
nodeCtx := common.NodeLocation.Context()

if nodeCtx != common.REGION_CTX {
log.Error("Cannot CalcPrimeEntropyThreshold for", "context", nodeCtx)
return nil, errors.New("cannot CalcPrimeEntropyThreshold for non-region context")
}

if parent.Hash() == chain.Config().GenesisHash {
return parent.PrimeEntropyThreshold(parent.Location().SubIndex()), nil
}

// Get the primeTerminus
termini := chain.GetTerminiByHash(parent.ParentHash())
if termini == nil {
return nil, errors.New("termini not found in CalcPrimeEntropyThreshold")
}
primeTerminusHeader := chain.GetHeaderByHash(termini.PrimeTerminiAtIndex(parent.Location().SubIndex()))

log.Info("CalcPrimeEntropyThreshold", "primeTerminusHeader:", primeTerminusHeader.NumberArray(), "Hash", primeTerminusHeader.Hash())
deltaNumber := new(big.Int).Sub(parent.Number(), primeTerminusHeader.Number())
log.Info("CalcPrimeEntropyThreshold", "deltaNumber:", deltaNumber)
target := new(big.Int).Mul(big.NewInt(common.NumRegionsInPrime), params.TimeFactor)
target = new(big.Int).Mul(big.NewInt(common.NumZonesInRegion), target)
log.Info("CalcPrimeEntropyThreshold", "target:", target)

var newThreshold *big.Int
if target.Cmp(deltaNumber) > 0 {
newThreshold = new(big.Int).Add(parent.PrimeEntropyThreshold(parent.Location().Zone()), big20)
} else {
newThreshold = new(big.Int).Sub(parent.PrimeEntropyThreshold(parent.Location().Zone()), big20)
}
newMinThreshold := new(big.Int).Div(target, big2)
newThreshold = new(big.Int).Set(common.MaxBigInt(newThreshold, newMinThreshold))
log.Info("CalcPrimeEntropyThreshold", "newThreshold:", newThreshold)

return newThreshold, nil
}

func (blake3pow *Blake3pow) IsDomCoincident(chain consensus.ChainHeaderReader, header *types.Header) bool {
_, order, err := blake3pow.CalcOrder(header)
if err != nil {
Expand Down
42 changes: 16 additions & 26 deletions consensus/blake3pow/poem.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,31 @@ func (blake3pow *Blake3pow) CalcOrder(header *types.Header) (*big.Int, int, erro

// Get entropy reduction of this header
intrinsicS := blake3pow.IntrinsicLogS(header.Hash())

// This is the updated the threshold calculation based on the zone difficulty threshold
target := new(big.Int).Div(big2e256, header.Difficulty()).Bytes()
target := new(big.Int).Div(common.Big2e256, header.Difficulty()).Bytes()
zoneThresholdS := blake3pow.IntrinsicLogS(common.BytesToHash(target))
timeFactorHierarchyDepthMultiple := new(big.Int).Mul(params.TimeFactor, big.NewInt(common.HierarchyDepth))

// Prime case
primeEntropyThreshold := new(big.Int).Mul(timeFactorHierarchyDepthMultiple, timeFactorHierarchyDepthMultiple)
primeEntropyThreshold = new(big.Int).Mul(primeEntropyThreshold, zoneThresholdS)
primeBlockThreshold := new(big.Int).Quo(primeEntropyThreshold, big.NewInt(2))
primeEntropyThreshold = new(big.Int).Sub(primeEntropyThreshold, primeBlockThreshold)

primeBlockEntropyThresholdAdder, _ := mathutil.BinaryLog(primeBlockThreshold, 8)
primeBlockEntropyThreshold := new(big.Int).Add(zoneThresholdS, big.NewInt(int64(primeBlockEntropyThresholdAdder)))
// PRIME
// Compute the total accumulated entropy since the last prime block
totalDeltaSPrime := new(big.Int).Add(header.ParentDeltaS(common.REGION_CTX), header.ParentDeltaS(common.ZONE_CTX))
totalDeltaSPrime.Add(totalDeltaSPrime, intrinsicS)

totalDeltaS := new(big.Int).Add(header.ParentDeltaS(common.REGION_CTX), header.ParentDeltaS(common.ZONE_CTX))
totalDeltaS.Add(totalDeltaS, intrinsicS)
if intrinsicS.Cmp(primeBlockEntropyThreshold) > 0 && totalDeltaS.Cmp(primeEntropyThreshold) > 0 {
// PrimeEntropyThreshold number of zone blocks times the intrinsic logs of
// the given header determines the prime block
primeEntropyThreshold := new(big.Int).Mul(zoneThresholdS, header.PrimeEntropyThreshold(header.Location().Zone()))
if totalDeltaSPrime.Cmp(primeEntropyThreshold) > 0 {
return intrinsicS, common.PRIME_CTX, nil
}

// Region case
regionEntropyThreshold := new(big.Int).Mul(timeFactorHierarchyDepthMultiple, zoneThresholdS)
regionBlockThreshold := new(big.Int).Quo(regionEntropyThreshold, big.NewInt(2))
regionEntropyThreshold = new(big.Int).Sub(regionEntropyThreshold, regionBlockThreshold)

regionBlockEntropyThresholdAdder, _ := mathutil.BinaryLog(regionBlockThreshold, 8)
regionBlockEntropyThreshold := new(big.Int).Add(zoneThresholdS, big.NewInt(int64(regionBlockEntropyThresholdAdder)))

totalDeltaS = new(big.Int).Add(header.ParentDeltaS(common.ZONE_CTX), intrinsicS)
if intrinsicS.Cmp(regionBlockEntropyThreshold) > 0 && totalDeltaS.Cmp(regionEntropyThreshold) > 0 {
// REGION
// Compute the total accumulated entropy since the last region block
totalDeltaSRegion := new(big.Int).Add(header.ParentDeltaS(common.ZONE_CTX), intrinsicS)
regionEntropyThreshold := new(big.Int).Mul(zoneThresholdS, params.TimeFactor)
regionEntropyThreshold = new(big.Int).Mul(regionEntropyThreshold, big.NewInt(common.NumZonesInRegion))
if totalDeltaSRegion.Cmp(regionEntropyThreshold) > 0 {
return intrinsicS, common.REGION_CTX, nil
}

// Zone case
// ZONE
return intrinsicS, common.ZONE_CTX, nil
}

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

// GetHeaderByHash retrieves a block header from the database by its hash.
GetHeaderByHash(hash common.Hash) *types.Header

// GetTerminiByHash retrieves the termini for a given header hash
GetTerminiByHash(hash common.Hash) *types.Termini
}

// ChainReader defines a small collection of methods needed to access the local
Expand Down Expand Up @@ -128,6 +131,9 @@ type Engine interface {
// that a new block should have.
CalcDifficulty(chain ChainHeaderReader, parent *types.Header) *big.Int

// CalcPrimeEntropyThreshold is the threshold adjustment algorithm for prime blocks per slice
CalcPrimeEntropyThreshold(chain ChainHeaderReader, parent *types.Header) (*big.Int, error)

// IsDomCoincident returns true if this block satisfies the difficulty order
// of a dominant chain. If this node does not have a dominant chain (i.e.
// if this is a prime node), then the function will always return false.
Expand Down
78 changes: 78 additions & 0 deletions consensus/progpow/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ var (
big8 = big.NewInt(8)
big9 = big.NewInt(9)
big10 = big.NewInt(10)
big20 = big.NewInt(20)
big32 = big.NewInt(32)
big100 = big.NewInt(100)
bigMinus99 = big.NewInt(-99)
big2e256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) // 2^256
)
Expand Down Expand Up @@ -280,11 +282,36 @@ func (progpow *Progpow) verifyHeader(chain consensus.ChainHeaderReader, header,
if common.Big0.Cmp(header.ParentDeltaS()) != 0 {
return fmt.Errorf("invalid parent delta s: have %v, want %v", header.ParentDeltaS(), common.Big0)
}
// If parent block is dom, validate the prime difficulty
if nodeCtx == common.REGION_CTX {
primeEntropyThreshold, err := progpow.CalcPrimeEntropyThreshold(chain, parent)
if err != nil {
return err
}
if header.PrimeEntropyThreshold(parent.Location().SubIndex()).Cmp(primeEntropyThreshold) != 0 {
return fmt.Errorf("invalid prime difficulty pd: have %v, want %v", header.PrimeEntropyThreshold(parent.Location().SubIndex()), primeEntropyThreshold)
}
}
} else {
parentDeltaS := progpow.DeltaLogS(parent)
if parentDeltaS.Cmp(header.ParentDeltaS()) != 0 {
return fmt.Errorf("invalid parent delta s: have %v, want %v", header.ParentDeltaS(), parentDeltaS)
}

if nodeCtx == common.REGION_CTX {
// if parent is not a dom block, no adjustment to the prime or region difficulty will be made
for i := 0; i < common.NumZonesInRegion; i++ {
if header.PrimeEntropyThreshold(i).Cmp(parent.PrimeEntropyThreshold(i)) != 0 {
return fmt.Errorf("invalid prime difficulty pd: have %v, want %v at index %v", header.PrimeEntropyThreshold(i), parent.PrimeEntropyThreshold(i), i)
}
}
}
if nodeCtx == common.ZONE_CTX {
if header.PrimeEntropyThreshold(common.NodeLocation.Zone()).Cmp(parent.PrimeEntropyThreshold(common.NodeLocation.Zone())) != 0 {
return fmt.Errorf("invalid prime difficulty pd: have %v, want %v at index %v", header.PrimeEntropyThreshold(common.NodeLocation.Zone()), parent.PrimeEntropyThreshold(common.NodeLocation.Zone()), common.NodeLocation.Zone())
}
}

}
}
if nodeCtx == common.ZONE_CTX {
Expand Down Expand Up @@ -320,6 +347,57 @@ func (progpow *Progpow) verifyHeader(chain consensus.ChainHeaderReader, header,
return nil
}

// CalcPrimeDifficultyThreshold calculates the difficulty that a block must meet
// to become a region block. This function needs to have a controller so that the
// liveliness of the slices can balance even if the hash rate of the slice varies.
// This will also cause the production of the prime blocks to naturally diverge
// with time reducing the uncle rate. The controller is built to adjust the
// number of zone blocks it takes to produce a prime block. This is done based on
// the prior number of blocks to reach threshold which is than multiplied by the
// current difficulty to establish the threshold. The controller adjust the block
// threshold value and is a simple form of a bang-bang controller which is all
// that is needed to ensure liveliness of the slices in prime overtime. If the
// slice is not sufficiently lively 20 zone blocks are subtracted from the
// threshold. If it is too lively 20 blocks are added to the threshold.
func (progpow *Progpow) CalcPrimeEntropyThreshold(chain consensus.ChainHeaderReader, parent *types.Header) (*big.Int, error) {
nodeCtx := common.NodeLocation.Context()

if nodeCtx != common.REGION_CTX {
log.Error("Cannot CalcPrimeEntropyThreshold for", "context", nodeCtx)
return nil, errors.New("cannot CalcPrimeEntropyThreshold for non-region context")
}

if parent.Hash() == chain.Config().GenesisHash {
return parent.PrimeEntropyThreshold(parent.Location().SubIndex()), nil
}

// Get the primeTerminus
termini := chain.GetTerminiByHash(parent.ParentHash())
if termini == nil {
return nil, errors.New("termini not found in CalcPrimeEntropyThreshold")
}
primeTerminusHeader := chain.GetHeaderByHash(termini.PrimeTerminiAtIndex(parent.Location().SubIndex()))

log.Info("CalcPrimeEntropyThreshold", "primeTerminusHeader:", primeTerminusHeader.NumberArray(), "Hash", primeTerminusHeader.Hash())
deltaNumber := new(big.Int).Sub(parent.Number(), primeTerminusHeader.Number())
log.Info("CalcPrimeEntropyThreshold", "deltaNumber:", deltaNumber)
target := new(big.Int).Mul(big.NewInt(common.NumRegionsInPrime), params.TimeFactor)
target = new(big.Int).Mul(big.NewInt(common.NumZonesInRegion), target)
log.Info("CalcPrimeEntropyThreshold", "target:", target)

var newThreshold *big.Int
if target.Cmp(deltaNumber) > 0 {
newThreshold = new(big.Int).Add(parent.PrimeEntropyThreshold(parent.Location().Zone()), big20)
} else {
newThreshold = new(big.Int).Sub(parent.PrimeEntropyThreshold(parent.Location().Zone()), big20)
}
newMinThreshold := new(big.Int).Div(target, big2)
newThreshold = new(big.Int).Set(common.MaxBigInt(newThreshold, newMinThreshold))
log.Info("CalcPrimeEntropyThreshold", "newThreshold:", newThreshold)

return newThreshold, nil
}

// CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
Expand Down
40 changes: 15 additions & 25 deletions consensus/progpow/poem.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,30 @@ func (progpow *Progpow) CalcOrder(header *types.Header) (*big.Int, int, error) {

// Get entropy reduction of this header
intrinsicS := progpow.IntrinsicLogS(powHash)

// This is the updated the threshold calculation based on the zone difficulty threshold
target := new(big.Int).Div(big2e256, header.Difficulty()).Bytes()
target := new(big.Int).Div(common.Big2e256, header.Difficulty()).Bytes()
zoneThresholdS := progpow.IntrinsicLogS(common.BytesToHash(target))
timeFactorHierarchyDepthMultiple := new(big.Int).Mul(params.TimeFactor, big.NewInt(common.HierarchyDepth))

// Prime case
primeEntropyThreshold := new(big.Int).Mul(timeFactorHierarchyDepthMultiple, timeFactorHierarchyDepthMultiple)
primeEntropyThreshold = new(big.Int).Mul(primeEntropyThreshold, zoneThresholdS)
primeBlockThreshold := new(big.Int).Quo(primeEntropyThreshold, big.NewInt(2))
primeEntropyThreshold = new(big.Int).Sub(primeEntropyThreshold, primeBlockThreshold)

primeBlockEntropyThresholdAdder, _ := mathutil.BinaryLog(primeBlockThreshold, 8)
primeBlockEntropyThreshold := new(big.Int).Add(zoneThresholdS, big.NewInt(int64(primeBlockEntropyThresholdAdder)))
// PRIME
// Compute the total accumulated entropy since the last prime block
totalDeltaSPrime := new(big.Int).Add(header.ParentDeltaS(common.REGION_CTX), header.ParentDeltaS(common.ZONE_CTX))
totalDeltaSPrime.Add(totalDeltaSPrime, intrinsicS)

totalDeltaS := new(big.Int).Add(header.ParentDeltaS(common.REGION_CTX), header.ParentDeltaS(common.ZONE_CTX))
totalDeltaS.Add(totalDeltaS, intrinsicS)
if intrinsicS.Cmp(primeBlockEntropyThreshold) > 0 && totalDeltaS.Cmp(primeEntropyThreshold) > 0 {
// PrimeEntropyThreshold number of zone blocks times the intrinsic logs of the given header determines the prime block
primeEntropyThreshold := new(big.Int).Mul(zoneThresholdS, header.PrimeEntropyThreshold(header.Location().Zone()))
if totalDeltaSPrime.Cmp(primeEntropyThreshold) > 0 {
return intrinsicS, common.PRIME_CTX, nil
}

// Region case
regionEntropyThreshold := new(big.Int).Mul(timeFactorHierarchyDepthMultiple, zoneThresholdS)
regionBlockThreshold := new(big.Int).Quo(regionEntropyThreshold, big.NewInt(2))
regionEntropyThreshold = new(big.Int).Sub(regionEntropyThreshold, regionBlockThreshold)

regionBlockEntropyThresholdAdder, _ := mathutil.BinaryLog(regionBlockThreshold, 8)
regionBlockEntropyThreshold := new(big.Int).Add(zoneThresholdS, big.NewInt(int64(regionBlockEntropyThresholdAdder)))

totalDeltaS = new(big.Int).Add(header.ParentDeltaS(common.ZONE_CTX), intrinsicS)
if intrinsicS.Cmp(regionBlockEntropyThreshold) > 0 && totalDeltaS.Cmp(regionEntropyThreshold) > 0 {
// REGION
// Compute the total accumulated entropy since the last region block
totalDeltaSRegion := new(big.Int).Add(header.ParentDeltaS(common.ZONE_CTX), intrinsicS)
regionEntropyThreshold := new(big.Int).Mul(zoneThresholdS, params.TimeFactor)
regionEntropyThreshold = new(big.Int).Mul(regionEntropyThreshold, big.NewInt(common.NumZonesInRegion))
if totalDeltaSRegion.Cmp(regionEntropyThreshold) > 0 {
return intrinsicS, common.REGION_CTX, nil
}

// ZONE
return intrinsicS, common.ZONE_CTX, nil
}

Expand Down
1 change: 1 addition & 0 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,4 @@ func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header
func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil }
func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil }
func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil }
func (cr *fakeChainReader) GetTerminiByHash(hash common.Hash) *types.Termini { return nil }
4 changes: 2 additions & 2 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func (c *Core) SubscribePendingEtxsRollup(ch chan<- types.PendingEtxsRollup) eve
return c.sl.SubscribePendingEtxsRollup(ch)
}

func (c *Core) GenerateRecoveryPendingHeader(pendingHeader *types.Header, checkpointHashes []common.Hash) error {
func (c *Core) GenerateRecoveryPendingHeader(pendingHeader *types.Header, checkpointHashes types.Termini) error {
return c.sl.GenerateRecoveryPendingHeader(pendingHeader, checkpointHashes)
}

Expand Down Expand Up @@ -498,7 +498,7 @@ func (c *Core) GetBodyRLP(hash common.Hash) rlp.RawValue {
}

// GetTerminiByHash retrieves the termini stored for a given header hash
func (c *Core) GetTerminiByHash(hash common.Hash) []common.Hash {
func (c *Core) GetTerminiByHash(hash common.Hash) *types.Termini {
return c.sl.hc.GetTerminiByHash(hash)
}

Expand Down
Loading