diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 866a75357..8c82895f9 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -526,9 +526,11 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, round int64, miner add return nil, xerrors.Errorf("failed to look up miners sector size: %w", err) } + snum := types.BigDiv(pow.MinerPower, types.NewInt(ssize)) + var winners []sectorbuilder.EPostCandidate for _, c := range candidates { - if types.IsTicketWinner(c.PartialTicket[:], ssize, pow.TotalPower) { + if types.IsTicketWinner(c.PartialTicket[:], ssize, snum.Uint64(), pow.TotalPower) { winners = append(winners, c) } } diff --git a/chain/sync.go b/chain/sync.go index c632c80a0..e594749e9 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -501,7 +501,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("received block was from miner slashed at height %d", slashedAt) } - _, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) + mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) if err != nil { return xerrors.Errorf("failed getting power: %w", err) } @@ -511,8 +511,10 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("failed to get sector size for block miner: %w", err) } + snum := types.BigDiv(mpow, types.NewInt(ssize)) + for _, t := range h.EPostProof.Candidates { - if !types.IsTicketWinner(t.Partial, ssize, tpow) { + if !types.IsTicketWinner(t.Partial, ssize, snum.Uint64(), tpow) { return xerrors.Errorf("miner created a block but was not a winner") } } diff --git a/chain/types/blockheader.go b/chain/types/blockheader.go index 7ba5ef436..e44ca54b4 100644 --- a/chain/types/blockheader.go +++ b/chain/types/blockheader.go @@ -174,9 +174,9 @@ var blocksPerEpoch = NewInt(build.BlocksPerEpoch) const sha256bits = 256 -func IsTicketWinner(partialTicket []byte, ssizeI uint64, totpow BigInt) bool { +func IsTicketWinner(partialTicket []byte, ssizeI uint64, snum uint64, totpow BigInt) bool { ssize := NewInt(ssizeI) - + ssampled := ElectionPostChallengeCount(snum) /* Need to check that (h(vrfout) + 1) / (max(h) + 1) <= e * sectorSize / totalPower @@ -185,23 +185,42 @@ func IsTicketWinner(partialTicket []byte, ssizeI uint64, totpow BigInt) bool { (h(vrfout) + 1) * totalPower <= e * sectorSize * 2^256 in 2^256 space, it is equivalent to: h(vrfout) * totalPower < e * sectorSize * 2^256 + + Because of SectorChallengeRatioDiv sampling for proofs + we need to scale this appropriately. + + Let c = ceil(numSectors/SectorChallengeRatioDiv) + (c is the number of tickets a miner requests) + Accordingly we check + (h(vrfout) + 1) / 2^256 <= e * sectorSize / totalPower * snum / c + or + h(vrfout) * totalPower * c < e * sectorSize * 2^256 * snum */ h := sha256.Sum256(partialTicket) lhs := BigFromBytes(h[:]).Int lhs = lhs.Mul(lhs, totpow.Int) + lhs = lhs.Mul(lhs, new(big.Int).SetUint64(ssampled)) // rhs = sectorSize * 2^256 // rhs = sectorSize << 256 rhs := new(big.Int).Lsh(ssize.Int, sha256bits) - rhs = rhs.Mul(rhs, big.NewInt(build.SectorChallengeRatioDiv)) + rhs = rhs.Mul(rhs, new(big.Int).SetUint64(snum)) rhs = rhs.Mul(rhs, blocksPerEpoch.Int) // h(vrfout) * totalPower < e * sectorSize * 2^256? return lhs.Cmp(rhs) < 0 } +func ElectionPostChallengeCount(sectors uint64) uint64 { + if sectors == 0 { + return 0 + } + // ceil(sectors / build.SectorChallengeRatioDiv) + return (sectors-1)/build.SectorChallengeRatioDiv + 1 +} + func (t *Ticket) Equals(ot *Ticket) bool { return bytes.Equal(t.VRFProof, ot.VRFProof) } diff --git a/lib/sectorbuilder/sectorbuilder.go b/lib/sectorbuilder/sectorbuilder.go index 7a5d744ab..c4fca12d4 100644 --- a/lib/sectorbuilder/sectorbuilder.go +++ b/lib/sectorbuilder/sectorbuilder.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/node/modules/dtypes" ) @@ -623,7 +624,7 @@ func (sb *SectorBuilder) GenerateEPostCandidates(sectorInfo SortedPublicSectorIn return nil, err } - challengeCount := ElectionPostChallengeCount(uint64(len(sectorInfo.Values()))) + challengeCount := types.ElectionPostChallengeCount(uint64(len(sectorInfo.Values()))) proverID := addressToProverID(sb.Miner) return sectorbuilder.GenerateCandidates(sb.ssize, proverID, challengeSeed, challengeCount, privsectors) @@ -674,13 +675,8 @@ func (sb *SectorBuilder) Stop() { close(sb.stopping) } -func ElectionPostChallengeCount(sectors uint64) uint64 { - // ceil(sectors / build.SectorChallengeRatioDiv) - return (sectors + build.SectorChallengeRatioDiv - 1) / build.SectorChallengeRatioDiv -} - func fallbackPostChallengeCount(sectors uint64) uint64 { - challengeCount := ElectionPostChallengeCount(sectors) + challengeCount := types.ElectionPostChallengeCount(sectors) if challengeCount > build.MaxFallbackPostChallengeCount { return build.MaxFallbackPostChallengeCount } diff --git a/lib/sectorbuilder/simple.go b/lib/sectorbuilder/simple.go index 0c9231092..457c4b2e1 100644 --- a/lib/sectorbuilder/simple.go +++ b/lib/sectorbuilder/simple.go @@ -8,6 +8,7 @@ import ( "go.opencensus.io/trace" "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/types" ) func (sb *SectorBuilder) SectorSize() uint64 { @@ -36,7 +37,7 @@ func NewSortedPublicSectorInfo(sectors []sectorbuilder.PublicSectorInfo) SortedP } func VerifyElectionPost(ctx context.Context, sectorSize uint64, sectorInfo SortedPublicSectorInfo, challengeSeed []byte, proof []byte, candidates []EPostCandidate, proverID address.Address) (bool, error) { - challengeCount := ElectionPostChallengeCount(uint64(len(sectorInfo.Values()))) + challengeCount := types.ElectionPostChallengeCount(uint64(len(sectorInfo.Values()))) return verifyPost(ctx, sectorSize, sectorInfo, challengeCount, challengeSeed, proof, candidates, proverID) }