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

Uptime tracking #1263

Draft
wants to merge 17 commits into
base: pausable-uptime-manager
Choose a base branch
from
Draft
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
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21.12
require (
github.com/VictoriaMetrics/fastcache v1.12.1
github.com/antithesishq/antithesis-sdk-go v0.3.8
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240916220401-1753950304a4
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240920150211-07af6b2fbe17
github.com/cespare/cp v0.1.0
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
github.com/davecgh/go-spew v1.1.1
Expand Down Expand Up @@ -55,7 +55,7 @@ require (
require (
github.com/DataDog/zstd v1.5.2 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732 // indirect
github.com/ava-labs/coreth v0.13.8 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
Expand Down
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax
github.com/antithesishq/antithesis-sdk-go v0.3.8 h1:OvGoHxIcOXFJLyn9IJQ5DzByZ3YVAWNBc394ObzDRb8=
github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240916220401-1753950304a4 h1:07qWIUU3C/nAVBJK5orGZKoEVodQE8OsfnpPZ8cTnSQ=
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240916220401-1753950304a4/go.mod h1:yFx3V31Jy9NFa8GZlgGnwiVf8KGjeF2+Uc99l9Scd/8=
github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732 h1:wlhGJbmb7s3bU2QWtxKjscGjfHknQiq+cVhhUjONsB8=
github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732/go.mod h1:RkQLaQ961Xe/sUb3ycn4Qi18vPPuEetTqDf2eDcquAs=
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240919204453-a754e44c1795 h1:zxqtKkAuU70XQlr3Pz6RQDvopXBqGDu2y2PshXe507U=
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240919204453-a754e44c1795/go.mod h1:YzHJbHAJOlRLwG1pxWk4uAI7nvV4cxpgQL1FSAx/H4Y=
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240920144520-3adf6d4c0f62 h1:H/8gjHFcYDC02oSDehRQW3s89cIV6aM2u7WfxMuxlNU=
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240920144520-3adf6d4c0f62/go.mod h1:YzHJbHAJOlRLwG1pxWk4uAI7nvV4cxpgQL1FSAx/H4Y=
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240920150211-07af6b2fbe17 h1:nc/U63uqdIikeiNexGe0PIOwSOWcRqmuOWuBxqJ7B3Y=
github.com/ava-labs/avalanchego v1.11.12-rc.2.0.20240920150211-07af6b2fbe17/go.mod h1:YzHJbHAJOlRLwG1pxWk4uAI7nvV4cxpgQL1FSAx/H4Y=
github.com/ava-labs/coreth v0.13.8 h1:f14X3KgwHl9LwzfxlN6S4bbn5VA2rhEsNnHaRLSTo/8=
github.com/ava-labs/coreth v0.13.8/go.mod h1:t3BSv/eQv0AlDPMfEDCMMoD/jq1RkUsbFzQAFg5qBcE=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
Expand Down
38 changes: 38 additions & 0 deletions plugin/evm/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"context"
"math/big"

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

// SnowmanAPI introduces snowman specific functionality to the evm
type SnowmanAPI struct{ vm *VM }

// GetAcceptedFrontReply defines the reply that will be sent from the
// GetAcceptedFront API call
type GetAcceptedFrontReply struct {
Hash common.Hash `json:"hash"`
Number *big.Int `json:"number"`
}

// GetAcceptedFront returns the last accepted block's hash and height
func (api *SnowmanAPI) GetAcceptedFront(ctx context.Context) (*GetAcceptedFrontReply, error) {
blk := api.vm.blockChain.LastConsensusAcceptedBlock()
return &GetAcceptedFrontReply{
Hash: blk.Hash(),
Number: blk.Number(),
}, nil
}

// IssueBlock to the chain
func (api *SnowmanAPI) IssueBlock(ctx context.Context) error {
log.Info("Issuing a new block")
api.vm.builder.signalTxsReady()
return nil
}
2 changes: 1 addition & 1 deletion plugin/evm/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (b *Block) verify(predicateContext *precompileconfig.PredicateContext, writ
// If the chain is still bootstrapping, we can assume that all blocks we are verifying have
// been accepted by the network (so the predicate was validated by the network when the
// block was originally verified).
if b.vm.bootstrapped {
if b.vm.bootstrapped.Get() {
if err := b.verifyPredicates(predicateContext); err != nil {
return fmt.Errorf("failed to verify predicates: %w", err)
}
Expand Down
18 changes: 14 additions & 4 deletions plugin/evm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ const (
// - state sync time: ~6 hrs.
defaultStateSyncMinBlocks = 300_000
defaultStateSyncRequestSize = 1024 // the number of key/values to ask peers for per request
defaultValidatorsAPIEnabled = true

// TODO: decide for a sane value for this
defaultLoadValidatorsFrequency = 5 * time.Minute
)

var (
Expand Down Expand Up @@ -87,10 +91,11 @@ type Config struct {
AirdropFile string `json:"airdrop"`

// Subnet EVM APIs
SnowmanAPIEnabled bool `json:"snowman-api-enabled"`
AdminAPIEnabled bool `json:"admin-api-enabled"`
AdminAPIDir string `json:"admin-api-dir"`
WarpAPIEnabled bool `json:"warp-api-enabled"`
SnowmanAPIEnabled bool `json:"snowman-api-enabled"`
ValidatorsAPIEnabled bool `json:"validators-api-enabled"`
AdminAPIEnabled bool `json:"admin-api-enabled"`
AdminAPIDir string `json:"admin-api-dir"`
WarpAPIEnabled bool `json:"warp-api-enabled"`

// EnabledEthAPIs is a list of Ethereum services that should be enabled
// If none is specified, then we use the default list [defaultEnabledAPIs]
Expand Down Expand Up @@ -225,6 +230,9 @@ type Config struct {

// RPC settings
HttpBodyLimit uint64 `json:"http-body-limit"`

// LoadValidatorsFrequency is the frequency at which the node should load the validators
LoadValidatorsFrequency time.Duration `json:"load-validators-frequency"`
}

// EthAPIs returns an array of strings representing the Eth APIs that should be enabled
Expand Down Expand Up @@ -284,6 +292,8 @@ func (c *Config) SetDefaults() {
c.StateSyncRequestSize = defaultStateSyncRequestSize
c.AllowUnprotectedTxHashes = defaultAllowUnprotectedTxHashes
c.AcceptedCacheSize = defaultAcceptedCacheSize
c.ValidatorsAPIEnabled = defaultValidatorsAPIEnabled
c.LoadValidatorsFrequency = defaultLoadValidatorsFrequency
}

func (d *Duration) UnmarshalJSON(data []byte) (err error) {
Expand Down
80 changes: 58 additions & 22 deletions plugin/evm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,71 @@
package evm

import (
"context"
"math/big"
"net/http"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/set"
)

// SnowmanAPI introduces snowman specific functionality to the evm
type SnowmanAPI struct{ vm *VM }
type ValidatorsAPI struct {
vm *VM
}

type GetCurrentValidatorsRequest struct {
NodeIDs []ids.NodeID `json:"nodeIDs"`
}

// GetAcceptedFrontReply defines the reply that will be sent from the
// GetAcceptedFront API call
type GetAcceptedFrontReply struct {
Hash common.Hash `json:"hash"`
Number *big.Int `json:"number"`
type GetCurrentValidatorsResponse struct {
Validators []currentValidatorResponse `json:"validators"`
}

// GetAcceptedFront returns the last accepted block's hash and height
func (api *SnowmanAPI) GetAcceptedFront(ctx context.Context) (*GetAcceptedFrontReply, error) {
blk := api.vm.blockChain.LastConsensusAcceptedBlock()
return &GetAcceptedFrontReply{
Hash: blk.Hash(),
Number: blk.Number(),
}, nil
type currentValidatorResponse struct {
ValidationID ids.ID `json:"validationID"`
NodeID ids.NodeID `json:"nodeID"`
StartTime time.Time `json:"startTime"`
IsActive bool `json:"isActive"`
IsConnected bool `json:"isConnected"`
Uptime time.Duration `json:"uptime"`
}

// IssueBlock to the chain
func (api *SnowmanAPI) IssueBlock(ctx context.Context) error {
log.Info("Issuing a new block")
api.vm.builder.signalTxsReady()
// GetUptime returns the uptime of the node
func (api *ValidatorsAPI) GetCurrentValidators(_ *http.Request, args *GetCurrentValidatorsRequest, reply *GetCurrentValidatorsResponse) error {
api.vm.ctx.Lock.RLock()
defer api.vm.ctx.Lock.RUnlock()

nodeIDs := set.Of(args.NodeIDs...)
if nodeIDs.Len() == 0 {
nodeIDs = api.vm.validatorState.GetValidatorIDs()
}

reply.Validators = make([]currentValidatorResponse, 0, nodeIDs.Len())

for _, nodeID := range nodeIDs.List() {
validator, err := api.vm.validatorState.GetValidator(nodeID)
switch {
case err == database.ErrNotFound:
continue
case err != nil:
return err
}

isConnected := api.vm.uptimeManager.IsConnected(nodeID)

uptime, _, err := api.vm.uptimeManager.CalculateUptime(nodeID)
if err != nil {
return err
}

reply.Validators = append(reply.Validators, currentValidatorResponse{
ValidationID: validator.ValidationID,
NodeID: nodeID,
StartTime: validator.StartTime,
IsActive: validator.IsActive,
IsConnected: isConnected,
Uptime: time.Duration(uptime.Seconds()),
})
}
return nil
}
2 changes: 1 addition & 1 deletion plugin/evm/syncervm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) {

// check we can transition to [NormalOp] state and continue to process blocks.
require.NoError(syncerVM.SetState(context.Background(), snow.NormalOp))
require.True(syncerVM.bootstrapped)
require.True(syncerVM.bootstrapped.Get())

// Generate blocks after we have entered normal consensus as well
generateAndAcceptBlocks(t, syncerVM, blocksToBuild, func(_ int, gen *core.BlockGen) {
Expand Down
2 changes: 1 addition & 1 deletion plugin/evm/tx_gossip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestEthTxGossip(t *testing.T) {
require := require.New(t)
ctx := context.Background()
snowCtx := utils.TestSnowContext()
validatorState := &validatorstest.State{}
validatorState := utils.NewTestValidatorState()
snowCtx.ValidatorState = validatorState

responseSender := &enginetest.SenderStub{
Expand Down
76 changes: 76 additions & 0 deletions plugin/evm/validators/mocks.go

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

23 changes: 23 additions & 0 deletions plugin/evm/validators/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type State interface {
GetValidationIDs() set.Set[ids.ID]
// GetValidatorIDs returns the validator node IDs in the state
GetValidatorIDs() set.Set[ids.NodeID]
// GetValidator returns the validator data for the given nodeID
GetValidator(nodeID ids.NodeID) (*ValidatorOutput, error)

// RegisterListener registers a listener to the state
RegisterListener(StateCallbackListener)
Expand All @@ -57,6 +59,13 @@ type StateCallbackListener interface {
OnValidatorStatusUpdated(vID ids.ID, nodeID ids.NodeID, isActive bool)
}

type ValidatorOutput struct {
ValidationID ids.ID `json:"validationID"`
NodeID ids.NodeID `json:"nodeID"`
StartTime time.Time `json:"startTime"`
IsActive bool `json:"isActive"`
}

type validatorData struct {
UpDuration time.Duration `serialize:"true"`
LastUpdated uint64 `serialize:"true"`
Expand Down Expand Up @@ -241,6 +250,20 @@ func (s *state) GetValidatorIDs() set.Set[ids.NodeID] {
return ids
}

// GetValidator returns the validator data for the given nodeID
func (s *state) GetValidator(nodeID ids.NodeID) (*ValidatorOutput, error) {
data, err := s.getData(nodeID)
if err != nil {
return nil, err
}
return &ValidatorOutput{
ValidationID: data.validationID,
NodeID: data.NodeID,
StartTime: data.getStartTime(),
IsActive: data.IsActive,
}, nil
}

// RegisterListener registers a listener to the state
// OnValidatorAdded is called for all current validators on the provided listener before this function returns
func (s *state) RegisterListener(listener StateCallbackListener) {
Expand Down
Loading
Loading