diff --git a/common/big.go b/common/big.go index 5be6b019d6..8406beccf1 100644 --- a/common/big.go +++ b/common/big.go @@ -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) + Big1 = big.NewInt(1) + Big2 = big.NewInt(2) + Big3 = big.NewInt(3) + Big4 = big.NewInt(4) + Big0 = big.NewInt(0) + 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 { @@ -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 +} diff --git a/consensus/blake3pow/consensus.go b/consensus/blake3pow/consensus.go index 10de5f466f..8d461a35fd 100644 --- a/consensus/blake3pow/consensus.go +++ b/consensus/blake3pow/consensus.go @@ -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 @@ -280,11 +281,47 @@ 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 region and prime difficulty + if nodeCtx == common.ZONE_CTX { + regionDifficulty, err := blake3pow.CalcRegionEntropyThreshold(chain, parent) + if err != nil { + return err + } + if header.RegionEntropyThreshold().Cmp(regionDifficulty) != 0 { + return fmt.Errorf("invalid region difficulty dom block rd: have %v, want %v", header.RegionEntropyThreshold(), regionDifficulty) + } + } + 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.RegionEntropyThreshold().Cmp(parent.RegionEntropyThreshold()) != 0 { + return fmt.Errorf("invalid region difficulty rd: have %v, want %v", header.RegionEntropyThreshold(), parent.RegionEntropyThreshold()) + } + 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()) + } + } + } } @@ -376,6 +413,82 @@ func (blake3pow *Blake3pow) CalcDifficulty(chain consensus.ChainHeaderReader, pa return x } +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) CalcRegionEntropyThreshold(chain consensus.ChainHeaderReader, parent *types.Header) (*big.Int, error) { + nodeCtx := common.NodeLocation.Context() + + if nodeCtx != common.ZONE_CTX { + log.Error("Cannot CalcRegionEntropyThreshold for", "context", nodeCtx) + return nil, errors.New("cannot CalcRegionEntropyThreshold for non-zone context") + } + + if parent.Hash() == chain.Config().GenesisHash { + return parent.RegionEntropyThreshold(), nil + } + + // Get the primeTerminus + termini := chain.GetTerminiByHash(parent.ParentHash()) + if termini == nil { + return nil, errors.New("termini not found in CalcRegionEntropyThreshold") + } + regionTerminusHeader := chain.GetHeaderByHash(termini.DomTerminus()) + + deltaNumber := new(big.Int).Sub(parent.Number(), regionTerminusHeader.Number()) + target := new(big.Int).Mul(big.NewInt(common.NumZonesInRegion), params.TimeFactor) + + var newThreshold *big.Int + if target.Cmp(deltaNumber) > 0 { + newThreshold = new(big.Int).Add(parent.RegionEntropyThreshold(), big2) + } else { + newThreshold = new(big.Int).Sub(parent.RegionEntropyThreshold(), big2) + } + log.Info("CalcRegionEntropyThreshold", "newThreshold:", newThreshold) + + newMinThreshold := new(big.Int).Div(target, big2) + newThreshold = new(big.Int).Set(common.MaxBigInt(newThreshold, newMinThreshold)) + log.Info("CalcRegionEntropyThreshold", "newThreshold:", newThreshold) + + return newThreshold, nil +} + func (blake3pow *Blake3pow) IsDomCoincident(chain consensus.ChainHeaderReader, header *types.Header) bool { _, order, err := blake3pow.CalcOrder(header) if err != nil { diff --git a/consensus/blake3pow/poem.go b/consensus/blake3pow/poem.go index 46203a819c..f8975a323c 100644 --- a/consensus/blake3pow/poem.go +++ b/consensus/blake3pow/poem.go @@ -1,17 +1,18 @@ package blake3pow import ( + "fmt" "math/big" "github.com/dominant-strategies/go-quai/common" "github.com/dominant-strategies/go-quai/core/types" - "github.com/dominant-strategies/go-quai/params" "modernc.org/mathutil" ) // CalcOrder returns the order of the block within the hierarchy of chains func (blake3pow *Blake3pow) CalcOrder(header *types.Header) (*big.Int, int, error) { if header.NumberU64() == 0 { + fmt.Println("Header number is zero, so its a zone block") return common.Big0, common.PRIME_CTX, nil } @@ -23,41 +24,30 @@ 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 := new(big.Int).Mul(zoneThresholdS, header.PrimeEntropyThreshold(header.Location().Zone())) + if totalDeltaSPrime.Cmp(primeEntropyThreshold) > 0 { + fmt.Println("prime block because", common.BigBitsToBits(totalDeltaSPrime), ">", common.BigBitsToBits(primeEntropyThreshold)) 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, header.RegionEntropyThreshold()) + if totalDeltaSRegion.Cmp(regionEntropyThreshold) > 0 { + fmt.Println("region block because", common.BigBitsToBits(totalDeltaSRegion), ">", common.BigBitsToBits(regionEntropyThreshold)) return intrinsicS, common.REGION_CTX, nil } - // Zone case + // ZONE return intrinsicS, common.ZONE_CTX, nil } diff --git a/consensus/consensus.go b/consensus/consensus.go index adb731c2b2..ae2795f893 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -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 @@ -128,6 +131,12 @@ type Engine interface { // that a new block should have. CalcDifficulty(chain ChainHeaderReader, parent *types.Header) *big.Int + // CalcRegionEntropyThreshold is the threshold adjustment algorithm for Region blocks per slice + CalcRegionEntropyThreshold(chain ChainHeaderReader, parent *types.Header) (*big.Int, error) + + // 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. diff --git a/consensus/progpow/consensus.go b/consensus/progpow/consensus.go index 3c3d95c118..438718aeea 100644 --- a/consensus/progpow/consensus.go +++ b/consensus/progpow/consensus.go @@ -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 ) @@ -280,11 +282,48 @@ 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 region and prime difficulty + if nodeCtx == common.ZONE_CTX { + regionDifficulty, err := progpow.CalcRegionEntropyThreshold(chain, parent) + if err != nil { + return err + } + if header.RegionEntropyThreshold().Cmp(regionDifficulty) != 0 { + return fmt.Errorf("invalid region difficulty dom block rd: have %v, want %v", header.RegionEntropyThreshold(), regionDifficulty) + } + } + 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.RegionEntropyThreshold().Cmp(parent.RegionEntropyThreshold()) != 0 { + return fmt.Errorf("invalid region difficulty rd: have %v, want %v", header.RegionEntropyThreshold(), parent.RegionEntropyThreshold()) + } + 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 { @@ -319,6 +358,81 @@ func (progpow *Progpow) verifyHeader(chain consensus.ChainHeaderReader, header, } return nil } +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 +} + +func (progpow *Progpow) CalcRegionEntropyThreshold(chain consensus.ChainHeaderReader, parent *types.Header) (*big.Int, error) { + nodeCtx := common.NodeLocation.Context() + + if nodeCtx != common.ZONE_CTX { + log.Error("Cannot CalcRegionEntropyThreshold for", "context", nodeCtx) + return nil, errors.New("cannot CalcRegionEntropyThreshold for non-zone context") + } + + if parent.Hash() == chain.Config().GenesisHash { + return parent.RegionEntropyThreshold(), nil + } + + // Get the primeTerminus + termini := chain.GetTerminiByHash(parent.ParentHash()) + if termini == nil { + return nil, errors.New("termini not found in CalcRegionEntropyThreshold") + } + regionTerminusHeader := chain.GetHeaderByHash(termini.DomTerminus()) + + deltaNumber := new(big.Int).Sub(parent.Number(), regionTerminusHeader.Number()) + target := new(big.Int).Mul(big.NewInt(common.NumZonesInRegion), params.TimeFactor) + + var newThreshold *big.Int + if target.Cmp(deltaNumber) > 0 { + newThreshold = new(big.Int).Add(parent.RegionEntropyThreshold(), big2) + } else { + newThreshold = new(big.Int).Sub(parent.RegionEntropyThreshold(), big2) + } + log.Info("CalcRegionEntropyThreshold", "newThreshold:", newThreshold) + + newMinThreshold := new(big.Int).Div(target, big2) + newThreshold = new(big.Int).Set(common.MaxBigInt(newThreshold, newMinThreshold)) + log.Info("CalcRegionEntropyThreshold", "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 diff --git a/consensus/progpow/poem.go b/consensus/progpow/poem.go index 0cafffff79..8a2489eabe 100644 --- a/consensus/progpow/poem.go +++ b/consensus/progpow/poem.go @@ -1,11 +1,11 @@ package progpow import ( + "fmt" "math/big" "github.com/dominant-strategies/go-quai/common" "github.com/dominant-strategies/go-quai/core/types" - "github.com/dominant-strategies/go-quai/params" "modernc.org/mathutil" ) @@ -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 := new(big.Int).Mul(zoneThresholdS, header.PrimeEntropyThreshold(header.Location().Zone())) + if totalDeltaSPrime.Cmp(primeEntropyThreshold) > 0 { + fmt.Println("prime block because", common.BigBitsToBits(totalDeltaSPrime), ">", common.BigBitsToBits(primeEntropyThreshold)) 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, header.RegionEntropyThreshold()) + if totalDeltaSRegion.Cmp(regionEntropyThreshold) > 0 { + fmt.Println("region block because", common.BigBitsToBits(totalDeltaSRegion), ">", common.BigBitsToBits(regionEntropyThreshold)) return intrinsicS, common.REGION_CTX, nil } + // ZONE return intrinsicS, common.ZONE_CTX, nil } diff --git a/core/chain_makers.go b/core/chain_makers.go index 5429487d9e..868746985a 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -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 } diff --git a/core/genesis.go b/core/genesis.go index 96b228d173..12e8f54c55 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -37,12 +37,19 @@ import ( "github.com/dominant-strategies/go-quai/log" "github.com/dominant-strategies/go-quai/params" "github.com/dominant-strategies/go-quai/trie" + "modernc.org/mathutil" ) //go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go //go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go -var errGenesisNoConfig = errors.New("genesis has no chain configuration") +var ( + errGenesisNoConfig = errors.New("genesis has no chain configuration") +) + +const ( + mantBits = 64 +) // Genesis specifies the header fields, state of a genesis block. It also defines hard // fork switch-over blocks through the chain configuration. @@ -278,9 +285,28 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { head.SetParentHash(common.Hash{}, i) } + initPrimeThreshold := new(big.Int).Mul(params.TimeFactor, big.NewInt(common.NumRegionsInPrime)) + initPrimeThreshold = new(big.Int).Mul(initPrimeThreshold, big.NewInt(common.NumZonesInRegion)) + initPrimeThreshold = new(big.Int).Mul(initPrimeThreshold, params.TimeFactor) + for i := 0; i < common.NumZonesInRegion; i++ { + head.SetPrimeEntropyThreshold(initPrimeThreshold, i) + } + initRegionThreshold := new(big.Int).Mul(params.TimeFactor, big.NewInt(common.NumZonesInRegion)) + head.SetRegionEntropyThreshold(initRegionThreshold) + return types.NewBlock(head, nil, nil, nil, nil, nil, trie.NewStackTrie(nil)) } +// IntrinsicLogS returns the logarithm of the intrinsic entropy reduction of a PoW hash +func (g *Genesis) IntrinsicLogS(powHash common.Hash) *big.Int { + x := new(big.Int).SetBytes(powHash.Bytes()) + d := new(big.Int).Div(common.Big2e256, x) + c, m := mathutil.BinaryLog(d, mantBits) + bigBits := new(big.Int).Mul(big.NewInt(int64(c)), new(big.Int).Exp(big.NewInt(2), big.NewInt(mantBits), nil)) + bigBits = new(big.Int).Add(bigBits, m) + return bigBits +} + // Commit writes the block and state of a genesis specification to the database. // The block is committed as the canonical head block. func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { diff --git a/core/headerchain.go b/core/headerchain.go index cd99511255..cedea6d0ee 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -278,7 +278,7 @@ func (hc *HeaderChain) collectInclusiveEtxRollup(b *types.Block) (types.Transact // Append func (hc *HeaderChain) Append(batch ethdb.Batch, block *types.Block, newInboundEtxs types.Transactions) error { nodeCtx := common.NodeLocation.Context() - log.Debug("HeaderChain Append:", "Block information: Hash:", block.Hash(), "block header hash:", block.Header().Hash(), "Number:", block.NumberU64(), "Location:", block.Header().Location, "Parent:", block.ParentHash()) + log.Debug("HeaderChain Append:", "Block information: Hash:", block.Hash(), "block header hash:", block.Header().Hash(), "Number:", block.NumberU64(), "Location:", block.Header().Location(), "Parent:", block.ParentHash()) err := hc.engine.VerifyHeader(hc, block.Header()) if err != nil { diff --git a/core/slice.go b/core/slice.go index 44cfebc1bd..4052116b2d 100644 --- a/core/slice.go +++ b/core/slice.go @@ -435,7 +435,7 @@ func (sl *Slice) pcrc(batch ethdb.Batch, header *types.Header, domTerminus commo nodeCtx := common.NodeLocation.Context() location := header.Location() - log.Debug("PCRC:", "Parent Hash:", header.ParentHash(), "Number", header.Number, "Location:", header.Location()) + log.Debug("PCRC:", "Parent Hash:", header.ParentHash(), "Number", header.Number(), "Location:", header.Location()) termini := sl.hc.GetTerminiByHash(header.ParentHash()) if !termini.IsValid() { @@ -451,8 +451,11 @@ func (sl *Slice) pcrc(batch ethdb.Batch, header *types.Header, domTerminus commo // Set the terminus if nodeCtx == common.PRIME_CTX || domOrigin { newTermini.SetDomTerminus(header.Hash()) - } else { - newTermini.SetDomTerminus(termini.DomTerminus()) + } + + // Set the prime termini + if nodeCtx == common.REGION_CTX && domOrigin { + newTermini.SetPrimeTerminiAtIndex(header.Hash(), location.SubIndex()) } // Check for a graph cyclic reference @@ -470,6 +473,8 @@ func (sl *Slice) pcrc(batch ethdb.Batch, header *types.Header, domTerminus commo return common.Hash{}, newTermini, nil } + fmt.Println("Prime Termini", "Hash", header.Hash(), "Termini", newTermini.PrimeTermini()) + return termini.SubTerminiAtIndex(location.SubIndex()), newTermini, nil } @@ -559,19 +564,27 @@ func (sl *Slice) computePendingHeader(localPendingHeaderWithTermini types.Pendin var cachedPendingHeaderWithTermini types.PendingHeader hash := localPendingHeaderWithTermini.Termini().DomTerminus() cachedPendingHeaderWithTermini, exists := sl.readPhCache(hash) - log.Debug("computePendingHeader:", "hash:", hash, "pendingHeader:", cachedPendingHeaderWithTermini, "termini:", cachedPendingHeaderWithTermini.Termini) var newPh *types.Header + log.Info("computePendingHeader:", "primeEntropyThreshold:", localPendingHeaderWithTermini.Header().PrimeEntropyThresholdArray(), "regionEntropyThreshold:", localPendingHeaderWithTermini.Header().RegionEntropyThreshold()) if exists { + log.Info("computePendingHeader:", "primeEntropyThreshold:", cachedPendingHeaderWithTermini.Header().PrimeEntropyThresholdArray(), "regionEntropyThreshold:", cachedPendingHeaderWithTermini.Header().RegionEntropyThreshold()) + newPh = sl.combinePendingHeader(localPendingHeaderWithTermini.Header(), cachedPendingHeaderWithTermini.Header(), nodeCtx, true) + + log.Info("computePendingHeader:", "primeEntropyThreshold:", newPh.PrimeEntropyThresholdArray(), "regionEntropyThreshold:", newPh.RegionEntropyThreshold()) return types.NewPendingHeader(newPh, localPendingHeaderWithTermini.Termini()) } else { + log.Info("computePendingHeader:", "primeEntropyThreshold:", domPendingHeader.PrimeEntropyThresholdArray(), "regionEntropyThreshold:", domPendingHeader.RegionEntropyThreshold()) + if domOrigin { newPh = sl.combinePendingHeader(localPendingHeaderWithTermini.Header(), domPendingHeader, nodeCtx, true) + log.Info("computePendingHeader:", "primeEntropyThreshold:", newPh.PrimeEntropyThresholdArray(), "regionEntropyThreshold:", newPh.RegionEntropyThreshold()) return types.NewPendingHeader(newPh, localPendingHeaderWithTermini.Termini()) } return localPendingHeaderWithTermini } + } // updatePhCacheFromDom combines the recieved pending header with the pending header stored locally at a given terminus for specified context @@ -677,6 +690,9 @@ func (sl *Slice) init(genesis *Genesis) error { for i := 0; i < len(genesisTermini.SubTermini()); i++ { genesisTermini.SetSubTerminiAtIndex(genesisHash, i) } + for i := 0; i < len(genesisTermini.PrimeTermini()); i++ { + genesisTermini.SetPrimeTerminiAtIndex(genesisHash, i) + } rawdb.WriteTermini(sl.sliceDb, genesisHash, genesisTermini) rawdb.WriteManifest(sl.sliceDb, genesisHash, types.BlockManifest{genesisHash}) @@ -850,7 +866,6 @@ func (sl *Slice) NewGenesisPendingHeader(domPendingHeader *types.Header) { domPendingHeader = sl.combinePendingHeader(localPendingHeader, domPendingHeader, nodeCtx, true) domPendingHeader.SetLocation(common.NodeLocation) } - if nodeCtx != common.ZONE_CTX { for _, client := range sl.subClients { if client != nil { @@ -866,6 +881,9 @@ func (sl *Slice) NewGenesisPendingHeader(domPendingHeader *types.Header) { for i := 0; i < len(genesisTermini.SubTermini()); i++ { genesisTermini.SetSubTerminiAtIndex(genesisHash, i) } + for i := 0; i < len(genesisTermini.PrimeTermini()); i++ { + genesisTermini.SetPrimeTerminiAtIndex(genesisHash, i) + } if sl.hc.Empty() { sl.phCache.Add(sl.config.GenesisHash, types.NewPendingHeader(domPendingHeader, genesisTermini)) } diff --git a/core/worker.go b/core/worker.go index c9cf74f730..a93b022c32 100644 --- a/core/worker.go +++ b/core/worker.go @@ -764,6 +764,33 @@ func (w *worker) prepareWork(genParams *generateParams, block *types.Block) (*en header.SetParentDeltaS(w.engine.DeltaLogS(parent.Header()), nodeCtx) } } + + if nodeCtx == common.REGION_CTX { + for i := 0; i < common.NumZonesInRegion; i++ { + header.SetPrimeEntropyThreshold(parent.Header().PrimeEntropyThreshold(i), i) + } + } + + if nodeCtx == common.REGION_CTX && order == common.PRIME_CTX { + primeEntropyThreshold, err := w.engine.CalcPrimeEntropyThreshold(w.hc, parent.Header()) + if err != nil { + return nil, err + } + header.SetPrimeEntropyThreshold(primeEntropyThreshold, parent.Header().Location().SubIndex()) + } + + if nodeCtx == common.ZONE_CTX { + if order < nodeCtx { + regionEntropyThreshold, err := w.engine.CalcRegionEntropyThreshold(w.hc, parent.Header()) + if err != nil { + return nil, err + } + header.SetRegionEntropyThreshold(regionEntropyThreshold) + } else { + header.SetRegionEntropyThreshold(parent.Header().RegionEntropyThreshold()) + } + } + header.SetParentEntropy(w.engine.TotalLogS(parent.Header())) } else { for i := 0; i < common.NumZonesInRegion; i++ {