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. // Tuning parameters.
const ( const (
headerBatchCount = 2000 headerBatchCount = 2000
version = "0.0.3" version = "0.0.4"
// This one comes from C# code and it's different from the constant used // 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 // 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()) { 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 spentCoin.items[input.PrevIndex] = block.Index
if err = processTXWithValidatorsSubtract(&prevTXOutput, account, cache); err != nil { if err = processTXWithValidatorsSubtract(&prevTXOutput, account, cache); err != nil {
return err return err
@ -570,6 +577,37 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
} }
break 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) delete(scs.items, input.PrevIndex)
if len(scs.items) > 0 { if len(scs.items) > 0 {
if err = cache.PutSpentCoinState(input.PrevHash, scs); err != nil { if err = cache.PutSpentCoinState(input.PrevHash, scs); err != nil {

View file

@ -14,6 +14,16 @@ type UnspentBalance struct {
Value util.Fixed8 `json:"value"` 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). // UnspentBalances is a slice of UnspentBalance (mostly needed to sort them).
type UnspentBalances []UnspentBalance type UnspentBalances []UnspentBalance
@ -24,6 +34,7 @@ type Account struct {
IsFrozen bool IsFrozen bool
Votes []*keys.PublicKey Votes []*keys.PublicKey
Balances map[util.Uint256][]UnspentBalance Balances map[util.Uint256][]UnspentBalance
Unclaimed []UnclaimedBalance
} }
// NewAccount returns a new Account object. // NewAccount returns a new Account object.
@ -34,6 +45,7 @@ func NewAccount(scriptHash util.Uint160) *Account {
IsFrozen: false, IsFrozen: false,
Votes: []*keys.PublicKey{}, Votes: []*keys.PublicKey{},
Balances: make(map[util.Uint256][]UnspentBalance), Balances: make(map[util.Uint256][]UnspentBalance),
Unclaimed: []UnclaimedBalance{},
} }
} }
@ -56,6 +68,8 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
} }
s.Balances[key] = ubs s.Balances[key] = ubs
} }
br.ReadArray(&s.Unclaimed)
} }
// EncodeBinary encodes Account to the given BinWriter. // EncodeBinary encodes Account to the given BinWriter.
@ -73,6 +87,8 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
v[i].EncodeBinary(bw) v[i].EncodeBinary(bw)
} }
} }
bw.WriteArray(s.Unclaimed)
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
@ -89,6 +105,24 @@ func (u *UnspentBalance) EncodeBinary(w *io.BinWriter) {
u.Value.EncodeBinary(w) 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 // GetBalanceValues sums all unspent outputs and returns a map of asset IDs to
// overall balances. // overall balances.
func (s *Account) GetBalanceValues() map[util.Uint256]util.Fixed8 { func (s *Account) GetBalanceValues() map[util.Uint256]util.Fixed8 {