Skip to content
This repository has been archived by the owner on Jun 17, 2022. It is now read-only.

Commit

Permalink
gas power
Browse files Browse the repository at this point in the history
Merge pull request #328 from devintegral3/feature/gas_power
  • Loading branch information
a.guzev committed Sep 20, 2019
2 parents d718f72 + b350c60 commit 6d3ef0d
Show file tree
Hide file tree
Showing 20 changed files with 534 additions and 289 deletions.
2 changes: 1 addition & 1 deletion src/evm_core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Generate a chain of b.N blocks using the supplied block
// generator function.
net := &lachesis.Config{
Dag: lachesis.DefaultDagConfig(),
Dag: lachesis.FakeNetDagConfig(),
Genesis: genesis.Genesis{
Alloc: genesis.Accounts{benchRootAddr: {Balance: benchRootFunds}},
},
Expand Down
17 changes: 15 additions & 2 deletions src/gossip/config_emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,31 @@ import (
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)

type EmitterConfig struct {
Emitbase common.Address

MinEmitInterval time.Duration // minimum event emission interval
MaxEmitInterval time.Duration // maximum event emission interval

MaxGasRateGrowthFactor float64 // fine to use float, because no need in determinism

// thresholds on GasLeft
SmoothTpsThreshold uint64 `json:"smoothTpsThreshold"`
NoTxsThreshold uint64 `json:"noTxsThreshold"`
EmergencyThreshold uint64 `json:"emergencyThreshold"`
}

func DefaultEmitterConfig() EmitterConfig {
return EmitterConfig{
MinEmitInterval: 1 * time.Second,
MaxEmitInterval: 60 * time.Second,
MinEmitInterval: 1 * time.Second,
MaxEmitInterval: 60 * time.Second,
MaxGasRateGrowthFactor: 3.0,

SmoothTpsThreshold: params.TxGas * 500,
NoTxsThreshold: params.TxGas * 100,
EmergencyThreshold: params.TxGas * 5,
}
}
166 changes: 133 additions & 33 deletions src/gossip/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"

"github.com/Fantom-foundation/go-lachesis/src/event_check"
Expand All @@ -32,14 +33,16 @@ type Emitter struct {
prevEpoch idx.Epoch
txpool txPool

dag *lachesis.DagConfig
config *EmitterConfig
networkId uint64
dag *lachesis.DagConfig
config *EmitterConfig

am *accounts.Manager
coinbase common.Address
coinbaseMu sync.RWMutex

gasRate metrics.Meter
prevEmittedTime time.Time

onEmitted func(e *inter.Event)

done chan struct{}
Expand All @@ -61,6 +64,7 @@ func NewEmitter(
dag: &config.Net.Dag,
config: &config.Emitter,
am: am,
gasRate: metrics.NewMeterForced(),
engine: engine,
engineMu: engineMu,
store: store,
Expand All @@ -76,15 +80,19 @@ func (em *Emitter) StartEventEmission() {
}
em.done = make(chan struct{})

em.prevEmittedTime = em.loadPrevEmitTime()

done := em.done
em.wg.Add(1)
go func() {
defer em.wg.Done()
ticker := time.NewTicker(em.config.MinEmitInterval)
ticker := time.NewTicker(em.config.MinEmitInterval / 10)
for {
select {
case <-ticker.C:
em.EmitEvent()
if time.Since(em.prevEmittedTime) >= em.config.MinEmitInterval {
em.EmitEvent()
}
case <-done:
return
}
Expand Down Expand Up @@ -117,6 +125,48 @@ func (em *Emitter) GetCoinbase() common.Address {
return em.coinbase
}

func (em *Emitter) loadPrevEmitTime() time.Time {
prevEventId := em.store.GetLastEvent(em.engine.GetEpoch(), em.GetCoinbase())
if prevEventId == nil {
return em.prevEmittedTime
}
prevEvent := em.store.GetEventHeader(prevEventId.Epoch(), *prevEventId)
if prevEvent == nil {
return em.prevEmittedTime
}
return prevEvent.ClaimedTime.Time()
}

func (em *Emitter) addTxs(e *inter.Event) *inter.Event {
poolTxs, err := em.txpool.Pending()
if err != nil {
log.Error("Tx pool transactions fetching error", "err", err)
return e
}

maxGasUsed := em.maxGasPowerToUse(e)

for _, txs := range poolTxs {
for _, tx := range txs {
if tx.Gas() < e.GasPowerLeft && e.GasPowerUsed+tx.Gas() < maxGasUsed {
e.GasPowerUsed += tx.Gas()
e.GasPowerLeft -= tx.Gas()
e.Transactions = append(e.Transactions, tx)
}
}
}
// Spill txs if exceeded size limit
// In all the "real" cases, the event will be limited by gas, not size.
// Yet it's technically possible to construct an event which is limited by size and not by gas.
for uint64(e.CalcSize()) > (basic_check.MaxEventSize-500) && len(e.Transactions) > 0 {
tx := e.Transactions[len(e.Transactions)-1]
e.GasPowerUsed -= tx.Gas()
e.GasPowerLeft += tx.Gas()
e.Transactions = e.Transactions[:len(e.Transactions)-1]
}
return e
}

// createEvent is not safe for concurrent use.
func (em *Emitter) createEvent() *inter.Event {
coinbase := em.GetCoinbase()
Expand Down Expand Up @@ -158,9 +208,11 @@ func (em *Emitter) createEvent() *inter.Event {

selfParentSeq = 0
selfParentTime = 0
var selfParentHeader *inter.EventHeaderData
if selfParent != nil {
selfParentSeq = parentHeaders[0].Seq
selfParentTime = parentHeaders[0].ClaimedTime
selfParentHeader = parentHeaders[0]
selfParentSeq = selfParentHeader.Seq
selfParentTime = selfParentHeader.ClaimedTime
}

event := inter.NewEvent()
Expand All @@ -171,38 +223,25 @@ func (em *Emitter) createEvent() *inter.Event {
event.Parents = parents
event.Lamport = maxLamport + 1
event.ClaimedTime = inter.MaxTimestamp(inter.Timestamp(time.Now().UnixNano()), selfParentTime+1)

// Add txs
poolTxs, err := em.txpool.Pending()
if err != nil {
log.Error("Tx pool transactions fetching error", "err", err)
return nil
}
event.GasPowerUsed = basic_check.CalcGasPowerUsed(event)
for _, txs := range poolTxs {
for _, tx := range txs {
if event.GasPowerUsed+tx.Gas() < basic_check.MaxGasPowerUsed {
event.Transactions = append(event.Transactions, txs...)
event.GasPowerUsed += tx.Gas()
}
}
}
// Spill txs if exceeded size limit
// In all the "real" cases, the event will be limited by gas, not size.
// Yet it's technically possible to construct an event which is limited by size and not by gas.
for uint64(event.CalcSize()) > basic_check.MaxEventSize && len(event.Transactions) > 0 {
event.Transactions = event.Transactions[:len(event.Transactions)-1]
}
// calc Merkle root
event.TxHash = types.DeriveSha(event.Transactions)

// set consensus fields
event = em.engine.Prepare(event)
event = em.engine.Prepare(event) // GasPowerLeft is calced here
if event == nil {
log.Warn("dropped event while emitting")
return nil
}

// Add txs
event = em.addTxs(event)

if !em.isAllowedToEmit(event, selfParentHeader) {
return nil
}

// calc Merkle root
event.TxHash = types.DeriveSha(event.Transactions)

// sign
signer := func(data []byte) (sig []byte, err error) {
acc := accounts.Account{
Expand Down Expand Up @@ -238,15 +277,76 @@ func (em *Emitter) createEvent() *inter.Event {
return event
}

func (em *Emitter) maxGasPowerToUse(e *inter.Event) uint64 {
// No txs if power is low
{
threshold := em.config.NoTxsThreshold
if e.GasPowerLeft <= threshold {
return 0
}
}
// Smooth TPS if power isn't big
{
threshold := em.config.SmoothTpsThreshold
if e.GasPowerLeft <= threshold {
// it's emitter, so no need in determinism => fine to use float
passedTime := float64(e.ClaimedTime.Time().Sub(em.prevEmittedTime)) / (float64(time.Second))
maxGasUsed := uint64(passedTime * em.gasRate.Rate1() * em.config.MaxGasRateGrowthFactor)
if maxGasUsed > basic_check.MaxGasPowerUsed {
maxGasUsed = basic_check.MaxGasPowerUsed
}
return maxGasUsed
}
}
return basic_check.MaxGasPowerUsed
}

func (em *Emitter) isAllowedToEmit(e *inter.Event, selfParent *inter.EventHeaderData) bool {
// Slow down emitting if power is low
{
threshold := em.config.NoTxsThreshold
if e.GasPowerLeft <= threshold {
// it's emitter, so no need in determinism => fine to use float
minT := float64(em.config.MinEmitInterval)
maxT := float64(em.config.MaxEmitInterval)
factor := float64(e.GasPowerLeft) / float64(threshold)
adjustedEmitInterval := time.Duration(maxT - (maxT-minT)*factor)
passedTime := e.ClaimedTime.Time().Sub(em.prevEmittedTime)
if passedTime < adjustedEmitInterval {
return false
}
}
}
// Forbid emitting if not enough power and power is decreasing
{
threshold := em.config.EmergencyThreshold
if e.GasPowerLeft <= threshold {
if !(selfParent != nil && e.GasPowerLeft >= selfParent.GasPowerLeft) {
log.Warn("Not enough power to emit event, waiting", "power", e.GasPowerLeft, "self_parent_power", selfParent.GasPowerLeft)
return false
}
}
}

return true
}

func (em *Emitter) EmitEvent() *inter.Event {
em.engineMu.Lock()
defer em.engineMu.Unlock()

e := em.createEvent()
if e != nil && em.onEmitted != nil {
if e == nil {
return nil
}

if em.onEmitted != nil {
em.onEmitted(e)
log.Info("New event emitted", "e", e.String())
}
em.gasRate.Mark(int64(e.GasPowerUsed))
em.prevEmittedTime = time.Now() // record time after connecting, to add the event processing time
log.Info("New event emitted", "e", e.String())

return e
}

Expand Down
4 changes: 4 additions & 0 deletions src/inter/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ func (t Timestamp) Unix() int64 {
return int64(t) / int64(time.Second)
}

func (t Timestamp) Time() time.Time {
return time.Unix(int64(t) / int64(time.Second), int64(t) % int64(time.Second))
}

// MaxTimestamp return max value.
func MaxTimestamp(x, y Timestamp) Timestamp {
if x > y {
Expand Down
50 changes: 45 additions & 5 deletions src/lachesis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package lachesis

import (
"math/big"
"time"

"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/params"

"github.com/Fantom-foundation/go-lachesis/src/inter"
"github.com/Fantom-foundation/go-lachesis/src/inter/idx"
"github.com/Fantom-foundation/go-lachesis/src/lachesis/genesis"
)
Expand All @@ -15,10 +18,20 @@ const (
FakeNetworkId uint64 = 3
)

type GasPowerConfig struct {
TotalPerH uint64 `json:"totalPerH"`
MaxStashedPeriod inter.Timestamp `json:"maxStashedPeriod"`
StartupPeriod inter.Timestamp `json:"startupPeriod"`
MinStartupGasPower uint64 `json:"minStartupGasPower"`
}

// DagConfig of DAG.
type DagConfig struct {
MaxParents int `json:"maxParents"`
EpochLen idx.Frame `json:"epochLen"`
MaxParents int `json:"maxParents"`
EpochLen idx.Frame `json:"epochLen"`
MaxMemberEventsInBlock idx.Event `json:"maxMemberEventsInBlock"`

GasPower GasPowerConfig `json:"gasPower"`
}

// Config describes lachesis net.
Expand Down Expand Up @@ -75,13 +88,40 @@ func FakeNetConfig(n int) Config {
Name: "fake",
NetworkId: FakeNetworkId,
Genesis: g,
Dag: DefaultDagConfig(),
Dag: FakeNetDagConfig(),
}
}

func DefaultDagConfig() DagConfig {
return DagConfig{
MaxParents: 3,
EpochLen: 100,
MaxParents: 3,
EpochLen: 100,
MaxMemberEventsInBlock: 50,
GasPower: DefaultGasPowerConfig(),
}
}

func FakeNetDagConfig() DagConfig {
return DagConfig{
MaxParents: 3,
EpochLen: 100,
MaxMemberEventsInBlock: 50,
GasPower: FakeNetGasPowerConfig(),
}
}

func DefaultGasPowerConfig() GasPowerConfig {
return GasPowerConfig{
TotalPerH: 50 * params.TxGas * 60 * 60,
MaxStashedPeriod: inter.Timestamp(1 * time.Hour),
StartupPeriod: inter.Timestamp(5 * time.Minute),
MinStartupGasPower: params.TxGas * 20,
}
}

func FakeNetGasPowerConfig() GasPowerConfig {
config := DefaultGasPowerConfig()
config.TotalPerH *= 10
config.MinStartupGasPower *= 10
return config
}
Loading

0 comments on commit 6d3ef0d

Please sign in to comment.