core: provide account in calculate claimable

`getunclaimedgas` RPC should return all GAS available to claim.
This commit is contained in:
Evgenii Stratonikov 2020-11-06 12:27:05 +03:00
parent 860c146260
commit 54992ad4f3
8 changed files with 76 additions and 38 deletions

View file

@ -172,21 +172,48 @@ func TestClaimGas(t *testing.T) {
e := newExecutor(t, true) e := newExecutor(t, true)
defer e.Close(t) defer e.Close(t)
start := e.Chain.BlockHeight() const walletPath = "testdata/testwallet.json"
balanceBefore := e.Chain.GetUtilityTokenBalance(validatorHash) w, err := wallet.NewWalletFromFile(walletPath)
require.NoError(t, err)
defer w.Close()
args := []string{
"neo-go", "wallet", "nep5", "multitransfer",
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet,
"--from", validatorAddr,
"neo:" + w.Accounts[0].Address + ":1000",
"gas:" + w.Accounts[0].Address + ":1000", // for tx send
}
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, args...)
e.checkTxPersisted(t)
h, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
balanceBefore := e.Chain.GetUtilityTokenBalance(h)
claimHeight := e.Chain.BlockHeight() + 1
cl, err := e.Chain.CalculateClaimable(h, claimHeight)
require.NoError(t, err)
require.True(t, cl.Sign() > 0)
e.In.WriteString("testpass\r")
e.Run(t, "neo-go", "wallet", "claim", e.Run(t, "neo-go", "wallet", "claim",
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", walletPath,
"--address", validatorAddr) "--address", w.Accounts[0].Address)
tx, end := e.checkTxPersisted(t) tx, height := e.checkTxPersisted(t)
b, _ := e.Chain.GetGoverningTokenBalance(validatorHash) balanceBefore.Sub(balanceBefore, big.NewInt(tx.NetworkFee+tx.SystemFee))
cl := e.Chain.CalculateClaimable(b, start, end) balanceBefore.Add(balanceBefore, cl)
require.True(t, cl.Sign() > 0)
cl.Sub(cl, big.NewInt(tx.NetworkFee+tx.SystemFee))
balanceAfter := e.Chain.GetUtilityTokenBalance(validatorHash) balanceAfter := e.Chain.GetUtilityTokenBalance(h)
require.Equal(t, 0, balanceAfter.Cmp(balanceBefore.Add(balanceBefore, cl))) // height can be bigger than claimHeight especially when tests are executed with -race.
if height == claimHeight {
require.Equal(t, 0, balanceAfter.Cmp(balanceBefore))
} else {
require.Equal(t, 1, balanceAfter.Cmp(balanceBefore))
}
} }
func TestImportDeployed(t *testing.T) { func TestImportDeployed(t *testing.T) {

View file

@ -1156,10 +1156,8 @@ func (bc *Blockchain) UnsubscribeFromExecutions(ch chan<- *state.AppExecResult)
// CalculateClaimable calculates the amount of GAS generated by owning specified // CalculateClaimable calculates the amount of GAS generated by owning specified
// amount of NEO between specified blocks. // amount of NEO between specified blocks.
func (bc *Blockchain) CalculateClaimable(value *big.Int, startHeight, endHeight uint32) *big.Int { func (bc *Blockchain) CalculateClaimable(acc util.Uint160, endHeight uint32) (*big.Int, error) {
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, nil) return bc.contracts.NEO.CalculateBonus(bc.dao, acc, endHeight)
res, _ := bc.contracts.NEO.CalculateNEOHolderReward(ic, value, startHeight, endHeight)
return res
} }
// FeePerByte returns transaction network fee per byte. // FeePerByte returns transaction network fee per byte.

View file

@ -854,8 +854,9 @@ func TestGetClaimable(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
t.Run("first generation period", func(t *testing.T) { t.Run("first generation period", func(t *testing.T) {
amount := bc.CalculateClaimable(big.NewInt(1), 0, 2) amount, err := bc.CalculateClaimable(neoOwner, 1)
require.EqualValues(t, big.NewInt(1), amount) require.NoError(t, err)
require.EqualValues(t, big.NewInt(5*native.GASFactor/10), amount)
}) })
} }

View file

@ -22,7 +22,7 @@ type Blockchainer interface {
AddHeaders(...*block.Header) error AddHeaders(...*block.Header) error
AddBlock(*block.Block) error AddBlock(*block.Block) error
AddStateRoot(r *state.MPTRoot) error AddStateRoot(r *state.MPTRoot) error
CalculateClaimable(value *big.Int, startHeight, endHeight uint32) *big.Int CalculateClaimable(h util.Uint160, endHeight uint32) (*big.Int, error)
Close() Close()
HeaderHeight() uint32 HeaderHeight() uint32
GetBlock(hash util.Uint256) (*block.Block, error) GetBlock(hash util.Uint256) (*block.Block, error)

View file

@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
@ -397,7 +398,7 @@ func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOB
if ic.Block == nil || ic.Block.Index == 0 { if ic.Block == nil || ic.Block.Index == 0 {
return nil return nil
} }
gen, err := n.CalculateBonus(ic, acc.VoteTo, &acc.Balance, acc.BalanceHeight, ic.Block.Index) gen, err := n.calculateBonus(ic.DAO, acc.VoteTo, &acc.Balance, acc.BalanceHeight, ic.Block.Index)
if err != nil { if err != nil {
return err return err
} }
@ -409,13 +410,7 @@ func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOB
func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem.Item { func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem.Item {
u := toUint160(args[0]) u := toUint160(args[0])
end := uint32(toBigInt(args[1]).Int64()) end := uint32(toBigInt(args[1]).Int64())
key := makeAccountKey(u) gen, err := n.CalculateBonus(ic.DAO, u, end)
si := ic.DAO.GetStorageItem(n.ContractID, key)
st, err := state.NEOBalanceStateFromBytes(si.Value)
if err != nil {
panic(err)
}
gen, err := n.CalculateBonus(ic, st.VoteTo, &st.Balance, st.BalanceHeight, end)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -534,14 +529,27 @@ func makeVoterKey(pub []byte, prealloc ...[]byte) []byte {
// CalculateBonus calculates amount of gas generated for holding value NEO from start to end block // CalculateBonus calculates amount of gas generated for holding value NEO from start to end block
// and having voted for active committee member. // and having voted for active committee member.
func (n *NEO) CalculateBonus(ic *interop.Context, vote *keys.PublicKey, value *big.Int, start, end uint32) (*big.Int, error) { func (n *NEO) CalculateBonus(d dao.DAO, acc util.Uint160, end uint32) (*big.Int, error) {
r, err := n.CalculateNEOHolderReward(ic, value, start, end) key := makeAccountKey(acc)
si := d.GetStorageItem(n.ContractID, key)
if si == nil {
return nil, storage.ErrKeyNotFound
}
st, err := state.NEOBalanceStateFromBytes(si.Value)
if err != nil {
return nil, err
}
return n.calculateBonus(d, st.VoteTo, &st.Balance, st.BalanceHeight, end)
}
func (n *NEO) calculateBonus(d dao.DAO, vote *keys.PublicKey, value *big.Int, start, end uint32) (*big.Int, error) {
r, err := n.CalculateNEOHolderReward(d, value, start, end)
if err != nil || vote == nil { if err != nil || vote == nil {
return r, err return r, err
} }
var key = makeVoterKey(vote.Bytes()) var key = makeVoterKey(vote.Bytes())
var reward = n.getGASPerVote(ic.DAO, key, start, end) var reward = n.getGASPerVote(d, key, start, end)
var tmp = new(big.Int).Sub(&reward[1], &reward[0]) var tmp = new(big.Int).Sub(&reward[1], &reward[0])
tmp.Mul(tmp, value) tmp.Mul(tmp, value)
tmp.Div(tmp, big.NewInt(voterRewardFactor)) tmp.Div(tmp, big.NewInt(voterRewardFactor))
@ -550,7 +558,7 @@ func (n *NEO) CalculateBonus(ic *interop.Context, vote *keys.PublicKey, value *b
} }
// CalculateNEOHolderReward return GAS reward for holding `value` of NEO from start to end block. // CalculateNEOHolderReward return GAS reward for holding `value` of NEO from start to end block.
func (n *NEO) CalculateNEOHolderReward(ic *interop.Context, value *big.Int, start, end uint32) (*big.Int, error) { func (n *NEO) CalculateNEOHolderReward(d dao.DAO, value *big.Int, start, end uint32) (*big.Int, error) {
if value.Sign() == 0 || start >= end { if value.Sign() == 0 || start >= end {
return big.NewInt(0), nil return big.NewInt(0), nil
} else if value.Sign() < 0 { } else if value.Sign() < 0 {
@ -563,7 +571,7 @@ func (n *NEO) CalculateNEOHolderReward(ic *interop.Context, value *big.Int, star
if !n.gasPerBlockChanged.Load().(bool) { if !n.gasPerBlockChanged.Load().(bool) {
gr = n.gasPerBlock.Load().(gasRecord) gr = n.gasPerBlock.Load().(gasRecord)
} else { } else {
gr, err = n.getSortedGASRecordFromDAO(ic.DAO) gr, err = n.getSortedGASRecordFromDAO(d)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -162,7 +162,8 @@ func TestNEO_Vote(t *testing.T) {
newGAS := bc.GetUtilityTokenBalance(accs[i].Contract.ScriptHash()) newGAS := bc.GetUtilityTokenBalance(accs[i].Contract.ScriptHash())
newGAS.Sub(newGAS, gasBalance[i]) newGAS.Sub(newGAS, gasBalance[i])
gasForHold := bc.CalculateClaimable(neoBalance[i], transferBlock, bc.BlockHeight()) gasForHold, err := bc.contracts.NEO.CalculateNEOHolderReward(bc.dao, neoBalance[i], transferBlock, bc.BlockHeight())
require.NoError(t, err)
newGAS.Sub(newGAS, gasForHold) newGAS.Sub(newGAS, gasForHold)
require.True(t, newGAS.Sign() > 0) require.True(t, newGAS.Sign() > 0)
gasBalance[i] = newGAS gasBalance[i] = newGAS
@ -258,11 +259,11 @@ func TestNEO_CalculateBonus(t *testing.T) {
ic.SpawnVM() ic.SpawnVM()
ic.VM.LoadScript([]byte{byte(opcode.RET)}) ic.VM.LoadScript([]byte{byte(opcode.RET)})
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
_, err := neo.CalculateNEOHolderReward(ic, new(big.Int).SetInt64(-1), 0, 1) _, err := neo.CalculateNEOHolderReward(ic.DAO, new(big.Int).SetInt64(-1), 0, 1)
require.Error(t, err) require.Error(t, err)
}) })
t.Run("Zero", func(t *testing.T) { t.Run("Zero", func(t *testing.T) {
res, err := neo.CalculateNEOHolderReward(ic, big.NewInt(0), 0, 100) res, err := neo.CalculateNEOHolderReward(ic.DAO, big.NewInt(0), 0, 100)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, 0, res.Int64()) require.EqualValues(t, 0, res.Int64())
}) })
@ -272,7 +273,7 @@ func TestNEO_CalculateBonus(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.True(t, ok) require.True(t, ok)
res, err := neo.CalculateNEOHolderReward(ic, big.NewInt(100), 5, 15) res, err := neo.CalculateNEOHolderReward(ic.DAO, big.NewInt(100), 5, 15)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, (100*5*5/10)+(100*5*1/10), res.Int64()) require.EqualValues(t, (100*5*5/10)+(100*5*1/10), res.Int64())

View file

@ -33,7 +33,7 @@ func (chain testChain) ApplyPolicyToTxSet([]*transaction.Transaction) []*transac
func (chain testChain) GetConfig() config.ProtocolConfiguration { func (chain testChain) GetConfig() config.ProtocolConfiguration {
panic("TODO") panic("TODO")
} }
func (chain testChain) CalculateClaimable(*big.Int, uint32, uint32) *big.Int { func (chain testChain) CalculateClaimable(util.Uint160, uint32) (*big.Int, error) {
panic("TODO") panic("TODO")
} }

View file

@ -951,13 +951,16 @@ func (s *Server) getUnclaimedGas(ps request.Params) (interface{}, *response.Erro
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
neo, neoHeight := s.chain.GetGoverningTokenBalance(u) neo, _ := s.chain.GetGoverningTokenBalance(u)
if neo.Sign() == 0 { if neo.Sign() == 0 {
return result.UnclaimedGas{ return result.UnclaimedGas{
Address: u, Address: u,
}, nil }, nil
} }
gas := s.chain.CalculateClaimable(neo, neoHeight, s.chain.BlockHeight()+1) // +1 as in C#, for the next block. gas, err := s.chain.CalculateClaimable(u, s.chain.BlockHeight()+1) // +1 as in C#, for the next block.
if err != nil {
return nil, response.NewInternalServerError("can't calculate claimable", err)
}
return result.UnclaimedGas{ return result.UnclaimedGas{
Address: u, Address: u,
Unclaimed: *gas, Unclaimed: *gas,