core: implement (*Blockchain).CalculateClaimable

Calculating amount of GAS that can be claimed is required
for getclaimable RPC.
This commit is contained in:
Evgenii Stratonikov 2020-02-25 16:15:17 +03:00
parent 252a9f2f31
commit 7095ec6c51
4 changed files with 99 additions and 0 deletions

View file

@ -93,6 +93,9 @@ type Blockchain struct {
// Number of headers stored in the chain file.
storedHeaderCount uint32
generationAmount []int
decrementInterval int
// All operations on headerList must be called from an
// headersOp to be routine safe.
headerList *HeaderHashList
@ -154,6 +157,9 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
memPool: mempool.NewMemPool(cfg.MemPoolSize),
keyCache: make(map[util.Uint160]map[string]*keys.PublicKey),
log: log,
generationAmount: genAmount,
decrementInterval: decrementInterval,
}
if err := bc.init(); err != nil {
@ -1033,6 +1039,55 @@ func (bc *Blockchain) GetConfig() config.ProtocolConfiguration {
return bc.config
}
// CalculateClaimable calculates the amount of GAS which can be claimed for a transaction with value.
// First return value is GAS generated between startHeight and endHeight.
// Second return value is GAS returned from accumulated SystemFees between startHeight and endHeight.
func (bc *Blockchain) CalculateClaimable(value util.Fixed8, startHeight, endHeight uint32) (util.Fixed8, util.Fixed8, error) {
var amount util.Fixed8
di := uint32(bc.decrementInterval)
ustart := startHeight / di
if genSize := uint32(len(bc.generationAmount)); ustart < genSize {
uend := endHeight / di
iend := endHeight % di
if uend >= genSize {
uend = genSize - 1
iend = di
} else if iend == 0 {
uend--
iend = di
}
istart := startHeight % di
for ustart < uend {
amount += util.Fixed8(di-istart) * util.Fixed8(bc.generationAmount[ustart])
ustart++
istart = 0
}
amount += util.Fixed8(iend-istart) * util.Fixed8(bc.generationAmount[ustart])
}
var sysFeeTotal util.Fixed8
if startHeight == 0 {
startHeight++
}
for i := startHeight; i < endHeight; i++ {
h := bc.GetHeaderHash(int(i))
b, err := bc.GetBlock(h)
if err != nil {
return 0, 0, err
}
for _, tx := range b.Transactions {
sysFeeTotal += bc.SystemFee(tx)
}
}
sysFeeTotal /= 100000000
ratio := value / 100000000
return amount * ratio, sysFeeTotal * ratio, nil
}
// References maps transaction's inputs into a slice of InOuts, effectively
// joining each Input with the corresponding Output.
// @TODO: unfortunately we couldn't attach this method to the Transaction struct in the

View file

@ -175,6 +175,46 @@ func TestGetTransaction(t *testing.T) {
}
}
func TestGetClaimable(t *testing.T) {
bc := newTestChain(t)
_, _, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 0, 2)
require.Error(t, err)
bc.generationAmount = []int{4, 3, 2, 1}
bc.decrementInterval = 2
_, err = bc.genBlocks(10)
require.NoError(t, err)
t.Run("first generation period", func(t *testing.T) {
amount, sysfee, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 0, 2)
require.NoError(t, err)
require.EqualValues(t, 8, amount)
require.EqualValues(t, 0, sysfee)
})
t.Run("a number of full periods", func(t *testing.T) {
amount, sysfee, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 0, 6)
require.NoError(t, err)
require.EqualValues(t, 4+4+3+3+2+2, amount)
require.EqualValues(t, 0, sysfee)
})
t.Run("start from the 2-nd block", func(t *testing.T) {
amount, sysfee, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 1, 7)
require.NoError(t, err)
require.EqualValues(t, 4+3+3+2+2+1, amount)
require.EqualValues(t, 0, sysfee)
})
t.Run("end height after generation has ended", func(t *testing.T) {
amount, sysfee, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 1, 10)
require.NoError(t, err)
require.EqualValues(t, 4+3+3+2+2+1+1, amount)
require.EqualValues(t, 0, sysfee)
})
}
func TestClose(t *testing.T) {
defer func() {
r := recover()

View file

@ -20,6 +20,7 @@ type Blockchainer interface {
AddHeaders(...*block.Header) error
AddBlock(*block.Block) error
BlockHeight() uint32
CalculateClaimable(value util.Fixed8, startHeight, endHeight uint32) (util.Fixed8, util.Fixed8, error)
Close()
HeaderHeight() uint32
GetBlock(hash util.Uint256) (*block.Block, error)

View file

@ -32,6 +32,9 @@ func (chain testChain) ApplyPolicyToTxSet([]mempool.TxWithFee) []mempool.TxWithF
func (chain testChain) GetConfig() config.ProtocolConfiguration {
panic("TODO")
}
func (chain testChain) CalculateClaimable(util.Fixed8, uint32, uint32) (util.Fixed8, util.Fixed8, error) {
panic("TODO")
}
func (chain testChain) References(t *transaction.Transaction) ([]transaction.InOut, error) {
panic("TODO")