diff --git a/docs/rpc.md b/docs/rpc.md index 31b956316..bc94144c3 100644 --- a/docs/rpc.md +++ b/docs/rpc.md @@ -57,7 +57,7 @@ which would yield the response: | `gettxout` | Yes | | `getunclaimed` | No (#712) | | `getunspents` | Yes | -| `getvalidators` | No (#714) | +| `getvalidators` | Yes | | `getversion` | Yes | | `invoke` | Yes | | `invokefunction` | Yes | diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 3fbd5cd20..0bf1926cb 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1804,6 +1804,40 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P return result, nil } +// GetEnrollments returns all registered validators and non-registered SB validators +func (bc *Blockchain) GetEnrollments() ([]*state.Validator, error) { + validators := bc.dao.GetValidators() + standByValidators, err := bc.GetStandByValidators() + if err != nil { + return nil, err + } + uniqueSBValidators := standByValidators.Unique() + + var result []*state.Validator + for _, validator := range validators { + if validator.Registered { + result = append(result, validator) + } + } + for _, sBValidator := range uniqueSBValidators { + isAdded := false + for _, v := range result { + if v.PublicKey == sBValidator { + isAdded = true + break + } + } + if !isAdded { + result = append(result, &state.Validator{ + PublicKey: sBValidator, + Registered: false, + Votes: 0, + }) + } + } + return result, nil +} + func processStateTX(dao *cachedDao, tx *transaction.StateTX) error { for _, desc := range tx.Descriptors { switch desc.Type { diff --git a/pkg/core/blockchainer.go b/pkg/core/blockchainer.go index 0c2aa0065..b38089076 100644 --- a/pkg/core/blockchainer.go +++ b/pkg/core/blockchainer.go @@ -25,6 +25,7 @@ type Blockchainer interface { HeaderHeight() uint32 GetBlock(hash util.Uint256) (*block.Block, error) GetContractState(hash util.Uint160) *state.Contract + GetEnrollments() ([]*state.Validator, error) GetHeaderHash(int) util.Uint256 GetHeader(hash util.Uint256) (*block.Header, error) CurrentHeaderHash() util.Uint256 diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index ff2edbc4b..985cfde4b 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -98,6 +98,9 @@ func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog { func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) { panic("TODO") } +func (chain testChain) GetEnrollments() ([]*state.Validator, error) { + panic("TODO") +} func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) { panic("TODO") } diff --git a/pkg/rpc/response/result/validator.go b/pkg/rpc/response/result/validator.go new file mode 100644 index 000000000..19c5fefda --- /dev/null +++ b/pkg/rpc/response/result/validator.go @@ -0,0 +1,14 @@ +package result + +import ( + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// Validator used for the representation of +// state.Validator on the RPC Server. +type Validator struct { + PublicKey keys.PublicKey `json:"publickey"` + Votes util.Fixed8 `json:"votes"` + Active bool `json:"active"` +} diff --git a/pkg/rpc/server/prometheus.go b/pkg/rpc/server/prometheus.go index a7b06f452..d0c76cc11 100644 --- a/pkg/rpc/server/prometheus.go +++ b/pkg/rpc/server/prometheus.go @@ -82,6 +82,13 @@ var ( Namespace: "neogo", }, ) + getvalidatorsCalled = prometheus.NewCounter( + prometheus.CounterOpts{ + Help: "Number of calls to getvalidators rpc endpoint", + Name: "getvalidators_called", + Namespace: "neogo", + }, + ) getnep5balancesCalled = prometheus.NewCounter( prometheus.CounterOpts{ @@ -215,6 +222,7 @@ func init() { getblocksysfeeCalled, getconnectioncountCalled, getcontractstateCalled, + getvalidatorsCalled, getversionCalled, getpeersCalled, getrawmempoolCalled, diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 030c031ba..b94977c41 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -16,6 +16,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/network" @@ -214,6 +215,9 @@ Methods: case "getnep5transfers": getnep5transfersCalled.Inc() results, resultsErr = s.getNEP5Transfers(reqParams) + case "getvalidators": + getvalidatorsCalled.Inc() + results, resultsErr = s.getValidators() case "getversion": getversionCalled.Inc() @@ -764,6 +768,29 @@ func (s *Server) getBlockHeader(reqParams request.Params) (interface{}, error) { return hex.EncodeToString(buf.Bytes()), nil } +// getValidators returns the current NEO consensus nodes information and voting status. +func (s *Server) getValidators() (interface{}, error) { + var validators keys.PublicKeys + + validators, err := s.chain.GetValidators() + if err != nil { + return nil, err + } + enrollments, err := s.chain.GetEnrollments() + if err != nil { + return nil, err + } + var res []result.Validator + for _, v := range enrollments { + res = append(res, result.Validator{ + PublicKey: *v.PublicKey, + Votes: v.Votes, + Active: validators.Contains(v.PublicKey), + }) + } + return res, nil +} + // invoke implements the `invoke` RPC call. func (s *Server) invoke(reqParams request.Params) (interface{}, error) { scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT) diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 06a780c1c..a685135d0 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -624,6 +624,31 @@ var rpcTestCases = map[string][]rpcTestCase{ }, }, }, + "getvalidators": { + { + params: "[]", + result: func(*executor) interface{} { + return &[]result.Validator{} + }, + check: func(t *testing.T, e *executor, validators interface{}) { + var expected []result.Validator + sBValidators, err := e.chain.GetStandByValidators() + require.NoError(t, err) + for _, sbValidator := range sBValidators { + expected = append(expected, result.Validator{ + PublicKey: *sbValidator, + Votes: 0, + Active: true, + }) + } + + actual, ok := validators.(*[]result.Validator) + require.True(t, ok) + + assert.ElementsMatch(t, expected, *actual) + }, + }, + }, "getversion": { { params: "[]",