rpc: make getnextvalidators behavior compliant with C# node

Turns out, our getnextvalidators implementation already works the way
getcandidates is supposed to work, but original getnextvalidators works a bit
differently. It only returns validators, it doesn't return Active flag (all
of them are active) and it represents votes as a number. So for the maximum
compatibility:
 * drop non-validator keys from getnextvalidators server-side
 * drop Active flag client-side (sorry, it doesn't exist)
 * allow unmarshalling old answers along with the new one

This technically breaks `query candidates` CLI command, but it'll be fixed
when getcandidates are to be introduced.
This commit is contained in:
Roman Khimov 2022-07-01 12:34:43 +03:00
parent 2f55070a57
commit 0da0bb21ee
5 changed files with 69 additions and 13 deletions

View file

@ -177,19 +177,21 @@ func queryCandidates(ctx *cli.Context) error {
} }
sort.Slice(vals, func(i, j int) bool { sort.Slice(vals, func(i, j int) bool {
if vals[i].Active != vals[j].Active { /*
return vals[i].Active 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].Votes != vals[j].Votes {
} return vals[i].Votes > vals[j].Votes
}
*/
return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1 return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1
}) })
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0) tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
_, _ = tw.Write([]byte("Key\tVotes\tCommittee\tConsensus\n")) _, _ = tw.Write([]byte("Key\tVotes\tCommittee\tConsensus\n"))
for _, val := range vals { 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), val.Active))) _, _ = 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.Flush() _ = tw.Flush()
fmt.Fprint(ctx.App.Writer, buf.String()) fmt.Fprint(ctx.App.Writer, buf.String())

View file

@ -557,7 +557,7 @@ func (c *Client) GetUnclaimedGas(address string) (result.UnclaimedGas, error) {
return resp, nil return resp, nil
} }
// GetNextBlockValidators returns the current NEO consensus nodes information and voting status. // GetNextBlockValidators returns the current NEO consensus nodes information and voting data.
func (c *Client) GetNextBlockValidators() ([]result.Validator, error) { func (c *Client) GetNextBlockValidators() ([]result.Validator, error) {
var ( var (
params = request.NewRawParams() params = request.NewRawParams()

View file

@ -1,13 +1,43 @@
package result package result
import ( import (
"encoding/json"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
) )
// Validator used for the representation of // Validator is used for the representation of consensus node data in the JSON-RPC
// state.Validator on the RPC Server. // protocol.
type Validator struct { type Validator struct {
PublicKey keys.PublicKey `json:"publickey"` PublicKey keys.PublicKey `json:"publickey"`
Votes int64 `json:"votes,string"` Votes int64 `json:"votes"`
Active bool `json:"active"` }
type newValidator struct {
PublicKey keys.PublicKey `json:"publickey"`
Votes int64 `json:"votes"`
}
type oldValidator struct {
PublicKey keys.PublicKey `json:"publickey"`
Votes int64 `json:"votes,string"`
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (v *Validator) UnmarshalJSON(data []byte) error {
var nv = new(newValidator)
err := json.Unmarshal(data, nv)
if err != nil {
var ov = new(oldValidator)
err := json.Unmarshal(data, ov)
if err != nil {
return err
}
v.PublicKey = ov.PublicKey
v.Votes = ov.Votes
return nil
}
v.PublicKey = nv.PublicKey
v.Votes = nv.Votes
return nil
} }

View file

@ -0,0 +1,22 @@
package result
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func TestValidatorUnmarshal(t *testing.T) {
old := []byte(`{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"100500","active":true}`)
v := new(Validator)
require.NoError(t, json.Unmarshal(old, v))
require.Equal(t, int64(100500), v.Votes)
new := []byte(`{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":42}`)
require.NoError(t, json.Unmarshal(new, v))
require.Equal(t, int64(42), v.Votes)
bad := []byte(`{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"notanumber"}`)
require.Error(t, json.Unmarshal(bad, v))
}

View file

@ -1552,10 +1552,12 @@ func (s *Server) getNextBlockValidators(_ request.Params) (interface{}, *respons
} }
var res = make([]result.Validator, 0) var res = make([]result.Validator, 0)
for _, v := range enrollments { for _, v := range enrollments {
if !validators.Contains(v.Key) {
continue
}
res = append(res, result.Validator{ res = append(res, result.Validator{
PublicKey: *v.Key, PublicKey: *v.Key,
Votes: v.Votes.Int64(), Votes: v.Votes.Int64(),
Active: validators.Contains(v.Key),
}) })
} }
return res, nil return res, nil