core: verify Claim transactions
This commit is contained in:
parent
9d5841df38
commit
45b8669b42
2 changed files with 94 additions and 0 deletions
|
@ -1268,11 +1268,97 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
||||||
if bc.dao.IsDoubleClaim(claim) {
|
if bc.dao.IsDoubleClaim(claim) {
|
||||||
return errors.New("double claim")
|
return errors.New("double claim")
|
||||||
}
|
}
|
||||||
|
if err := bc.verifyClaims(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bc.verifyTxWitnesses(t, block)
|
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
|
// isTxStillRelevant is a callback for mempool transaction filtering after the
|
||||||
// new block addition. It returns false for transactions already present in 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
|
// chain (added by the new block), transactions using some inputs that are
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -14,6 +15,13 @@ type SpentCoinState struct {
|
||||||
items map[uint16]uint32
|
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.
|
// NewSpentCoinState returns a new SpentCoinState object.
|
||||||
func NewSpentCoinState(hash util.Uint256, height uint32) *SpentCoinState {
|
func NewSpentCoinState(hash util.Uint256, height uint32) *SpentCoinState {
|
||||||
return &SpentCoinState{
|
return &SpentCoinState{
|
||||||
|
|
Loading…
Reference in a new issue