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 validation nits #1378

Merged
merged 9 commits into from
Nov 6, 2024
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
122 changes: 122 additions & 0 deletions examples/sign-uptime-message/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package main

import (
"context"
"log"
"net/netip"
"time"

"github.com/prometheus/client_golang/prometheus"
"google.golang.org/protobuf/proto"

"github.com/ava-labs/avalanchego/api/info"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/network/p2p"
"github.com/ava-labs/avalanchego/network/peer"
"github.com/ava-labs/avalanchego/proto/pb/sdk"
"github.com/ava-labs/avalanchego/snow/networking/router"
"github.com/ava-labs/avalanchego/utils/compression"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/vms/platformvm/warp"
"github.com/ava-labs/avalanchego/vms/platformvm/warp/payload"
"github.com/ava-labs/avalanchego/wallet/subnet/primary"
"github.com/ava-labs/subnet-evm/warp/messages"

p2pmessage "github.com/ava-labs/avalanchego/message"
)

func main() {
ceyonur marked this conversation as resolved.
Show resolved Hide resolved
uri := primary.LocalAPIURI
// The following IDs are placeholders and should be replaced with real values
// before running the code.
// The validationID is for the validation period that the uptime message is signed for.
validationID := ids.FromStringOrPanic("p3NUAY4PbcAnyCyvUTjGVjezNEQCdnVdfAbJcZScvKpxP5tJr")
darioush marked this conversation as resolved.
Show resolved Hide resolved
// The sourceChainID is the ID of the chain.
sourceChainID := ids.FromStringOrPanic("2UZWB4xjNadRcHSpXarQoCryiVdcGWoT5w1dUztNfMKkAd2hJX")
reqUptime := uint64(3486)
infoClient := info.NewClient(uri)
networkID, err := infoClient.GetNetworkID(context.Background())
if err != nil {
log.Fatalf("failed to fetch network ID: %s\n", err)
}

validatorUptime, err := messages.NewValidatorUptime(validationID, reqUptime)
if err != nil {
log.Fatalf("failed to create validatorUptime message: %s\n", err)
}

addressedCall, err := payload.NewAddressedCall(
nil,
validatorUptime.Bytes(),
)
if err != nil {
log.Fatalf("failed to create AddressedCall message: %s\n", err)
}

unsignedWarp, err := warp.NewUnsignedMessage(
networkID,
sourceChainID,
addressedCall.Bytes(),
)
if err != nil {
log.Fatalf("failed to create unsigned Warp message: %s\n", err)
}

p, err := peer.StartTestPeer(
context.Background(),
netip.AddrPortFrom(
netip.AddrFrom4([4]byte{127, 0, 0, 1}),
9651,
),
networkID,
router.InboundHandlerFunc(func(_ context.Context, msg p2pmessage.InboundMessage) {
log.Printf("received %s: %s", msg.Op(), msg.Message())
}),
)
if err != nil {
log.Fatalf("failed to start peer: %s\n", err)
}

messageBuilder, err := p2pmessage.NewCreator(
logging.NoLog{},
prometheus.NewRegistry(),
compression.TypeZstd,
time.Hour,
)
if err != nil {
log.Fatalf("failed to create message builder: %s\n", err)
}

appRequestPayload, err := proto.Marshal(&sdk.SignatureRequest{
Message: unsignedWarp.Bytes(),
})
if err != nil {
log.Fatalf("failed to marshal SignatureRequest: %s\n", err)
}

appRequest, err := messageBuilder.AppRequest(
sourceChainID,
0,
time.Hour,
p2p.PrefixMessage(
p2p.ProtocolPrefix(p2p.SignatureRequestHandlerID),
appRequestPayload,
),
)
if err != nil {
log.Fatalf("failed to create AppRequest: %s\n", err)
}

p.Send(context.Background(), appRequest)

time.Sleep(5 * time.Second)

p.StartClose()
err = p.AwaitClosed(context.Background())
if err != nil {
log.Fatalf("failed to close peer: %s\n", err)
}
}
6 changes: 5 additions & 1 deletion plugin/evm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ type GetCurrentValidatorsResponse struct {
type CurrentValidator struct {
ValidationID ids.ID `json:"validationID"`
NodeID ids.NodeID `json:"nodeID"`
Weight uint64 `json:"weight"`
StartTime time.Time `json:"startTime"`
IsActive bool `json:"isActive"`
IsSoV bool `json:"isSoV"`
IsConnected bool `json:"isConnected"`
Uptime time.Duration `json:"uptime"`
}
Expand Down Expand Up @@ -63,8 +65,10 @@ func (api *ValidatorsAPI) GetCurrentValidators(_ *http.Request, args *GetCurrent
reply.Validators = append(reply.Validators, CurrentValidator{
ValidationID: validator.ValidationID,
NodeID: nodeID,
StartTime: validator.StartTime,
StartTime: validator.StartTime(),
Weight: validator.Weight,
IsActive: validator.IsActive,
IsSoV: validator.IsSoV,
IsConnected: isConnected,
Uptime: time.Duration(uptime.Seconds()),
})
Expand Down
2 changes: 1 addition & 1 deletion plugin/evm/validators/locked_state_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (s *lockedStateReader) GetNodeIDs() set.Set[ids.NodeID] {
return s.s.GetNodeIDs()
}

func (s *lockedStateReader) GetValidator(nodeID ids.NodeID) (*ValidatorOutput, error) {
func (s *lockedStateReader) GetValidator(nodeID ids.NodeID) (*Validator, error) {
s.lock.Lock()
defer s.lock.Unlock()

Expand Down
4 changes: 2 additions & 2 deletions plugin/evm/validators/noop_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (n *noOpState) GetValidationIDs() set.Set[ids.ID] { return set.NewSet[ids.I

func (n *noOpState) GetNodeIDs() set.Set[ids.NodeID] { return set.NewSet[ids.NodeID](0) }

func (n *noOpState) GetValidator(nodeID ids.NodeID) (*ValidatorOutput, error) {
func (n *noOpState) GetValidator(nodeID ids.NodeID) (*Validator, error) {
return nil, nil
}

Expand Down Expand Up @@ -45,7 +45,7 @@ func (n *noOpState) GetStartTime(
return time.Time{}, nil
}

func (n *noOpState) AddValidator(vID ids.ID, nodeID ids.NodeID, startTimestamp uint64, isActive bool) error {
func (n *noOpState) AddValidator(vdr Validator) error {
return nil
}

Expand Down
47 changes: 23 additions & 24 deletions plugin/evm/validators/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type StateReader interface {
// GetNodeIDs returns the validator node IDs in the state
GetNodeIDs() set.Set[ids.NodeID]
// GetValidator returns the validator data for the given nodeID
GetValidator(nodeID ids.NodeID) (*ValidatorOutput, error)
GetValidator(nodeID ids.NodeID) (*Validator, error)
// GetNodeID returns the node ID for the given validation ID
GetNodeID(vID ids.ID) (ids.NodeID, error)
}
Expand All @@ -41,7 +41,7 @@ type State interface {
uptime.State
StateReader
// AddValidator adds a new validator to the state
AddValidator(vID ids.ID, nodeID ids.NodeID, startTimestamp uint64, isActive bool) error
AddValidator(vdr Validator) error
// DeleteValidator deletes the validator from the state
DeleteValidator(vID ids.ID) error
// WriteState writes the validator state to the disk
Expand All @@ -64,19 +64,14 @@ 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"`
NodeID ids.NodeID `serialize:"true"`
Weight uint64 `serialize:"true"`
StartTime uint64 `serialize:"true"`
IsActive bool `serialize:"true"`
IsSoV bool `serialize:"true"`

validationID ids.ID // database key
}
Expand Down Expand Up @@ -144,23 +139,25 @@ func (s *state) GetStartTime(nodeID ids.NodeID) (time.Time, error) {

// AddValidator adds a new validator to the state
// the new validator is marked as updated and will be written to the disk when WriteState is called
func (s *state) AddValidator(vID ids.ID, nodeID ids.NodeID, startTimestamp uint64, isActive bool) error {
func (s *state) AddValidator(vdr Validator) error {
data := &validatorData{
NodeID: nodeID,
validationID: vID,
IsActive: isActive,
StartTime: startTimestamp,
NodeID: vdr.NodeID,
validationID: vdr.ValidationID,
IsActive: vdr.IsActive,
StartTime: vdr.StartTimestamp,
UpDuration: 0,
LastUpdated: startTimestamp,
LastUpdated: vdr.StartTimestamp,
IsSoV: vdr.IsSoV,
Weight: vdr.Weight,
}
if err := s.addData(vID, data); err != nil {
if err := s.addData(vdr.ValidationID, data); err != nil {
return err
}

s.updatedData[vID] = updated
s.updatedData[vdr.ValidationID] = updated

for _, listener := range s.listeners {
listener.OnValidatorAdded(vID, nodeID, startTimestamp, isActive)
listener.OnValidatorAdded(vdr.ValidationID, vdr.NodeID, vdr.StartTimestamp, vdr.IsActive)
}
return nil
}
Expand Down Expand Up @@ -256,16 +253,18 @@ func (s *state) GetNodeIDs() set.Set[ids.NodeID] {
}

// GetValidator returns the validator data for the given nodeID
func (s *state) GetValidator(nodeID ids.NodeID) (*ValidatorOutput, error) {
func (s *state) GetValidator(nodeID ids.NodeID) (*Validator, 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,
return &Validator{
ValidationID: data.validationID,
NodeID: data.NodeID,
StartTimestamp: data.StartTime,
IsActive: data.IsActive,
Weight: data.Weight,
IsSoV: data.IsSoV,
}, nil
}

Expand Down
Loading
Loading