core: implement UnclaimedBalance tracking

To make it easy to get unclaimed coins for the
specified account they must be tracked together.
This commit is contained in:
Evgenii Stratonikov 2020-02-25 17:29:37 +03:00
parent 7095ec6c51
commit 95d9f36c98
2 changed files with 73 additions and 1 deletions

View file

@ -29,7 +29,7 @@ import (
// Tuning parameters.
const (
headerBatchCount = 2000
version = "0.0.3"
version = "0.0.4"
// This one comes from C# code and it's different from the constant used
// when creating an asset with Neo.Asset.Create interop call. It looks
@ -479,6 +479,13 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
}
if prevTXOutput.AssetID.Equals(GoverningTokenID()) {
account.Unclaimed = append(account.Unclaimed, state.UnclaimedBalance{
Tx: prevTX.Hash(),
Index: input.PrevIndex,
Start: prevTXHeight,
End: block.Index,
Value: prevTXOutput.Amount,
})
spentCoin.items[input.PrevIndex] = block.Index
if err = processTXWithValidatorsSubtract(&prevTXOutput, account, cache); err != nil {
return err
@ -570,6 +577,37 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
}
break
}
prevTx, _, err := cache.GetTransaction(input.PrevHash)
if err != nil {
return err
} else if int(input.PrevIndex) > len(prevTx.Outputs) {
return errors.New("invalid input in claim")
}
acc, err := cache.GetAccountState(prevTx.Outputs[input.PrevIndex].ScriptHash)
if err != nil {
return err
}
var changed bool
for i := range acc.Unclaimed {
if acc.Unclaimed[i].Tx == input.PrevHash && acc.Unclaimed[i].Index == input.PrevIndex {
copy(acc.Unclaimed[i:], acc.Unclaimed[i+1:])
acc.Unclaimed = acc.Unclaimed[:len(acc.Unclaimed)-1]
changed = true
break
}
}
if !changed {
bc.log.Warn("no spent coin in the account",
zap.String("tx", tx.Hash().StringLE()),
zap.String("input", input.PrevHash.StringLE()),
zap.String("account", acc.ScriptHash.String()))
} else if err := cache.PutAccountState(acc); err != nil {
return err
}
delete(scs.items, input.PrevIndex)
if len(scs.items) > 0 {
if err = cache.PutSpentCoinState(input.PrevHash, scs); err != nil {

View file

@ -14,6 +14,16 @@ type UnspentBalance struct {
Value util.Fixed8 `json:"value"`
}
// UnclaimedBalance represents transaction output which was spent and
// can be claimed.
type UnclaimedBalance struct {
Tx util.Uint256
Index uint16
Start uint32
End uint32
Value util.Fixed8
}
// UnspentBalances is a slice of UnspentBalance (mostly needed to sort them).
type UnspentBalances []UnspentBalance
@ -24,6 +34,7 @@ type Account struct {
IsFrozen bool
Votes []*keys.PublicKey
Balances map[util.Uint256][]UnspentBalance
Unclaimed []UnclaimedBalance
}
// NewAccount returns a new Account object.
@ -34,6 +45,7 @@ func NewAccount(scriptHash util.Uint160) *Account {
IsFrozen: false,
Votes: []*keys.PublicKey{},
Balances: make(map[util.Uint256][]UnspentBalance),
Unclaimed: []UnclaimedBalance{},
}
}
@ -56,6 +68,8 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
}
s.Balances[key] = ubs
}
br.ReadArray(&s.Unclaimed)
}
// EncodeBinary encodes Account to the given BinWriter.
@ -73,6 +87,8 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
v[i].EncodeBinary(bw)
}
}
bw.WriteArray(s.Unclaimed)
}
// DecodeBinary implements io.Serializable interface.
@ -89,6 +105,24 @@ func (u *UnspentBalance) EncodeBinary(w *io.BinWriter) {
u.Value.EncodeBinary(w)
}
// DecodeBinary implements io.Serializable interface.
func (u *UnclaimedBalance) DecodeBinary(r *io.BinReader) {
u.Tx.DecodeBinary(r)
u.Index = r.ReadU16LE()
u.Start = r.ReadU32LE()
u.End = r.ReadU32LE()
u.Value.DecodeBinary(r)
}
// EncodeBinary implements io.Serializable interface.
func (u *UnclaimedBalance) EncodeBinary(w *io.BinWriter) {
u.Tx.EncodeBinary(w)
w.WriteU16LE(u.Index)
w.WriteU32LE(u.Start)
w.WriteU32LE(u.End)
u.Value.EncodeBinary(w)
}
// GetBalanceValues sums all unspent outputs and returns a map of asset IDs to
// overall balances.
func (s *Account) GetBalanceValues() map[util.Uint256]util.Fixed8 {