From 770c8d774c6642b90be67322541d6e2556e5acb0 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 21 Sep 2020 15:34:04 +0300 Subject: [PATCH] core, rpc: add GetCommittee method Closes #1414 --- pkg/core/blockchain.go | 11 +++++++++++ pkg/core/blockchainer/blockchainer.go | 1 + pkg/network/helper_test.go | 3 +++ pkg/rpc/client/rpc.go | 13 +++++++++++++ pkg/rpc/client/rpc_test.go | 24 ++++++++++++++++++++++++ pkg/rpc/server/server.go | 10 ++++++++++ pkg/rpc/server/server_test.go | 12 ++++++++++++ 7 files changed, 74 insertions(+) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 5d0c55d31..b6582c6a4 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math/big" + "sort" "sync" "sync/atomic" "time" @@ -1403,6 +1404,16 @@ func (bc *Blockchain) GetStandByCommittee() keys.PublicKeys { return bc.sbCommittee.Copy() } +// GetCommittee returns the sorted list of public keys of nodes in committee. +func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) { + pubs, err := bc.contracts.NEO.GetCommitteeMembers(bc, bc.dao) + if err != nil { + return nil, err + } + sort.Sort(pubs) + return pubs, nil +} + // GetValidators returns current validators. func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) { return bc.contracts.NEO.GetValidatorsInternal(bc, bc.dao) diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index dfbd4b39a..2fae4c2b5 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -26,6 +26,7 @@ type Blockchainer interface { Close() HeaderHeight() uint32 GetBlock(hash util.Uint256) (*block.Block, error) + GetCommittee() (keys.PublicKeys, error) GetContractState(hash util.Uint160) *state.Contract GetContractScriptHash(id int32) (util.Uint160, error) GetEnrollments() ([]state.Validator, error) diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index dd9473ed6..d8c41f64c 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -76,6 +76,9 @@ func (chain testChain) GetAppExecResult(hash util.Uint256) (*state.AppExecResult func (chain testChain) GetBlock(hash util.Uint256) (*block.Block, error) { panic("TODO") } +func (chain testChain) GetCommittee() (keys.PublicKeys, error) { + panic("TODO") +} func (chain testChain) GetContractState(hash util.Uint160) *state.Contract { panic("TODO") } diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index 0d8ca01ff..d7039c2ec 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "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/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" @@ -179,6 +180,18 @@ func (c *Client) GetConnectionCount() (int, error) { return resp, nil } +// GetCommittee returns the current public keys of NEO nodes in committee. +func (c *Client) GetCommittee() (keys.PublicKeys, error) { + var ( + params = request.NewRawParams() + resp = new(keys.PublicKeys) + ) + if err := c.performRequest("getcommittee", params, resp); err != nil { + return nil, err + } + return *resp, nil +} + // GetContractState queries contract information, according to the contract script hash. func (c *Client) GetContractState(hash util.Uint160) (*state.Contract, error) { var ( diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 01e04b077..0c0212fa1 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -19,6 +19,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/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/rpc/request" @@ -30,6 +31,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -284,6 +286,22 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ }, }, }, + "getcommittee": { + { + name: "positive", + invoke: func(c *Client) (interface{}, error) { + return c.GetCommittee() + }, + serverResponse: `{"jsonrpc":"2.0","id":1,"result":["02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e"]}`, + result: func(c *Client) interface{} { + member, err := keys.NewPublicKeyFromString("02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e") + if err != nil { + panic(errors.Wrap(err, "failed to decode public key")) + } + return keys.PublicKeys{member} + }, + }, + }, "getconnectioncount": { { name: "positive", @@ -1068,6 +1086,12 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ return c.GetBlockSysFee(1) }, }, + { + name: "getcommittee_unmarshalling_error", + invoke: func(c *Client) (interface{}, error) { + return c.GetCommittee() + }, + }, { name: "getconnectioncount_unmarshalling_error", invoke: func(c *Client) (interface{}, error) { diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 2ff4d216c..d9708b289 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -87,6 +87,7 @@ var rpcHandlers = map[string]func(*Server, request.Params) (interface{}, *respon "getblockhash": (*Server).getBlockHash, "getblockheader": (*Server).getBlockHeader, "getblocksysfee": (*Server).getBlockSysFee, + "getcommittee": (*Server).getCommittee, "getconnectioncount": (*Server).getConnectionCount, "getcontractstate": (*Server).getContractState, "getnep5balances": (*Server).getNEP5Balances, @@ -881,6 +882,15 @@ func (s *Server) getValidators(_ request.Params) (interface{}, *response.Error) return res, nil } +// getCommittee returns the current list of NEO committee members +func (s *Server) getCommittee(_ request.Params) (interface{}, *response.Error) { + keys, err := s.chain.GetCommittee() + if err != nil { + return nil, response.NewInternalServerError("can't get committee members", err) + } + return keys, nil +} + // invokeFunction implements the `invokeFunction` RPC call. func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *response.Error) { scriptHash, err := reqParams.ValueWithType(0, request.StringT).GetUint160FromHex() diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index adc1d7cd0..295de0640 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -10,6 +10,7 @@ import ( "net/http" "net/http/httptest" "reflect" + "sort" "strconv" "strings" "testing" @@ -353,6 +354,17 @@ var rpcTestCases = map[string][]rpcTestCase{ fail: true, }, }, + "getcommittee": { + { + params: "[]", + result: func(e *executor) interface{} { + // it's a test chain, so committee is a sorted standby committee + expected := e.chain.GetStandByCommittee() + sort.Sort(expected) + return &expected + }, + }, + }, "getconnectioncount": { { params: "[]",