diff --git a/api/admin/service.go b/api/admin/service.go index 0718dfd..3d61730 100644 --- a/api/admin/service.go +++ b/api/admin/service.go @@ -10,129 +10,35 @@ import ( "github.com/ava-labs/gecko/api" "github.com/ava-labs/gecko/chains" - "github.com/ava-labs/gecko/genesis" - "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/network" "github.com/ava-labs/gecko/snow/engine/common" "github.com/ava-labs/gecko/utils/logging" - "github.com/ava-labs/gecko/version" cjson "github.com/ava-labs/gecko/utils/json" ) // Admin is the API service for node admin management type Admin struct { - version version.Version - nodeID ids.ShortID - networkID uint32 log logging.Logger - networking network.Network performance Performance chainManager chains.Manager httpServer *api.Server } // NewService returns a new admin API service -func NewService(version version.Version, nodeID ids.ShortID, networkID uint32, log logging.Logger, chainManager chains.Manager, peers network.Network, httpServer *api.Server) *common.HTTPHandler { +func NewService(log logging.Logger, chainManager chains.Manager, peers network.Network, httpServer *api.Server) *common.HTTPHandler { newServer := rpc.NewServer() codec := cjson.NewCodec() newServer.RegisterCodec(codec, "application/json") newServer.RegisterCodec(codec, "application/json;charset=UTF-8") newServer.RegisterService(&Admin{ - version: version, - nodeID: nodeID, - networkID: networkID, log: log, chainManager: chainManager, - networking: peers, httpServer: httpServer, }, "admin") return &common.HTTPHandler{Handler: newServer} } -// GetNodeVersionReply are the results from calling GetNodeVersion -type GetNodeVersionReply struct { - Version string `json:"version"` -} - -// GetNodeVersion returns the version this node is running -func (service *Admin) GetNodeVersion(_ *http.Request, _ *struct{}, reply *GetNodeVersionReply) error { - service.log.Info("Admin: GetNodeVersion called") - - reply.Version = service.version.String() - return nil -} - -// GetNodeIDReply are the results from calling GetNodeID -type GetNodeIDReply struct { - NodeID ids.ShortID `json:"nodeID"` -} - -// GetNodeID returns the node ID of this node -func (service *Admin) GetNodeID(_ *http.Request, _ *struct{}, reply *GetNodeIDReply) error { - service.log.Info("Admin: GetNodeID called") - - reply.NodeID = service.nodeID - return nil -} - -// GetNetworkIDReply are the results from calling GetNetworkID -type GetNetworkIDReply struct { - NetworkID cjson.Uint32 `json:"networkID"` -} - -// GetNetworkID returns the network ID this node is running on -func (service *Admin) GetNetworkID(_ *http.Request, _ *struct{}, reply *GetNetworkIDReply) error { - service.log.Info("Admin: GetNetworkID called") - - reply.NetworkID = cjson.Uint32(service.networkID) - return nil -} - -// GetNetworkNameReply is the result from calling GetNetworkName -type GetNetworkNameReply struct { - NetworkName string `json:"networkName"` -} - -// GetNetworkName returns the network name this node is running on -func (service *Admin) GetNetworkName(_ *http.Request, _ *struct{}, reply *GetNetworkNameReply) error { - service.log.Info("Admin: GetNetworkName called") - - reply.NetworkName = genesis.NetworkName(service.networkID) - return nil -} - -// GetBlockchainIDArgs are the arguments for calling GetBlockchainID -type GetBlockchainIDArgs struct { - Alias string `json:"alias"` -} - -// GetBlockchainIDReply are the results from calling GetBlockchainID -type GetBlockchainIDReply struct { - BlockchainID string `json:"blockchainID"` -} - -// GetBlockchainID returns the blockchain ID that resolves the alias that was supplied -func (service *Admin) GetBlockchainID(_ *http.Request, args *GetBlockchainIDArgs, reply *GetBlockchainIDReply) error { - service.log.Info("Admin: GetBlockchainID called") - - bID, err := service.chainManager.Lookup(args.Alias) - reply.BlockchainID = bID.String() - return err -} - -// PeersReply are the results from calling Peers -type PeersReply struct { - Peers []network.PeerID `json:"peers"` -} - -// Peers returns the list of current validators -func (service *Admin) Peers(_ *http.Request, _ *struct{}, reply *PeersReply) error { - service.log.Info("Admin: Peers called") - reply.Peers = service.networking.Peers() - return nil -} - // StartCPUProfilerArgs are the arguments for calling StartCPUProfiler type StartCPUProfilerArgs struct { Filename string `json:"filename"` diff --git a/api/health/service.go b/api/health/service.go index fdd405b..1989ab3 100644 --- a/api/health/service.go +++ b/api/health/service.go @@ -7,11 +7,13 @@ import ( "net/http" "time" - "github.com/AppsFlyer/go-sundheit" + health "github.com/AppsFlyer/go-sundheit" + + "github.com/gorilla/rpc/v2" + "github.com/ava-labs/gecko/snow/engine/common" "github.com/ava-labs/gecko/utils/json" "github.com/ava-labs/gecko/utils/logging" - "github.com/gorilla/rpc/v2" ) // defaultCheckOpts is a Check whose properties represent a default Check diff --git a/api/info/service.go b/api/info/service.go new file mode 100644 index 0000000..2c903e2 --- /dev/null +++ b/api/info/service.go @@ -0,0 +1,131 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package info + +import ( + "net/http" + + "github.com/gorilla/rpc/v2" + + "github.com/ava-labs/gecko/chains" + "github.com/ava-labs/gecko/genesis" + "github.com/ava-labs/gecko/ids" + "github.com/ava-labs/gecko/network" + "github.com/ava-labs/gecko/snow/engine/common" + "github.com/ava-labs/gecko/utils/logging" + "github.com/ava-labs/gecko/version" + + cjson "github.com/ava-labs/gecko/utils/json" +) + +// Info is the API service for unprivileged info on a node +type Info struct { + version version.Version + nodeID ids.ShortID + networkID uint32 + log logging.Logger + networking network.Network + chainManager chains.Manager +} + +// NewService returns a new admin API service +func NewService(log logging.Logger, version version.Version, nodeID ids.ShortID, networkID uint32, chainManager chains.Manager, peers network.Network) *common.HTTPHandler { + newServer := rpc.NewServer() + codec := cjson.NewCodec() + newServer.RegisterCodec(codec, "application/json") + newServer.RegisterCodec(codec, "application/json;charset=UTF-8") + newServer.RegisterService(&Info{ + version: version, + nodeID: nodeID, + networkID: networkID, + log: log, + chainManager: chainManager, + networking: peers, + }, "info") + return &common.HTTPHandler{Handler: newServer} +} + +// GetNodeVersionReply are the results from calling GetNodeVersion +type GetNodeVersionReply struct { + Version string `json:"version"` +} + +// GetNodeVersion returns the version this node is running +func (service *Info) GetNodeVersion(_ *http.Request, _ *struct{}, reply *GetNodeVersionReply) error { + service.log.Info("Info: GetNodeVersion called") + + reply.Version = service.version.String() + return nil +} + +// GetNodeIDReply are the results from calling GetNodeID +type GetNodeIDReply struct { + NodeID ids.ShortID `json:"nodeID"` +} + +// GetNodeID returns the node ID of this node +func (service *Info) GetNodeID(_ *http.Request, _ *struct{}, reply *GetNodeIDReply) error { + service.log.Info("Info: GetNodeID called") + + reply.NodeID = service.nodeID + return nil +} + +// GetNetworkIDReply are the results from calling GetNetworkID +type GetNetworkIDReply struct { + NetworkID cjson.Uint32 `json:"networkID"` +} + +// GetNetworkID returns the network ID this node is running on +func (service *Info) GetNetworkID(_ *http.Request, _ *struct{}, reply *GetNetworkIDReply) error { + service.log.Info("Info: GetNetworkID called") + + reply.NetworkID = cjson.Uint32(service.networkID) + return nil +} + +// GetNetworkNameReply is the result from calling GetNetworkName +type GetNetworkNameReply struct { + NetworkName string `json:"networkName"` +} + +// GetNetworkName returns the network name this node is running on +func (service *Info) GetNetworkName(_ *http.Request, _ *struct{}, reply *GetNetworkNameReply) error { + service.log.Info("Info: GetNetworkName called") + + reply.NetworkName = genesis.NetworkName(service.networkID) + return nil +} + +// GetBlockchainIDArgs are the arguments for calling GetBlockchainID +type GetBlockchainIDArgs struct { + Alias string `json:"alias"` +} + +// GetBlockchainIDReply are the results from calling GetBlockchainID +type GetBlockchainIDReply struct { + BlockchainID string `json:"blockchainID"` +} + +// GetBlockchainID returns the blockchain ID that resolves the alias that was supplied +func (service *Info) GetBlockchainID(_ *http.Request, args *GetBlockchainIDArgs, reply *GetBlockchainIDReply) error { + service.log.Info("Info: GetBlockchainID called") + + bID, err := service.chainManager.Lookup(args.Alias) + reply.BlockchainID = bID.String() + return err +} + +// PeersReply are the results from calling Peers +type PeersReply struct { + Peers []network.PeerID `json:"peers"` +} + +// Peers returns the list of current validators +func (service *Info) Peers(_ *http.Request, _ *struct{}, reply *PeersReply) error { + service.log.Info("Info: Peers called") + + reply.Peers = service.networking.Peers() + return nil +} diff --git a/api/keystore/service.go b/api/keystore/service.go index ac9e4e6..e6979fa 100644 --- a/api/keystore/service.go +++ b/api/keystore/service.go @@ -12,6 +12,8 @@ import ( "github.com/gorilla/rpc/v2" + zxcvbn "github.com/nbutton23/zxcvbn-go" + "github.com/ava-labs/gecko/chains/atomic" "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database/encdb" @@ -19,12 +21,11 @@ import ( "github.com/ava-labs/gecko/database/prefixdb" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow/engine/common" + "github.com/ava-labs/gecko/utils/codec" "github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/gecko/utils/logging" - "github.com/ava-labs/gecko/utils/codec" jsoncodec "github.com/ava-labs/gecko/utils/json" - zxcvbn "github.com/nbutton23/zxcvbn-go" ) const ( diff --git a/api/metrics/service.go b/api/metrics/service.go index 5fa9206..463456a 100644 --- a/api/metrics/service.go +++ b/api/metrics/service.go @@ -4,9 +4,10 @@ package metrics import ( - "github.com/ava-labs/gecko/snow/engine/common" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/ava-labs/gecko/snow/engine/common" ) // NewService returns a new prometheus service diff --git a/chains/atomic/memory.go b/chains/atomic/memory.go index 9774711..778b9e5 100644 --- a/chains/atomic/memory.go +++ b/chains/atomic/memory.go @@ -10,9 +10,9 @@ import ( "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database/prefixdb" "github.com/ava-labs/gecko/ids" + "github.com/ava-labs/gecko/utils/codec" "github.com/ava-labs/gecko/utils/hashing" "github.com/ava-labs/gecko/utils/logging" - "github.com/ava-labs/gecko/utils/codec" ) type rcLock struct { diff --git a/database/encdb/db.go b/database/encdb/db.go index 4814805..fe33fa7 100644 --- a/database/encdb/db.go +++ b/database/encdb/db.go @@ -13,8 +13,8 @@ import ( "github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database/nodb" "github.com/ava-labs/gecko/utils" - "github.com/ava-labs/gecko/utils/hashing" "github.com/ava-labs/gecko/utils/codec" + "github.com/ava-labs/gecko/utils/hashing" ) // Database encrypts all values that are provided diff --git a/database/leveldb/db.go b/database/leveldb/db.go index edcb4be..7055d61 100644 --- a/database/leveldb/db.go +++ b/database/leveldb/db.go @@ -6,14 +6,15 @@ package leveldb import ( "bytes" - "github.com/ava-labs/gecko/database" - "github.com/ava-labs/gecko/utils" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/util" + + "github.com/ava-labs/gecko/database" + "github.com/ava-labs/gecko/utils" ) const ( diff --git a/database/nodb/db.go b/database/nodb/db.go index 3f1bceb..9a7525f 100644 --- a/database/nodb/db.go +++ b/database/nodb/db.go @@ -17,7 +17,7 @@ func (*Database) Has([]byte) (bool, error) { return false, database.ErrClosed } func (*Database) Get([]byte) ([]byte, error) { return nil, database.ErrClosed } // Put returns nil -func (*Database) Put(_ []byte, _ []byte) error { return database.ErrClosed } +func (*Database) Put(_, _ []byte) error { return database.ErrClosed } // Delete returns nil func (*Database) Delete([]byte) error { return database.ErrClosed } diff --git a/database/rpcdb/db_client.go b/database/rpcdb/db_client.go index 67af7ef..dc3f60b 100644 --- a/database/rpcdb/db_client.go +++ b/database/rpcdb/db_client.go @@ -27,7 +27,7 @@ func NewClient(client rpcdbproto.DatabaseClient) *DatabaseClient { return &DatabaseClient{client: client} } -// Has returns false, nil +// Has attempts to return if the database has a key with the provided value. func (db *DatabaseClient) Has(key []byte) (bool, error) { resp, err := db.client.Has(context.Background(), &rpcdbproto.HasRequest{ Key: key, @@ -38,7 +38,7 @@ func (db *DatabaseClient) Has(key []byte) (bool, error) { return resp.Has, nil } -// Get returns nil, error +// Get attempts to return the value that was mapped to the key that was provided func (db *DatabaseClient) Get(key []byte) ([]byte, error) { resp, err := db.client.Get(context.Background(), &rpcdbproto.GetRequest{ Key: key, @@ -49,7 +49,7 @@ func (db *DatabaseClient) Get(key []byte) ([]byte, error) { return resp.Value, nil } -// Put returns nil +// Put attempts to set the value this key maps to func (db *DatabaseClient) Put(key, value []byte) error { _, err := db.client.Put(context.Background(), &rpcdbproto.PutRequest{ Key: key, @@ -58,7 +58,7 @@ func (db *DatabaseClient) Put(key, value []byte) error { return updateError(err) } -// Delete returns nil +// Delete attempts to remove any mapping from the key func (db *DatabaseClient) Delete(key []byte) error { _, err := db.client.Delete(context.Background(), &rpcdbproto.DeleteRequest{ Key: key, @@ -99,7 +99,7 @@ func (db *DatabaseClient) NewIteratorWithStartAndPrefix(start, prefix []byte) da } } -// Stat returns an error +// Stat attempts to return the statistic of this database func (db *DatabaseClient) Stat(property string) (string, error) { resp, err := db.client.Stat(context.Background(), &rpcdbproto.StatRequest{ Property: property, @@ -110,7 +110,7 @@ func (db *DatabaseClient) Stat(property string) (string, error) { return resp.Stat, nil } -// Compact returns nil +// Compact attempts to optimize the space utilization in the provided range func (db *DatabaseClient) Compact(start, limit []byte) error { _, err := db.client.Compact(context.Background(), &rpcdbproto.CompactRequest{ Start: start, @@ -119,7 +119,7 @@ func (db *DatabaseClient) Compact(start, limit []byte) error { return updateError(err) } -// Close returns nil +// Close attempts to close the database func (db *DatabaseClient) Close() error { _, err := db.client.Close(context.Background(), &rpcdbproto.CloseRequest{}) return updateError(err) @@ -207,7 +207,8 @@ type iterator struct { err error } -// Next returns false +// Next attempts to move the iterator to the next element and returns if this +// succeeded func (it *iterator) Next() bool { resp, err := it.db.client.IteratorNext(context.Background(), &rpcdbproto.IteratorNextRequest{ Id: it.id, @@ -221,7 +222,7 @@ func (it *iterator) Next() bool { return resp.FoundNext } -// Error returns any errors +// Error returns any that occurred while iterating func (it *iterator) Error() error { if it.err != nil { return it.err @@ -234,19 +235,21 @@ func (it *iterator) Error() error { return it.err } -// Key returns nil +// Key returns the key of the current element func (it *iterator) Key() []byte { return it.key } -// Value returns nil +// Value returns the value of the current element func (it *iterator) Value() []byte { return it.value } -// Release does nothing +// Release frees any resources held by the iterator func (it *iterator) Release() { it.db.client.IteratorRelease(context.Background(), &rpcdbproto.IteratorReleaseRequest{ Id: it.id, }) } +// updateError sets the error value to the errors required by the Database +// interface func updateError(err error) error { if err == nil { return nil diff --git a/database/rpcdb/db_server.go b/database/rpcdb/db_server.go index fe4b47a..cc32db8 100644 --- a/database/rpcdb/db_server.go +++ b/database/rpcdb/db_server.go @@ -34,16 +34,16 @@ func NewServer(db database.Database) *DatabaseServer { } } -// Has ... +// Has delegates the Has call to the managed database and returns the result func (db *DatabaseServer) Has(_ context.Context, req *rpcdbproto.HasRequest) (*rpcdbproto.HasResponse, error) { has, err := db.db.Has(req.Key) if err != nil { return nil, err } - return &rpcdbproto.HasResponse{Has: has}, nil + return &rpcdbproto.HasResponse{Has: has}, err } -// Get ... +// Get delegates the Get call to the managed database and returns the result func (db *DatabaseServer) Get(_ context.Context, req *rpcdbproto.GetRequest) (*rpcdbproto.GetResponse, error) { value, err := db.db.Get(req.Key) if err != nil { @@ -52,17 +52,18 @@ func (db *DatabaseServer) Get(_ context.Context, req *rpcdbproto.GetRequest) (*r return &rpcdbproto.GetResponse{Value: value}, nil } -// Put ... +// Put delegates the Put call to the managed database and returns the result func (db *DatabaseServer) Put(_ context.Context, req *rpcdbproto.PutRequest) (*rpcdbproto.PutResponse, error) { return &rpcdbproto.PutResponse{}, db.db.Put(req.Key, req.Value) } -// Delete ... +// Delete delegates the Delete call to the managed database and returns the +// result func (db *DatabaseServer) Delete(_ context.Context, req *rpcdbproto.DeleteRequest) (*rpcdbproto.DeleteResponse, error) { return &rpcdbproto.DeleteResponse{}, db.db.Delete(req.Key) } -// Stat ... +// Stat delegates the Stat call to the managed database and returns the result func (db *DatabaseServer) Stat(_ context.Context, req *rpcdbproto.StatRequest) (*rpcdbproto.StatResponse, error) { stat, err := db.db.Stat(req.Property) if err != nil { @@ -71,17 +72,19 @@ func (db *DatabaseServer) Stat(_ context.Context, req *rpcdbproto.StatRequest) ( return &rpcdbproto.StatResponse{Stat: stat}, nil } -// Compact ... +// Compact delegates the Compact call to the managed database and returns the +// result func (db *DatabaseServer) Compact(_ context.Context, req *rpcdbproto.CompactRequest) (*rpcdbproto.CompactResponse, error) { return &rpcdbproto.CompactResponse{}, db.db.Compact(req.Start, req.Limit) } -// Close ... -func (db *DatabaseServer) Close(_ context.Context, _ *rpcdbproto.CloseRequest) (*rpcdbproto.CloseResponse, error) { +// Close delegates the Close call to the managed database and returns the result +func (db *DatabaseServer) Close(context.Context, *rpcdbproto.CloseRequest) (*rpcdbproto.CloseResponse, error) { return &rpcdbproto.CloseResponse{}, db.db.Close() } -// WriteBatch ... +// WriteBatch takes in a set of key-value pairs and atomically writes them to +// the internal database func (db *DatabaseServer) WriteBatch(_ context.Context, req *rpcdbproto.WriteBatchRequest) (*rpcdbproto.WriteBatchResponse, error) { db.batch.Reset() @@ -100,7 +103,8 @@ func (db *DatabaseServer) WriteBatch(_ context.Context, req *rpcdbproto.WriteBat return &rpcdbproto.WriteBatchResponse{}, db.batch.Write() } -// NewIteratorWithStartAndPrefix ... +// NewIteratorWithStartAndPrefix allocates an iterator and returns the iterator +// ID func (db *DatabaseServer) NewIteratorWithStartAndPrefix(_ context.Context, req *rpcdbproto.NewIteratorWithStartAndPrefixRequest) (*rpcdbproto.NewIteratorWithStartAndPrefixResponse, error) { id := db.nextIteratorID it := db.db.NewIteratorWithStartAndPrefix(req.Start, req.Prefix) @@ -110,7 +114,7 @@ func (db *DatabaseServer) NewIteratorWithStartAndPrefix(_ context.Context, req * return &rpcdbproto.NewIteratorWithStartAndPrefixResponse{Id: id}, nil } -// IteratorNext ... +// IteratorNext attempts to call next on the requested iterator func (db *DatabaseServer) IteratorNext(_ context.Context, req *rpcdbproto.IteratorNextRequest) (*rpcdbproto.IteratorNextResponse, error) { it, exists := db.iterators[req.Id] if !exists { @@ -123,7 +127,7 @@ func (db *DatabaseServer) IteratorNext(_ context.Context, req *rpcdbproto.Iterat }, nil } -// IteratorError ... +// IteratorError attempts to report any errors that occurred during iteration func (db *DatabaseServer) IteratorError(_ context.Context, req *rpcdbproto.IteratorErrorRequest) (*rpcdbproto.IteratorErrorResponse, error) { it, exists := db.iterators[req.Id] if !exists { @@ -132,7 +136,7 @@ func (db *DatabaseServer) IteratorError(_ context.Context, req *rpcdbproto.Itera return &rpcdbproto.IteratorErrorResponse{}, it.Error() } -// IteratorRelease ... +// IteratorRelease attempts to release the resources allocated to an iterator func (db *DatabaseServer) IteratorRelease(_ context.Context, req *rpcdbproto.IteratorReleaseRequest) (*rpcdbproto.IteratorReleaseResponse, error) { it, exists := db.iterators[req.Id] if exists { diff --git a/genesis/aliases.go b/genesis/aliases.go index 80e5dcd..b1eae46 100644 --- a/genesis/aliases.go +++ b/genesis/aliases.go @@ -18,27 +18,27 @@ import ( // Aliases returns the default aliases based on the network ID func Aliases(networkID uint32) (map[string][]string, map[[32]byte][]string, map[[32]byte][]string, error) { generalAliases := map[string][]string{ - "vm/" + platformvm.ID.String(): []string{"vm/platform"}, - "vm/" + avm.ID.String(): []string{"vm/avm"}, - "vm/" + EVMID.String(): []string{"vm/evm"}, - "vm/" + spdagvm.ID.String(): []string{"vm/spdag"}, - "vm/" + spchainvm.ID.String(): []string{"vm/spchain"}, - "vm/" + timestampvm.ID.String(): []string{"vm/timestamp"}, - "bc/" + ids.Empty.String(): []string{"P", "platform", "bc/P", "bc/platform"}, + "vm/" + platformvm.ID.String(): {"vm/platform"}, + "vm/" + avm.ID.String(): {"vm/avm"}, + "vm/" + EVMID.String(): {"vm/evm"}, + "vm/" + spdagvm.ID.String(): {"vm/spdag"}, + "vm/" + spchainvm.ID.String(): {"vm/spchain"}, + "vm/" + timestampvm.ID.String(): {"vm/timestamp"}, + "bc/" + ids.Empty.String(): {"P", "platform", "bc/P", "bc/platform"}, } chainAliases := map[[32]byte][]string{ - ids.Empty.Key(): []string{"P", "platform"}, + ids.Empty.Key(): {"P", "platform"}, } vmAliases := map[[32]byte][]string{ - platformvm.ID.Key(): []string{"platform"}, - avm.ID.Key(): []string{"avm"}, - EVMID.Key(): []string{"evm"}, - spdagvm.ID.Key(): []string{"spdag"}, - spchainvm.ID.Key(): []string{"spchain"}, - timestampvm.ID.Key(): []string{"timestamp"}, - secp256k1fx.ID.Key(): []string{"secp256k1fx"}, - nftfx.ID.Key(): []string{"nftfx"}, - propertyfx.ID.Key(): []string{"propertyfx"}, + platformvm.ID.Key(): {"platform"}, + avm.ID.Key(): {"avm"}, + EVMID.Key(): {"evm"}, + spdagvm.ID.Key(): {"spdag"}, + spchainvm.ID.Key(): {"spchain"}, + timestampvm.ID.Key(): {"timestamp"}, + secp256k1fx.ID.Key(): {"secp256k1fx"}, + nftfx.ID.Key(): {"nftfx"}, + propertyfx.ID.Key(): {"propertyfx"}, } genesisBytes, err := Genesis(networkID) diff --git a/genesis/genesis.go b/genesis/genesis.go index c4245b9..f37a584 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -9,12 +9,12 @@ import ( "time" "github.com/ava-labs/gecko/ids" + "github.com/ava-labs/gecko/utils/codec" "github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/gecko/utils/json" "github.com/ava-labs/gecko/utils/units" "github.com/ava-labs/gecko/utils/wrappers" "github.com/ava-labs/gecko/vms/avm" - "github.com/ava-labs/gecko/utils/codec" "github.com/ava-labs/gecko/vms/nftfx" "github.com/ava-labs/gecko/vms/platformvm" "github.com/ava-labs/gecko/vms/propertyfx" @@ -156,7 +156,7 @@ func FromConfig(networkID uint32, config *Config) ([]byte, error) { // Specify the chains that exist upon this network's creation platformvmArgs.Chains = []platformvm.APIChain{ - platformvm.APIChain{ + { GenesisData: avmReply.Bytes, SubnetID: platformvm.DefaultSubnetID, VMID: avm.ID, @@ -167,25 +167,25 @@ func FromConfig(networkID uint32, config *Config) ([]byte, error) { }, Name: "X-Chain", }, - platformvm.APIChain{ + { GenesisData: formatting.CB58{Bytes: config.EVMBytes}, SubnetID: platformvm.DefaultSubnetID, VMID: EVMID, Name: "C-Chain", }, - platformvm.APIChain{ + { GenesisData: spdagvmReply.Bytes, SubnetID: platformvm.DefaultSubnetID, VMID: spdagvm.ID, Name: "Simple DAG Payments", }, - platformvm.APIChain{ + { GenesisData: spchainvmReply.Bytes, SubnetID: platformvm.DefaultSubnetID, VMID: spchainvm.ID, Name: "Simple Chain Payments", }, - platformvm.APIChain{ + { GenesisData: formatting.CB58{Bytes: []byte{}}, // There is no genesis data SubnetID: platformvm.DefaultSubnetID, VMID: timestampvm.ID, diff --git a/go.sum b/go.sum index d46c985..3f809ed 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -122,6 +123,7 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= @@ -177,8 +179,10 @@ github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc= github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696/go.mod h1:ym2A+zigScwkSEb/cVQB0/ZMpU3rqiH6X7WRRsxgOGw= @@ -230,6 +234,7 @@ github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8 github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -345,6 +350,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= @@ -354,6 +360,7 @@ gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLv gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9 h1:ITeyKbRetrVzqR3U1eY+ywgp7IBspGd1U/bkwd1gWu4= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/redis.v4 v4.2.4/go.mod h1:8KREHdypkCEojGKQcjMqAODMICIVwZAONWq8RowTITA= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= diff --git a/main/params.go b/main/params.go index 94db609..468a599 100644 --- a/main/params.go +++ b/main/params.go @@ -226,6 +226,7 @@ func init() { // Enable/Disable APIs: fs.BoolVar(&Config.AdminAPIEnabled, "api-admin-enabled", true, "If true, this node exposes the Admin API") + fs.BoolVar(&Config.InfoAPIEnabled, "api-info-enabled", true, "If true, this node exposes the Info API") fs.BoolVar(&Config.KeystoreAPIEnabled, "api-keystore-enabled", true, "If true, this node exposes the Keystore API") fs.BoolVar(&Config.MetricsAPIEnabled, "api-metrics-enabled", true, "If true, this node exposes the Metrics API") fs.BoolVar(&Config.HealthAPIEnabled, "api-health-enabled", true, "If true, this node exposes the Health API") diff --git a/node/config.go b/node/config.go index 2504276..b8754e9 100644 --- a/node/config.go +++ b/node/config.go @@ -51,6 +51,7 @@ type Config struct { // Enable/Disable APIs AdminAPIEnabled bool + InfoAPIEnabled bool KeystoreAPIEnabled bool MetricsAPIEnabled bool HealthAPIEnabled bool diff --git a/node/node.go b/node/node.go index 942a746..eae8cfd 100644 --- a/node/node.go +++ b/node/node.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/gecko/api" "github.com/ava-labs/gecko/api/admin" "github.com/ava-labs/gecko/api/health" + "github.com/ava-labs/gecko/api/info" "github.com/ava-labs/gecko/api/ipcs" "github.com/ava-labs/gecko/api/keystore" "github.com/ava-labs/gecko/api/metrics" @@ -461,11 +462,19 @@ func (n *Node) initMetricsAPI() { func (n *Node) initAdminAPI() { if n.Config.AdminAPIEnabled { n.Log.Info("initializing Admin API") - service := admin.NewService(Version, n.ID, n.Config.NetworkID, n.Log, n.chainManager, n.Net, &n.APIServer) + service := admin.NewService(n.Log, n.chainManager, n.Net, &n.APIServer) n.APIServer.AddRoute(service, &sync.RWMutex{}, "admin", "", n.HTTPLog) } } +func (n *Node) initInfoAPI() { + if n.Config.InfoAPIEnabled { + n.Log.Info("initializing Info API") + service := info.NewService(n.Log, Version, n.ID, n.Config.NetworkID, n.chainManager, n.Net) + n.APIServer.AddRoute(service, &sync.RWMutex{}, "info", "", n.HTTPLog) + } +} + // initHealthAPI initializes the Health API service // Assumes n.Log, n.ConsensusAPI, and n.ValidatorAPI already initialized func (n *Node) initHealthAPI() { @@ -562,6 +571,7 @@ func (n *Node) Initialize(Config *Config, logger logging.Logger, logFactory logg n.initChainManager() // Set up the chain manager n.initAdminAPI() // Start the Admin API + n.initInfoAPI() // Start the Info API n.initHealthAPI() // Start the Health API n.initIPCAPI() // Start the IPC API diff --git a/snow/consensus/snowball/parameters_test.go b/snow/consensus/snowball/parameters_test.go index 7c3668c..056e229 100644 --- a/snow/consensus/snowball/parameters_test.go +++ b/snow/consensus/snowball/parameters_test.go @@ -125,14 +125,14 @@ func TestParametersAnotherInvalidBetaRogue(t *testing.T) { func TestParametersInvalidConcurrentRepolls(t *testing.T) { tests := []Parameters{ - Parameters{ + { K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 2, }, - Parameters{ + { K: 1, Alpha: 1, BetaVirtuous: 1, diff --git a/snow/engine/avalanche/bootstrapper.go b/snow/engine/avalanche/bootstrapper.go index 705d841..d5e5e7f 100644 --- a/snow/engine/avalanche/bootstrapper.go +++ b/snow/engine/avalanche/bootstrapper.go @@ -107,6 +107,9 @@ func (b *bootstrapper) fetch(vtxID ids.ID) error { // Make sure we don't already have this vertex if _, err := b.State.GetVertex(vtxID); err == nil { + if numPending := b.outstandingRequests.Len(); numPending == 0 && b.processedStartingAcceptedFrontier { + return b.finish() + } return nil } @@ -124,15 +127,12 @@ func (b *bootstrapper) fetch(vtxID ids.ID) error { // Process vertices func (b *bootstrapper) process(vtx avalanche.Vertex) error { - toProcess := []avalanche.Vertex{vtx} - for len(toProcess) > 0 { - newLen := len(toProcess) - 1 - vtx := toProcess[newLen] - toProcess = toProcess[:newLen] - if _, ok := b.processedCache.Get(vtx.ID()); ok { // already processed this - continue - } - + toProcess := newMaxVertexHeap() + if _, ok := b.processedCache.Get(vtx.ID()); !ok { // only process if we haven't already + toProcess.Push(vtx) + } + for toProcess.Len() > 0 { + vtx := toProcess.Pop() switch vtx.Status() { case choices.Unknown: if err := b.fetch(vtx.ID()); err != nil { @@ -168,7 +168,9 @@ func (b *bootstrapper) process(vtx avalanche.Vertex) error { } } for _, parent := range vtx.Parents() { - toProcess = append(toProcess, parent) + if _, ok := b.processedCache.Get(parent.ID()); !ok { // already processed this + toProcess.Push(parent) + } } b.processedCache.Put(vtx.ID(), nil) } diff --git a/snow/engine/avalanche/bootstrapper_test.go b/snow/engine/avalanche/bootstrapper_test.go index 85e3a6e..c488d11 100644 --- a/snow/engine/avalanche/bootstrapper_test.go +++ b/snow/engine/avalanche/bootstrapper_test.go @@ -805,3 +805,113 @@ func TestBootstrapperIncompleteMultiPut(t *testing.T) { t.Fatal("should be accepted") } } + +func TestBootstrapperFinalized(t *testing.T) { + config, peerID, sender, state, vm := newConfig(t) + + vtxID0 := ids.Empty.Prefix(0) + vtxID1 := ids.Empty.Prefix(1) + + vtxBytes0 := []byte{0} + vtxBytes1 := []byte{1} + + vtx0 := &Vtx{ + id: vtxID0, + height: 0, + status: choices.Unknown, + bytes: vtxBytes0, + } + vtx1 := &Vtx{ + id: vtxID1, + height: 1, + parents: []avalanche.Vertex{vtx0}, + status: choices.Unknown, + bytes: vtxBytes1, + } + + bs := bootstrapper{} + bs.metrics.Initialize(config.Context.Log, fmt.Sprintf("gecko_%s", config.Context.ChainID), prometheus.NewRegistry()) + bs.Initialize(config) + finished := new(bool) + bs.onFinished = func() error { *finished = true; return nil } + + acceptedIDs := ids.Set{} + acceptedIDs.Add(vtxID0) + acceptedIDs.Add(vtxID1) + + parsedVtx0 := false + parsedVtx1 := false + state.getVertex = func(vtxID ids.ID) (avalanche.Vertex, error) { + switch { + case vtxID.Equals(vtxID0): + if parsedVtx0 { + return vtx0, nil + } + return nil, errUnknownVertex + case vtxID.Equals(vtxID1): + if parsedVtx1 { + return vtx1, nil + } + return nil, errUnknownVertex + default: + t.Fatal(errUnknownVertex) + panic(errUnknownVertex) + } + } + state.parseVertex = func(vtxBytes []byte) (avalanche.Vertex, error) { + switch { + case bytes.Equal(vtxBytes, vtxBytes0): + vtx0.status = choices.Processing + parsedVtx0 = true + return vtx0, nil + case bytes.Equal(vtxBytes, vtxBytes1): + vtx1.status = choices.Processing + parsedVtx1 = true + return vtx1, nil + } + t.Fatal(errUnknownVertex) + return nil, errUnknownVertex + } + + requestIDs := map[[32]byte]uint32{} + sender.GetAncestorsF = func(vdr ids.ShortID, reqID uint32, vtxID ids.ID) { + if !vdr.Equals(peerID) { + t.Fatalf("Should have requested block from %s, requested from %s", peerID, vdr) + } + requestIDs[vtxID.Key()] = reqID + } + + vm.CantBootstrapping = false + + if err := bs.ForceAccepted(acceptedIDs); err != nil { // should request vtx0 and vtx1 + t.Fatal(err) + } + + reqID, ok := requestIDs[vtxID1.Key()] + if !ok { + t.Fatalf("should have requested vtx1") + } + + vm.CantBootstrapped = false + + if err := bs.MultiPut(peerID, reqID, [][]byte{vtxBytes1, vtxBytes0}); err != nil { + t.Fatal(err) + } + + reqID, ok = requestIDs[vtxID0.Key()] + if !ok { + t.Fatalf("should have requested vtx0") + } + + if err := bs.GetAncestorsFailed(peerID, reqID); err != nil { + t.Fatal(err) + } + + if !*finished { + t.Fatalf("Bootstrapping should have finished") + } else if vtx0.Status() != choices.Accepted { + t.Fatalf("Vertex should be accepted") + } else if vtx1.Status() != choices.Accepted { + t.Fatalf("Vertex should be accepted") + } +} diff --git a/snow/engine/snowman/bootstrapper.go b/snow/engine/snowman/bootstrapper.go index b7df6b8..e811b81 100644 --- a/snow/engine/snowman/bootstrapper.go +++ b/snow/engine/snowman/bootstrapper.go @@ -115,6 +115,9 @@ func (b *bootstrapper) fetch(blkID ids.ID) error { // Make sure we don't already have this block if _, err := b.VM.GetBlock(blkID); err == nil { + if numPending := b.outstandingRequests.Len(); numPending == 0 && b.processedStartingAcceptedFrontier { + return b.finish() + } return nil } diff --git a/snow/engine/snowman/bootstrapper_test.go b/snow/engine/snowman/bootstrapper_test.go index 286d9d7..016e05c 100644 --- a/snow/engine/snowman/bootstrapper_test.go +++ b/snow/engine/snowman/bootstrapper_test.go @@ -622,3 +622,128 @@ func TestBootstrapperFilterAccepted(t *testing.T) { t.Fatalf("Blk shouldn't be accepted") } } + +func TestBootstrapperFinalized(t *testing.T) { + config, peerID, sender, vm := newConfig(t) + + blkID0 := ids.Empty.Prefix(0) + blkID1 := ids.Empty.Prefix(1) + blkID2 := ids.Empty.Prefix(2) + + blkBytes0 := []byte{0} + blkBytes1 := []byte{1} + blkBytes2 := []byte{2} + + blk0 := &Blk{ + id: blkID0, + height: 0, + status: choices.Accepted, + bytes: blkBytes0, + } + blk1 := &Blk{ + parent: blk0, + id: blkID1, + height: 1, + status: choices.Unknown, + bytes: blkBytes1, + } + blk2 := &Blk{ + parent: blk1, + id: blkID2, + height: 2, + status: choices.Unknown, + bytes: blkBytes2, + } + + bs := bootstrapper{} + bs.metrics.Initialize(config.Context.Log, fmt.Sprintf("gecko_%s", config.Context.ChainID), prometheus.NewRegistry()) + bs.Initialize(config) + finished := new(bool) + bs.onFinished = func() error { *finished = true; return nil } + + acceptedIDs := ids.Set{} + acceptedIDs.Add(blkID1) + acceptedIDs.Add(blkID2) + + parsedBlk1 := false + parsedBlk2 := false + vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) { + switch { + case blkID.Equals(blkID0): + return blk0, nil + case blkID.Equals(blkID1): + if parsedBlk1 { + return blk1, nil + } + return nil, errUnknownBlock + case blkID.Equals(blkID2): + if parsedBlk2 { + return blk2, nil + } + return nil, errUnknownBlock + default: + t.Fatal(errUnknownBlock) + panic(errUnknownBlock) + } + } + vm.ParseBlockF = func(blkBytes []byte) (snowman.Block, error) { + switch { + case bytes.Equal(blkBytes, blkBytes0): + return blk0, nil + case bytes.Equal(blkBytes, blkBytes1): + blk1.status = choices.Processing + parsedBlk1 = true + return blk1, nil + case bytes.Equal(blkBytes, blkBytes2): + blk2.status = choices.Processing + parsedBlk2 = true + return blk2, nil + } + t.Fatal(errUnknownBlock) + return nil, errUnknownBlock + } + + requestIDs := map[[32]byte]uint32{} + sender.GetAncestorsF = func(vdr ids.ShortID, reqID uint32, vtxID ids.ID) { + if !vdr.Equals(peerID) { + t.Fatalf("Should have requested block from %s, requested from %s", peerID, vdr) + } + requestIDs[vtxID.Key()] = reqID + } + + vm.CantBootstrapping = false + + if err := bs.ForceAccepted(acceptedIDs); err != nil { // should request blk0 and blk1 + t.Fatal(err) + } + + reqID, ok := requestIDs[blkID2.Key()] + if !ok { + t.Fatalf("should have requested blk2") + } + + vm.CantBootstrapped = false + + if err := bs.MultiPut(peerID, reqID, [][]byte{blkBytes2, blkBytes1}); err != nil { + t.Fatal(err) + } + + reqID, ok = requestIDs[blkID1.Key()] + if !ok { + t.Fatalf("should have requested blk1") + } + + if err := bs.GetAncestorsFailed(peerID, reqID); err != nil { + t.Fatal(err) + } + + if !*finished { + t.Fatalf("Bootstrapping should have finished") + } else if blk0.Status() != choices.Accepted { + t.Fatalf("Block should be accepted") + } else if blk1.Status() != choices.Accepted { + t.Fatalf("Block should be accepted") + } else if blk2.Status() != choices.Accepted { + t.Fatalf("Block should be accepted") + } +} diff --git a/vms/components/core/block.go b/vms/components/core/block.go index 3609477..6d1d37b 100644 --- a/vms/components/core/block.go +++ b/vms/components/core/block.go @@ -34,8 +34,7 @@ type Block struct { func (b *Block) Initialize(bytes []byte, vm *SnowmanVM) { b.VM = vm b.Metadata.Initialize(bytes) - status := b.VM.State.GetStatus(vm.DB, b.ID()) - b.SetStatus(status) + b.SetStatus(choices.Unknown) // don't set status until it is queried } // ParentID returns [b]'s parent's ID @@ -55,7 +54,6 @@ func (b *Block) Parent() snowman.Block { // Recall that b.vm.DB.Commit() must be called to persist to the DB func (b *Block) Accept() error { b.SetStatus(choices.Accepted) // Change state of this block - blkID := b.ID() // Persist data diff --git a/vms/components/core/block_test.go b/vms/components/core/block_test.go new file mode 100644 index 0000000..d9d30bc --- /dev/null +++ b/vms/components/core/block_test.go @@ -0,0 +1,48 @@ +package core + +import ( + "testing" + + "github.com/ava-labs/gecko/snow/choices" + "github.com/ava-labs/gecko/snow/consensus/snowman" + + "github.com/ava-labs/gecko/ids" + + "github.com/ava-labs/gecko/database/memdb" + "github.com/ava-labs/gecko/database/versiondb" +) + +func TestBlock(t *testing.T) { + parentID := ids.NewID([32]byte{1, 2, 3, 4, 5}) + db := versiondb.New(memdb.New()) + state, err := NewSnowmanState(func([]byte) (snowman.Block, error) { return nil, nil }) + if err != nil { + t.Fatal(err) + } + b := NewBlock(parentID) + + b.Initialize([]byte{1, 2, 3}, &SnowmanVM{ + DB: db, + State: state, + }) + + // should be unknown until someone queries for it + if status := b.Metadata.status; status != choices.Unknown { + t.Fatalf("status should be unknown but is %s", status) + } + + // querying should change status to processing + if status := b.Status(); status != choices.Processing { + t.Fatalf("status should be processing but is %s", status) + } + + b.Accept() + if status := b.Status(); status != choices.Accepted { + t.Fatalf("status should be accepted but is %s", status) + } + + b.Reject() + if status := b.Status(); status != choices.Rejected { + t.Fatalf("status should be rejected but is %s", status) + } +} diff --git a/vms/components/state/state.go b/vms/components/state/state.go index d4d0da8..24b50e3 100644 --- a/vms/components/state/state.go +++ b/vms/components/state/state.go @@ -128,19 +128,10 @@ func (s *state) Get(db database.Database, typeID uint64, key ids.ID) (interface{ // The unique ID of this key/typeID pair uID := s.uniqueID(key, typeID) - // See if exists in database - exists, err := db.Has(uID.Bytes()) - if err != nil { - return nil, err - } - if !exists { - return nil, database.ErrNotFound - } - // Get the value from the database valueBytes, err := db.Get(uID.Bytes()) if err != nil { - return nil, fmt.Errorf("problem getting value from database: %w", err) + return nil, err } // Unmarshal the value from bytes and return it diff --git a/vms/platformvm/add_default_subnet_delegator_tx.go b/vms/platformvm/add_default_subnet_delegator_tx.go index 3012d84..9881652 100644 --- a/vms/platformvm/add_default_subnet_delegator_tx.go +++ b/vms/platformvm/add_default_subnet_delegator_tx.go @@ -128,7 +128,7 @@ func (tx *addDefaultSubnetDelegatorTx) SemanticVerify(db database.Database) (*ve // The account if this block's proposal is committed and the validator is // added to the pending validator set. (Increase the account's nonce; // decrease its balance.) - newAccount, err := account.Remove(0, tx.Nonce) // Remove also removes the fee + newAccount, err := account.Remove(tx.Wght, tx.Nonce) // Remove also removes the fee if err != nil { return nil, nil, nil, nil, permError{err} } diff --git a/vms/platformvm/add_default_subnet_delegator_tx_test.go b/vms/platformvm/add_default_subnet_delegator_tx_test.go index 65a0a71..9380001 100644 --- a/vms/platformvm/add_default_subnet_delegator_tx_test.go +++ b/vms/platformvm/add_default_subnet_delegator_tx_test.go @@ -386,4 +386,52 @@ func TestAddDefaultSubnetDelegatorTxSemanticVerify(t *testing.T) { t.Fatal("should have failed verification because payer account has no $AVA to pay fee") } txFee = txFeeSaved // Reset tx fee + + // Case 8: fail verification for spending more funds than it has + tx, err = vm.newAddDefaultSubnetDelegatorTx( + defaultNonce+1, + defaultBalance*2, // weight + uint64(defaultValidateStartTime.Unix()), // start time + uint64(defaultValidateEndTime.Unix()), // end time + defaultKey.PublicKey().Address(), // node ID + defaultKey.PublicKey().Address(), // destination + testNetworkID, // network ID + defaultKey, // tx fee payer + ) + if err != nil { + t.Fatal(err) + } + _, _, _, _, err = tx.SemanticVerify(vm.DB) + if err == nil { + t.Fatal("should have failed verification because payer account spent twice the account's balance") + } + + // Case 9: Confirm balance is correct + tx, err = vm.newAddDefaultSubnetDelegatorTx( + defaultNonce+1, + defaultStakeAmount, // weight + uint64(defaultValidateStartTime.Unix()), // start time + uint64(defaultValidateEndTime.Unix()), // end time + defaultKey.PublicKey().Address(), // node ID + defaultKey.PublicKey().Address(), // destination + testNetworkID, // network ID + defaultKey, // tx fee payer + ) + if err != nil { + t.Fatal(err) + } + + onCommitDB, _, _, _, err := tx.SemanticVerify(vm.DB) + if err != nil { + t.Fatal(err) + } + account, err := tx.vm.getAccount(onCommitDB, defaultKey.PublicKey().Address()) + if err != nil { + t.Fatal(err) + } + balance := account.Balance + + if balance != defaultBalance-(defaultStakeAmount+txFee) { + t.Fatalf("balance was not updated correctly after subnet delegator tx") + } }