aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugin/evm/vm.go144
1 files changed, 87 insertions, 57 deletions
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index c9bdad9..f125564 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -69,19 +69,15 @@ const (
maxBlockTime = 1000 * time.Millisecond
batchSize = 250
maxUTXOsToFetch = 1024
- blockCacheSize = 1 << 10 // 1024
+ blockCacheSize = 1024
codecVersion = uint16(0)
)
const (
- bdTimerStateMin = iota
- bdTimerStateMax
- bdTimerStateLong
+ txFee = units.MilliAvax
)
var (
- txFee = units.MilliAvax
-
errEmptyBlock = errors.New("empty block")
errCreateBlock = errors.New("couldn't create block")
errUnknownBlock = errors.New("unknown block")
@@ -103,6 +99,17 @@ var (
errInvalidNonce = errors.New("invalid nonce")
)
+// mayBuildBlockStatus denotes whether the engine should be notified
+// that a block should be built, or whether more time has to pass
+// before doing so. See VM's [mayBuildBlock].
+type mayBuildBlockStatus uint8
+
+const (
+ wait mayBuildBlockStatus = iota
+ conditionalWait
+ build
+)
+
func maxDuration(x, y time.Duration) time.Duration {
if x > y {
return x
@@ -145,14 +152,16 @@ type VM struct {
CLIConfig CommandLineConfig
- chainID *big.Int
- networkID uint64
- genesisHash common.Hash
- chain *coreth.ETHChain
- chaindb Database
- newBlockChan chan *Block
- networkChan chan<- commonEng.Message
- newMinedBlockSub *event.TypeMuxSubscription
+ chainID *big.Int
+ networkID uint64
+ genesisHash common.Hash
+ chain *coreth.ETHChain
+ chaindb Database
+ newBlockChan chan *Block
+ // A message is sent on this channel when a new block
+ // is ready to be build. This notifies the consensus engine.
+ notifyBuildBlockChan chan<- commonEng.Message
+ newMinedBlockSub *event.TypeMuxSubscription
acceptedDB database.Database
@@ -165,11 +174,27 @@ type VM struct {
lastAccepted *Block
writingMetadata uint32
- bdlock sync.Mutex
- blockDelayTimer *timer.Timer
- bdTimerState int8
- bdGenWaitFlag bool
- bdGenFlag bool
+ // [buildBlockLock] must be held when accessing [mayBuildBlock],
+ // [tryToBuildBlock] or [awaitingBuildBlock].
+ buildBlockLock sync.Mutex
+ // [buildBlockTimer] periodically fires in order to update [mayBuildBlock]
+ // and to try to build a block, if applicable.
+ buildBlockTimer *timer.Timer
+ // [mayBuildBlock] == [wait] means that the next block may be built
+ // only after more time has elapsed.
+ // [mayBuildBlock] == [conditionalWait] means that the next block may be built
+ // only if it has more than [batchSize] txs in it. Otherwise, wait until more
+ // time has elapsed.
+ // [mayBuildBlock] == [build] means that the next block may be built
+ // at any time.
+ mayBuildBlock mayBuildBlockStatus
+ // If true, try to notify the engine that a block should be built.
+ // Engine may not be notified because [mayBuildBlock] says to wait.
+ tryToBuildBlock bool
+ // If true, the engine has been notified that it should build a block
+ // but has not done so yet. If this is the case, wait until it has
+ // built a block before notifying it again.
+ awaitingBuildBlock bool
genlock sync.Mutex
txSubmitChan <-chan struct{}
@@ -329,26 +354,31 @@ func (vm *VM) Initialize(
vm.blockStatusCache = cache.LRU{Size: blockCacheSize}
vm.blockAtomicInputCache = cache.LRU{Size: blockCacheSize}
vm.newBlockChan = make(chan *Block)
- vm.networkChan = toEngine
- vm.blockDelayTimer = timer.NewTimer(func() {
- vm.bdlock.Lock()
- switch vm.bdTimerState {
- case bdTimerStateMin:
- vm.bdTimerState = bdTimerStateMax
- vm.blockDelayTimer.SetTimeoutIn(maxDuration(maxBlockTime-minBlockTime, 0))
- case bdTimerStateMax:
- vm.bdTimerState = bdTimerStateLong
+ vm.notifyBuildBlockChan = toEngine
+
+ // Periodically updates [vm.mayBuildBlock] and tries to notify the engine to build
+ // a new block, if applicable.
+ vm.buildBlockTimer = timer.NewTimer(func() {
+ vm.buildBlockLock.Lock()
+ switch vm.mayBuildBlock {
+ case wait:
+ // Some time has passed. Allow block to be built if it has enough txs in it.
+ vm.mayBuildBlock = conditionalWait
+ vm.buildBlockTimer.SetTimeoutIn(maxDuration(maxBlockTime-minBlockTime, 0))
+ case conditionalWait:
+ // More time has passed. Allow block to be built regardless of tx count.
+ vm.mayBuildBlock = build
}
- tryAgain := vm.bdGenWaitFlag
- vm.bdlock.Unlock()
- if tryAgain {
+ tryBuildBlock := vm.tryToBuildBlock
+ vm.buildBlockLock.Unlock()
+ if tryBuildBlock {
vm.tryBlockGen()
}
})
- go ctx.Log.RecoverAndPanic(vm.blockDelayTimer.Dispatch)
+ go ctx.Log.RecoverAndPanic(vm.buildBlockTimer.Dispatch)
- vm.bdTimerState = bdTimerStateLong
- vm.bdGenWaitFlag = true
+ vm.mayBuildBlock = build
+ vm.tryToBuildBlock = true
vm.txPoolStabilizedOk = make(chan struct{}, 1)
// TODO: read size from options
vm.pendingAtomicTxs = make(chan *Tx, 1024)
@@ -411,7 +441,7 @@ func (vm *VM) Shutdown() error {
}
vm.writeBackMetadata()
- vm.blockDelayTimer.Stop()
+ vm.buildBlockTimer.Stop()
close(vm.shutdownChan)
vm.chain.Stop()
vm.shutdownWg.Wait()
@@ -423,15 +453,13 @@ func (vm *VM) BuildBlock() (snowman.Block, error) {
vm.chain.GenBlock()
block := <-vm.newBlockChan
- // reset the min block time timer
- // after finishing attempt to build
- // a block.
- vm.bdlock.Lock()
- vm.bdTimerState = bdTimerStateMin
- vm.bdGenWaitFlag = false
- vm.bdGenFlag = false
- vm.blockDelayTimer.SetTimeoutIn(minBlockTime)
- vm.bdlock.Unlock()
+ vm.buildBlockLock.Lock()
+ // Specify that we should wait before trying to build another block.
+ vm.mayBuildBlock = wait
+ vm.tryToBuildBlock = false
+ vm.awaitingBuildBlock = false
+ vm.buildBlockTimer.SetTimeoutIn(minBlockTime)
+ vm.buildBlockLock.Unlock()
if block == nil {
return nil, errCreateBlock
@@ -578,13 +606,14 @@ func (vm *VM) updateStatus(blockID ids.ID, status choices.Status) {
}
func (vm *VM) tryBlockGen() error {
- vm.bdlock.Lock()
- defer vm.bdlock.Unlock()
- if vm.bdGenFlag {
- // skip if one call already generates a block in this round
+ vm.buildBlockLock.Lock()
+ defer vm.buildBlockLock.Unlock()
+ if vm.awaitingBuildBlock {
+ // We notified the engine that a block should be built but it hasn't
+ // done so yet. Wait until it has done so before notifying again.
return nil
}
- vm.bdGenWaitFlag = true
+ vm.tryToBuildBlock = true
vm.genlock.Lock()
defer vm.genlock.Unlock()
@@ -597,20 +626,21 @@ func (vm *VM) tryBlockGen() error {
return nil
}
- switch vm.bdTimerState {
- case bdTimerStateMin:
+ switch vm.mayBuildBlock {
+ case wait: // Wait more time before notifying engine to building a block
return nil
- case bdTimerStateMax:
+ case conditionalWait: // Notify engine only if there are enough pending txs
if size < batchSize {
return nil
}
- case bdTimerStateLong:
- // timeout; go ahead and generate a new block anyway
+ case build: // Notify engine
+ default:
+ panic(fmt.Sprintf("mayBuildBlock has unexpected value %d", vm.mayBuildBlock))
}
select {
- case vm.networkChan <- commonEng.PendingTxs:
- // successfully push out the notification; this round ends
- vm.bdGenFlag = true
+ case vm.notifyBuildBlockChan <- commonEng.PendingTxs:
+ // Notify engine to build a block
+ vm.awaitingBuildBlock = true
default:
return errBlockFrequency
}