core: verify Claim transactions

This commit is contained in:
Evgenii Stratonikov 2020-02-27 15:45:52 +03:00
parent 9d5841df38
commit 45b8669b42
2 changed files with 94 additions and 0 deletions

View file

@ -1268,11 +1268,97 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
if bc.dao.IsDoubleClaim(claim) {
return errors.New("double claim")
}
if err := bc.verifyClaims(t); err != nil {
return err
}
}
return bc.verifyTxWitnesses(t, block)
}
func (bc *Blockchain) verifyClaims(tx *transaction.Transaction) (err error) {
t := tx.Data.(*transaction.ClaimTX)
var result *transaction.Result
results := bc.GetTransactionResults(tx)
for i := range results {
if results[i].AssetID == UtilityTokenID() {
result = results[i]
break
}
}
if result == nil || result.Amount.GreaterThan(0) {
return errors.New("invalid output in claim tx")
}
bonus, err := bc.calculateBonus(t.Claims)
if err == nil && bonus != -result.Amount {
return fmt.Errorf("wrong bonus calculated in claim tx: %s != %s",
bonus.String(), (-result.Amount).String())
}
return err
}
func (bc *Blockchain) calculateBonus(claims []transaction.Input) (util.Fixed8, error) {
unclaimed := []*spentCoin{}
inputs := transaction.GroupInputsByPrevHash(claims)
for _, group := range inputs {
h := group[0].PrevHash
claimable, err := bc.getUnclaimed(h)
if err != nil || len(claimable) == 0 {
return 0, errors.New("no unclaimed inputs")
}
for _, c := range group {
s, ok := claimable[c.PrevIndex]
if !ok {
return 0, fmt.Errorf("can't find spent coins for %s (%d)", c.PrevHash.StringLE(), c.PrevIndex)
}
unclaimed = append(unclaimed, s)
}
}
return bc.calculateBonusInternal(unclaimed)
}
func (bc *Blockchain) calculateBonusInternal(scs []*spentCoin) (util.Fixed8, error) {
var claimed util.Fixed8
for _, sc := range scs {
gen, sys, err := bc.CalculateClaimable(sc.Output.Amount, sc.StartHeight, sc.EndHeight)
if err != nil {
return 0, err
}
claimed += gen + sys
}
return claimed, nil
}
func (bc *Blockchain) getUnclaimed(h util.Uint256) (map[uint16]*spentCoin, error) {
tx, txHeight, err := bc.GetTransaction(h)
if err != nil {
return nil, err
}
scs, err := bc.dao.GetSpentCoinState(h)
if err != nil {
return nil, err
}
result := make(map[uint16]*spentCoin)
for i, height := range scs.items {
result[i] = &spentCoin{
Output: &tx.Outputs[i],
StartHeight: txHeight,
EndHeight: height,
}
}
return result, nil
}
// isTxStillRelevant is a callback for mempool transaction filtering after the
// new block addition. It returns false for transactions already present in the
// chain (added by the new block), transactions using some inputs that are

View file

@ -1,6 +1,7 @@
package core
import (
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util"
)
@ -14,6 +15,13 @@ type SpentCoinState struct {
items map[uint16]uint32
}
// spentCoin represents the state of a single spent coin output.
type spentCoin struct {
Output *transaction.Output
StartHeight uint32
EndHeight uint32
}
// NewSpentCoinState returns a new SpentCoinState object.
func NewSpentCoinState(hash util.Uint256, height uint32) *SpentCoinState {
return &SpentCoinState{