Skip to content

Commit

Permalink
rpc: implement getcandidates call, fix #2571
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-khimov committed Jul 1, 2022
1 parent 0da0bb2 commit 039fcda
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 10 deletions.
18 changes: 8 additions & 10 deletions cli/query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func queryCandidates(ctx *cli.Context) error {
return cli.NewExitError(err, 1)
}

vals, err := c.GetNextBlockValidators()
vals, err := c.GetCandidates()
if err != nil {
return cli.NewExitError(err, 1)
}
Expand All @@ -177,21 +177,19 @@ func queryCandidates(ctx *cli.Context) error {
}

sort.Slice(vals, func(i, j int) bool {
/*
if vals[i].Active != vals[j].Active {
return vals[i].Active
}
if vals[i].Votes != vals[j].Votes {
return vals[i].Votes > vals[j].Votes
}
*/
if vals[i].Active != vals[j].Active {
return vals[i].Active
}
if vals[i].Votes != vals[j].Votes {
return vals[i].Votes > vals[j].Votes
}
return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1
})
buf := bytes.NewBuffer(nil)
tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
_, _ = tw.Write([]byte("Key\tVotes\tCommittee\tConsensus\n"))
for _, val := range vals {
_, _ = tw.Write([]byte(fmt.Sprintf("%s\t%d\t%t\t%t\n", hex.EncodeToString(val.PublicKey.Bytes()), val.Votes, comm.Contains(&val.PublicKey), true)))
_, _ = tw.Write([]byte(fmt.Sprintf("%s\t%d\t%t\t%t\n", hex.EncodeToString(val.PublicKey.Bytes()), val.Votes, comm.Contains(&val.PublicKey), val.Active)))
}
_ = tw.Flush()
fmt.Fprint(ctx.App.Writer, buf.String())
Expand Down
1 change: 1 addition & 0 deletions docs/rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ which would yield the response:
| `getblockhash` |
| `getblockheader` |
| `getblockheadercount` |
| `getcandidates` |
| `getcommittee` |
| `getconnectioncount` |
| `getcontractstate` |
Expand Down
13 changes: 13 additions & 0 deletions pkg/rpc/client/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,19 @@ func (c *Client) GetUnclaimedGas(address string) (result.UnclaimedGas, error) {
return resp, nil
}

// GetCandidates returns the current list of NEO candidate node with voting data and
// validator status.
func (c *Client) GetCandidates() ([]result.Candidate, error) {
var (
params = request.NewRawParams()
resp = new([]result.Candidate)
)
if err := c.performRequest("getcandidates", params, resp); err != nil {
return nil, err
}
return *resp, nil
}

// GetNextBlockValidators returns the current NEO consensus nodes information and voting data.
func (c *Client) GetNextBlockValidators() ([]result.Validator, error) {
var (
Expand Down
21 changes: 21 additions & 0 deletions pkg/rpc/client/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,21 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
},
},
},
"getcandidates": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetCandidates()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}`,
result: func(c *Client) interface{} { return []result.Candidate{} },
check: func(t *testing.T, c *Client, uns interface{}) {
res, ok := uns.([]result.Candidate)
require.True(t, ok)
assert.Equal(t, 4, len(res))
},
},
},
"getvalidators": {
{
name: "positive",
Expand Down Expand Up @@ -1657,6 +1672,12 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
return c.GetUnclaimedGas("")
},
},
{
name: "getcandidates_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetCandidates()
},
},
{
name: "getvalidators_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
Expand Down
8 changes: 8 additions & 0 deletions pkg/rpc/response/result/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ type Validator struct {
Votes int64 `json:"votes"`
}

// Candidate represents a node participating in the governance elections, it's
// active when it's a validator (consensus node).
type Candidate struct {
PublicKey keys.PublicKey `json:"publickey"`
Votes int64 `json:"votes,string"`
Active bool `json:"active"`
}

type newValidator struct {
PublicKey keys.PublicKey `json:"publickey"`
Votes int64 `json:"votes"`
Expand Down
24 changes: 24 additions & 0 deletions pkg/rpc/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ var rpcHandlers = map[string]func(*Server, request.Params) (interface{}, *respon
"getblockheader": (*Server).getBlockHeader,
"getblockheadercount": (*Server).getBlockHeaderCount,
"getblocksysfee": (*Server).getBlockSysFee,
"getcandidates": (*Server).getCandidates,
"getcommittee": (*Server).getCommittee,
"getconnectioncount": (*Server).getConnectionCount,
"getcontractstate": (*Server).getContractState,
Expand Down Expand Up @@ -1538,6 +1539,29 @@ func (s *Server) getUnclaimedGas(ps request.Params) (interface{}, *response.Erro
}, nil
}

// getCandidates returns the current list of candidates with their active/inactive voting status.
func (s *Server) getCandidates(_ request.Params) (interface{}, *response.Error) {
var validators keys.PublicKeys

validators, err := s.chain.GetNextBlockValidators()
if err != nil {
return nil, response.NewRPCError("Can't get next block validators", err.Error())
}
enrollments, err := s.chain.GetEnrollments()
if err != nil {
return nil, response.NewRPCError("Can't get enrollments", err.Error())
}
var res = make([]result.Candidate, 0)
for _, v := range enrollments {
res = append(res, result.Candidate{
PublicKey: *v.Key,
Votes: v.Votes.Int64(),
Active: validators.Contains(v.Key),
})
}
return res, nil
}

// getNextBlockValidators returns validators for the next block with voting status.
func (s *Server) getNextBlockValidators(_ request.Params) (interface{}, *response.Error) {
var validators keys.PublicKeys
Expand Down
8 changes: 8 additions & 0 deletions pkg/rpc/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,14 @@ var rpcTestCases = map[string][]rpcTestCase{
},
},
},
"getcandidates": {
{
params: "[]",
result: func(*executor) interface{} {
return &[]result.Candidate{}
},
},
},
"getnextblockvalidators": {
{
params: "[]",
Expand Down

0 comments on commit 039fcda

Please sign in to comment.