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

Emitter accepts adjustments from EmissionDriver contract #484

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
20 changes: 9 additions & 11 deletions gossip/emitter/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ type Emitter struct {
fcIndexer *ancestor.FCIndexer
payloadIndexer *ancestor.PayloadIndexer

intervals EmitIntervals
intervals EmitIntervals
globalConfirmingInterval time.Duration

done chan struct{}
wg sync.WaitGroup
Expand All @@ -78,9 +79,6 @@ type Emitter struct {
emittedEvFile *os.File
busyRate *rate.Gauge

switchToFCIndexer bool
validatorVersions map[idx.ValidatorID]uint64

logger.Periodic
}

Expand All @@ -95,12 +93,12 @@ func NewEmitter(
config.EmitIntervals = config.EmitIntervals.RandomizeEmitTime(r)

return &Emitter{
config: config,
world: world,
originatedTxs: originatedtxs.New(SenderCountBufferSize),
intervals: config.EmitIntervals,
Periodic: logger.Periodic{Instance: logger.New()},
validatorVersions: make(map[idx.ValidatorID]uint64),
config: config,
world: world,
originatedTxs: originatedtxs.New(SenderCountBufferSize),
intervals: config.EmitIntervals,
globalConfirmingInterval: config.EmitIntervals.Confirming,
Periodic: logger.Periodic{Instance: logger.New()},
}
}

Expand Down Expand Up @@ -376,7 +374,7 @@ func (em *Emitter) createEvent(sortedTxs *types.TransactionsByPriceAndNonce) (*i
metric = 0.03 * piecefunc.DecimalUnit
}
metric = overheadAdjustedEventMetricF(em.validators.Len(), uint64(em.busyRate.Rate1()*piecefunc.DecimalUnit), metric)
metric = kickStartMetric(metric/2, mutEvent.Seq()) // adjust emission interval for FC
metric = kickStartMetric(metric, mutEvent.Seq())
} else if em.quorumIndexer != nil {
metric = eventMetric(em.quorumIndexer.GetMetricOf(hash.Events{mutEvent.ID()}), mutEvent.Seq())
metric = overheadAdjustedEventMetricF(em.validators.Len(), uint64(em.busyRate.Rate1()*piecefunc.DecimalUnit), metric)
Expand Down
3 changes: 3 additions & 0 deletions gossip/emitter/emitter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ func TestEmitter(t *testing.T) {
external.EXPECT().PeersNum().
Return(int(3)).
AnyTimes()
external.EXPECT().StateDB().
Return(nil).
AnyTimes()

em := NewEmitter(cfg, World{
External: external,
Expand Down
78 changes: 41 additions & 37 deletions gossip/emitter/hooks.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
package emitter

import (
"fmt"
"time"

"github.com/Fantom-foundation/lachesis-base/emitter/ancestor"
"github.com/Fantom-foundation/lachesis-base/inter/idx"
"github.com/Fantom-foundation/lachesis-base/inter/pos"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"

"github.com/Fantom-foundation/go-opera/inter"
"github.com/Fantom-foundation/go-opera/opera/contracts/emitterdriver"
"github.com/Fantom-foundation/go-opera/utils"
"github.com/Fantom-foundation/go-opera/utils/adapters/vecmt2dagidx"
"github.com/Fantom-foundation/go-opera/version"
)

var (
fcVersion = version.ToU64(1, 1, 3)
)

// OnNewEpoch should be called after each epoch change, and on startup
Expand Down Expand Up @@ -47,9 +44,31 @@ func (em *Emitter) OnNewEpoch(newValidators *pos.Validators, newEpoch idx.Epoch)
em.expectedEmitIntervals = make(map[idx.ValidatorID]time.Duration)
em.stakeRatio = make(map[idx.ValidatorID]uint64)

em.recountValidators(newValidators)
// get current adjustments from emitterdriver contract
statedb := em.world.StateDB()
var (
extMinInterval time.Duration
extConfirmingInterval time.Duration
switchToFCIndexer bool
)
if statedb != nil {
switchToFCIndexer = statedb.GetState(emitterdriver.ContractAddress, utils.U64to256(0)) != (common.Hash{0})
extMinInterval = time.Duration(statedb.GetState(emitterdriver.ContractAddress, utils.U64to256(1)).Big().Uint64())
extConfirmingInterval = time.Duration(statedb.GetState(emitterdriver.ContractAddress, utils.U64to256(2)).Big().Uint64())
}
if extMinInterval == 0 {
extMinInterval = em.config.EmitIntervals.Min
}
if extConfirmingInterval == 0 {
extConfirmingInterval = em.config.EmitIntervals.Confirming
}

// sanity check to ensure that durations aren't too small/large
em.intervals.Min = maxDuration(minDuration(em.config.EmitIntervals.Min*20, extMinInterval), em.config.EmitIntervals.Min/4)
em.globalConfirmingInterval = maxDuration(minDuration(em.config.EmitIntervals.Confirming*20, extConfirmingInterval), em.config.EmitIntervals.Confirming/4)
em.recountConfirmingIntervals(newValidators)

if em.switchToFCIndexer {
if switchToFCIndexer {
em.quorumIndexer = nil
em.fcIndexer = ancestor.NewFCIndexer(newValidators, em.world.DagIndex(), em.config.Validator.ID)
} else {
Expand All @@ -66,37 +85,8 @@ func (em *Emitter) OnNewEpoch(newValidators *pos.Validators, newEpoch idx.Epoch)
em.payloadIndexer = ancestor.NewPayloadIndexer(PayloadIndexerSize)
}

func (em *Emitter) handleVersionUpdate(e inter.EventPayloadI) {
if e.Seq() <= 1 && len(e.Extra()) > 0 {
var (
vMajor int
vMinor int
vPatch int
vMeta string
)
n, err := fmt.Sscanf(string(e.Extra()), "v-%d.%d.%d-%s", &vMajor, &vMinor, &vPatch, &vMeta)
if n == 4 && err == nil {
em.validatorVersions[e.Creator()] = version.ToU64(uint16(vMajor), uint16(vMinor), uint16(vPatch))
}
}
}

func (em *Emitter) fcValidators() pos.Weight {
counter := pos.Weight(0)
for v, ver := range em.validatorVersions {
if ver >= fcVersion {
counter += em.validators.Get(v)
}
}
return counter
}

// OnEventConnected tracks new events
func (em *Emitter) OnEventConnected(e inter.EventPayloadI) {
em.handleVersionUpdate(e)
if !em.switchToFCIndexer && em.fcValidators() >= pos.Weight(uint64(em.validators.TotalWeight())*5/6) {
em.switchToFCIndexer = true
}
if !em.isValidator() {
return
}
Expand Down Expand Up @@ -138,3 +128,17 @@ func (em *Emitter) OnEventConfirmed(he inter.EventI) {
}
}
}

func minDuration(a, b time.Duration) time.Duration {
if a < b {
return a
}
return b
}

func maxDuration(a, b time.Duration) time.Duration {
if a > b {
return a
}
return b
}
15 changes: 15 additions & 0 deletions gossip/emitter/mock/world.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 4 additions & 11 deletions gossip/emitter/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import (

const (
validatorChallenge = 4 * time.Second
networkStartPeriod = 3 * time.Hour
)

func (em *Emitter) recountValidators(validators *pos.Validators) {
// stakers with lower stake should emit less events to reduce network load
func (em *Emitter) recountConfirmingIntervals(validators *pos.Validators) {
// validators with lower stake should emit fewer events to reduce network load
// confirmingEmitInterval = piecefunc(totalStakeBeforeMe / totalStake) * MinEmitInterval
totalStakeBefore := pos.Weight(0)
for i, stake := range validators.SortedWeights() {
Expand All @@ -26,15 +25,9 @@ func (em *Emitter) recountValidators(validators *pos.Validators) {
}
confirmingEmitIntervalRatio := confirmingEmitIntervalF(stakeRatio)
em.stakeRatio[vid] = stakeRatio
em.expectedEmitIntervals[vid] = time.Duration(piecefunc.Mul(uint64(em.config.EmitIntervals.Confirming), confirmingEmitIntervalRatio))
em.expectedEmitIntervals[vid] = time.Duration(piecefunc.Mul(uint64(em.globalConfirmingInterval), confirmingEmitIntervalRatio))
}
em.intervals.Confirming = em.expectedEmitIntervals[em.config.Validator.ID]
em.intervals.Max = em.config.EmitIntervals.Max
// if network just has started, then relax the doublesign protection
if time.Since(em.world.GetGenesisTime().Time()) < networkStartPeriod {
em.intervals.Max /= 6
em.intervals.DoublesignProtection /= 6
}
}

func (em *Emitter) recheckChallenges() {
Expand Down Expand Up @@ -67,7 +60,7 @@ func (em *Emitter) recheckChallenges() {
}
}
if recount {
em.recountValidators(em.validators)
em.recountConfirmingIntervals(em.validators)
}
em.prevRecheckedChallenges = now
}
3 changes: 3 additions & 0 deletions gossip/emitter/world.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/Fantom-foundation/lachesis-base/inter/idx"
"github.com/Fantom-foundation/lachesis-base/inter/pos"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"

"github.com/Fantom-foundation/go-opera/inter"
Expand Down Expand Up @@ -35,6 +36,8 @@ type (
IsBusy() bool
IsSynced() bool
PeersNum() int

StateDB() *state.StateDB
}

// aliases for mock generator
Expand Down
9 changes: 9 additions & 0 deletions gossip/emitter_world.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/Fantom-foundation/lachesis-base/hash"
"github.com/Fantom-foundation/lachesis-base/inter/idx"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"

"github.com/Fantom-foundation/go-opera/gossip/emitter"
Expand Down Expand Up @@ -60,6 +61,14 @@ func (ew *emitterWorldProc) IsBusy() bool {
return atomic.LoadUint32(&ew.s.eventBusyFlag) != 0 || atomic.LoadUint32(&ew.s.blockBusyFlag) != 0
}

func (ew *emitterWorldProc) StateDB() *state.StateDB {
statedb, err := ew.s.store.evm.StateDB(ew.s.store.GetBlockState().FinalizedStateRoot)
if err != nil {
return nil
}
return statedb
}

func (ew *emitterWorldProc) IsSynced() bool {
return ew.s.handler.syncStatus.AcceptEvents()
}
Expand Down
6 changes: 6 additions & 0 deletions opera/contracts/emitterdriver/emitterdriver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package emitterdriver

import "github.com/ethereum/go-ethereum/common"

// ContractAddress is the EmitterDriver contract address
var ContractAddress = common.HexToAddress("0xee00d10000000000000000000000000000000000")