From c23a522d25472a7258f7425bc98e76d13331cfe4 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 6 Mar 2020 20:38:17 +0300 Subject: [PATCH] rpc: implement getunclaimed closes #712 --- docs/rpc.md | 65 ++++++++++++++-------------- pkg/rpc/response/result/unclaimed.go | 49 +++++++++++++++++++++ pkg/rpc/server/prometheus.go | 9 ++++ pkg/rpc/server/server.go | 23 ++++++++++ pkg/rpc/server/server_test.go | 26 +++++++++++ 5 files changed, 139 insertions(+), 33 deletions(-) create mode 100644 pkg/rpc/response/result/unclaimed.go diff --git a/docs/rpc.md b/docs/rpc.md index bc94144c3..9cae46a16 100644 --- a/docs/rpc.md +++ b/docs/rpc.md @@ -30,41 +30,40 @@ which would yield the response: } } ``` - ### Supported methods -| Method | Implemented | -| ------- | ------------| -| `getaccountstate` | Yes | -| `getapplicationlog` | Yes | -| `getassetstate` | Yes | -| `getbestblockhash` | Yes | -| `getblock` | Yes | -| `getblockcount` | Yes | -| `getblockhash` | Yes | -| `getblockheader` | Yes | -| `getblocksysfee` | Yes | -| `getclaimable` | Yes | -| `getconnectioncount` | Yes | -| `getcontractstate` | Yes | -| `getnep5balances` | Yes | -| `getnep5transfers` | Yes | -| `getpeers` | Yes | -| `getrawmempool` | Yes | -| `getrawtransaction` | Yes | -| `getstorage` | Yes | -| `gettransactionheight` | Yes | -| `gettxout` | Yes | -| `getunclaimed` | No (#712) | -| `getunspents` | Yes | -| `getvalidators` | Yes | -| `getversion` | Yes | -| `invoke` | Yes | -| `invokefunction` | Yes | -| `invokescript` | Yes | -| `sendrawtransaction` | Yes | -| `submitblock` | Yes | -| `validateaddress` | Yes | +| Method | +| ------- | +| `getaccountstate` | +| `getapplicationlog` | +| `getassetstate` | +| `getbestblockhash` | +| `getblock` | +| `getblockcount` | +| `getblockhash` | +| `getblockheader` | +| `getblocksysfee` | +| `getclaimable` | +| `getconnectioncount` | +| `getcontractstate` | +| `getnep5balances` | +| `getnep5transfers` | +| `getpeers` | +| `getrawmempool` | +| `getrawtransaction` | +| `getstorage` | +| `gettransactionheight` | +| `gettxout` | +| `getunclaimed` | +| `getunspents` | +| `getvalidators` | +| `getversion` | +| `invoke` | +| `invokefunction` | +| `invokescript` | +| `sendrawtransaction` | +| `submitblock` | +| `validateaddress` | ### Unsupported methods diff --git a/pkg/rpc/response/result/unclaimed.go b/pkg/rpc/response/result/unclaimed.go new file mode 100644 index 000000000..05a7bf950 --- /dev/null +++ b/pkg/rpc/response/result/unclaimed.go @@ -0,0 +1,49 @@ +package result + +import ( + "github.com/nspcc-dev/neo-go/pkg/core" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// Unclaimed wrapper is used to represent getunclaimed return result. +type Unclaimed struct { + Available util.Fixed8 `json:"available"` + Unavailable util.Fixed8 `json:"unavailable"` + Unclaimed util.Fixed8 `json:"unclaimed"` +} + +// NewUnclaimed creates a new Unclaimed wrapper using given Blockchainer. +func NewUnclaimed(a *state.Account, chain core.Blockchainer) (*Unclaimed, error) { + var ( + available util.Fixed8 + unavailable util.Fixed8 + ) + + for _, ucb := range a.Unclaimed { + gen, sys, err := chain.CalculateClaimable(ucb.Value, ucb.Start, ucb.End) + if err != nil { + return nil, err + } + available += gen + sys + } + + blockHeight := chain.BlockHeight() + for _, usb := range a.Balances[core.GoverningTokenID()] { + _, txHeight, err := chain.GetTransaction(usb.Tx) + if err != nil { + return nil, err + } + gen, sys, err := chain.CalculateClaimable(usb.Value, txHeight, blockHeight) + if err != nil { + return nil, err + } + unavailable += gen + sys + } + + return &Unclaimed{ + Available: available, + Unavailable: unavailable, + Unclaimed: available + unavailable, + }, nil +} diff --git a/pkg/rpc/server/prometheus.go b/pkg/rpc/server/prometheus.go index d0c76cc11..014973daf 100644 --- a/pkg/rpc/server/prometheus.go +++ b/pkg/rpc/server/prometheus.go @@ -178,6 +178,14 @@ var ( }, ) + getunclaimedCalled = prometheus.NewCounter( + prometheus.CounterOpts{ + Help: "Number of calls to getunclaimed rpc endpoint", + Name: "getunclaimed_called", + Namespace: "neogo", + }, + ) + getunspentsCalled = prometheus.NewCounter( prometheus.CounterOpts{ Help: "Number of calls to getunspents rpc endpoint", @@ -229,6 +237,7 @@ func init() { validateaddressCalled, getassetstateCalled, getaccountstateCalled, + getunclaimedCalled, getunspentsCalled, gettransactionheightCalled, gettxoutCalled, diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index b94977c41..05c7c2a43 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -298,6 +298,10 @@ Methods: gettxoutCalled.Inc() results, resultsErr = s.getTxOut(reqParams) + case "getunclaimed": + getunclaimedCalled.Inc() + results, resultsErr = s.getUnclaimed(reqParams) + case "getunspents": getunspentsCalled.Inc() results, resultsErr = s.getAccountState(reqParams, true) @@ -768,6 +772,25 @@ func (s *Server) getBlockHeader(reqParams request.Params) (interface{}, error) { return hex.EncodeToString(buf.Bytes()), nil } +// getUnclaimed returns unclaimed GAS amount of the specified address. +func (s *Server) getUnclaimed(ps request.Params) (interface{}, error) { + p, ok := ps.ValueWithType(0, request.StringT) + if !ok { + return nil, response.ErrInvalidParams + } + u, err := p.GetUint160FromAddress() + if err != nil { + return nil, response.ErrInvalidParams + } + + acc := s.chain.GetAccountState(u) + if acc == nil { + return nil, response.NewInternalServerError("unknown account", nil) + } + + return result.NewUnclaimed(acc, s.chain) +} + // getValidators returns the current NEO consensus nodes information and voting status. func (s *Server) getValidators() (interface{}, error) { var validators keys.PublicKeys diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index a685135d0..b3a2a77af 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -601,6 +601,32 @@ var rpcTestCases = map[string][]rpcTestCase{ fail: true, }, }, + "getunclaimed": { + { + name: "no params", + params: "[]", + fail: true, + }, + { + name: "invalid address", + params: `["invalid"]`, + fail: true, + }, + { + name: "positive", + params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`, + result: func(*executor) interface{} { + return &result.Unclaimed{} + }, + check: func(t *testing.T, e *executor, uncl interface{}) { + res, ok := uncl.(*result.Unclaimed) + require.True(t, ok) + assert.Equal(t, res.Available, util.Fixed8FromInt64(8)) + assert.True(t, res.Unavailable > 0) + assert.Equal(t, res.Available + res.Unavailable, res.Unclaimed) + }, + }, + }, "getunspents": { { name: "positive",