}
func (view *UtxoViewpoint) ApplyTransaction(block *bc.Block, tx *bc.Tx, statusFail bool) error {
- for _, prevout := range tx.MainchainOutputIDs {
- entry, ok := view.Entries[prevout]
- if !ok {
- return errors.New("fail to find mainchain output entry")
- }
+ if err := view.applyCrossChainUtxo(block, tx); err != nil {
+ return err
+ }
- if entry.Type != storage.CrosschainUTXOType {
- return errors.New("look up mainchainOutputID but find utxo not from mainchain")
+ if err := view.applySpendUtxo(block, tx, statusFail); err != nil {
+ return err
+ }
+
+ return view.applyOutputUtxo(block, tx, statusFail)
+}
+
+func (view *UtxoViewpoint) ApplyBlock(block *bc.Block, txStatus *bc.TransactionStatus) error {
+ for i, tx := range block.Transactions {
+ statusFail, err := txStatus.GetStatus(i)
+ if err != nil {
+ return err
}
- if entry.Spent {
- return errors.New("mainchain output has been spent")
+ if err := view.ApplyTransaction(block, tx, statusFail); err != nil {
+ return err
}
+ }
+ return nil
+}
- entry.BlockHeight = block.Height
- entry.SpendOutput()
+func (view *UtxoViewpoint) CanSpend(hash *bc.Hash) bool {
+ entry := view.Entries[*hash]
+ return entry != nil && !entry.Spent
+}
+
+func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error {
+ if err := view.detachCrossChainUtxo(tx); err != nil {
+ return err
}
- for _, prevout := range tx.SpentOutputIDs {
- assetID := bc.AssetID{}
- entryOutput, err := tx.Entry(prevout)
+ if err := view.detachSpendUtxo(tx, statusFail); err != nil {
+ return err
+ }
+
+ return view.detachOutputUtxo(tx, statusFail)
+}
+
+func (view *UtxoViewpoint) DetachBlock(block *bc.Block, txStatus *bc.TransactionStatus) error {
+ for i := len(block.Transactions) - 1; i >= 0; i-- {
+ statusFail, err := txStatus.GetStatus(i)
if err != nil {
return err
}
- switch output := entryOutput.(type) {
- case *bc.IntraChainOutput:
- assetID = *output.Source.Value.AssetId
- case *bc.VoteOutput:
- assetID = *output.Source.Value.AssetId
- default:
- return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput)
+ if err := view.DetachTransaction(block.Transactions[i], statusFail); err != nil {
+ return err
}
+ }
+ return nil
+}
- if statusFail && assetID != *consensus.BTMAssetID {
- continue
- }
+func (view *UtxoViewpoint) HasUtxo(hash *bc.Hash) bool {
+ _, ok := view.Entries[*hash]
+ return ok
+}
+func (view *UtxoViewpoint) applyCrossChainUtxo(block *bc.Block, tx *bc.Tx) error {
+ for _, prevout := range tx.MainchainOutputIDs {
entry, ok := view.Entries[prevout]
if !ok {
- return errors.New("fail to find utxo entry")
+ return errors.New("fail to find mainchain output entry")
}
if entry.Spent {
- return errors.New("utxo has been spent")
- }
-
- switch entry.Type {
- case storage.CrosschainUTXOType:
- return errors.New("look up spentOutputID but find utxo from mainchain")
-
- case storage.CoinbaseUTXOType:
- if (entry.BlockHeight + consensus.CoinbasePendingBlockNumber) > block.Height {
- return errors.New("coinbase utxo is not ready for use")
- }
-
- case storage.VoteUTXOType:
- if (entry.BlockHeight + consensus.VotePendingBlockNumber) > block.Height {
- return errors.New("Coin is within the voting lock time")
- }
+ return errors.New("mainchain output has been spent")
}
+ entry.BlockHeight = block.Height
entry.SpendOutput()
}
+ return nil
+}
+func (view *UtxoViewpoint) applyOutputUtxo(block *bc.Block, tx *bc.Tx, statusFail bool) error {
for _, id := range tx.TxHeader.ResultIds {
- assetID := bc.AssetID{}
entryOutput, err := tx.Entry(*id)
if err != nil {
- continue
+ return err
}
+ var assetID bc.AssetID
utxoType := storage.NormalUTXOType
-
switch output := entryOutput.(type) {
case *bc.IntraChainOutput:
if output.Source.Value.Amount == uint64(0) {
return nil
}
-func (view *UtxoViewpoint) ApplyBlock(block *bc.Block, txStatus *bc.TransactionStatus) error {
- for i, tx := range block.Transactions {
- statusFail, err := txStatus.GetStatus(i)
- if err != nil {
- return err
- }
- if err := view.ApplyTransaction(block, tx, statusFail); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (view *UtxoViewpoint) CanSpend(hash *bc.Hash) bool {
- entry := view.Entries[*hash]
- return entry != nil && !entry.Spent
-}
-
-func (view *UtxoViewpoint) DetachTransaction(tx *bc.Tx, statusFail bool) error {
- for _, prevout := range tx.MainchainOutputIDs {
- // don't simply delete(view.Entries, prevout), because we need to delete from db in saveUtxoView()
- entry, ok := view.Entries[prevout]
- if ok && (entry.Type != storage.CrosschainUTXOType) {
- return errors.New("look up mainchainOutputID but find utxo not from mainchain")
- }
-
- if ok && !entry.Spent {
- return errors.New("try to revert an unspent utxo")
- }
-
- if !ok {
- view.Entries[prevout] = storage.NewUtxoEntry(storage.CrosschainUTXOType, 0, false)
- continue
- }
- entry.UnspendOutput()
- }
-
+func (view *UtxoViewpoint) applySpendUtxo(block *bc.Block, tx *bc.Tx, statusFail bool) error {
for _, prevout := range tx.SpentOutputIDs {
- assetID := bc.AssetID{}
entryOutput, err := tx.Entry(prevout)
if err != nil {
return err
}
- utxoType := storage.NormalUTXOType
+ var assetID bc.AssetID
switch output := entryOutput.(type) {
case *bc.IntraChainOutput:
assetID = *output.Source.Value.AssetId
case *bc.VoteOutput:
assetID = *output.Source.Value.AssetId
- utxoType = storage.VoteUTXOType
default:
return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput)
}
}
entry, ok := view.Entries[prevout]
- if ok && (entry.Type == storage.CrosschainUTXOType) {
- return errors.New("look up SpentOutputIDs but find mainchain utxo")
+ if !ok {
+ return errors.New("fail to find utxo entry")
}
- if ok && !entry.Spent {
- return errors.New("try to revert an unspent utxo")
+ if entry.Spent {
+ return errors.New("utxo has been spent")
}
+ switch entry.Type {
+ case storage.CoinbaseUTXOType:
+ if (entry.BlockHeight + consensus.CoinbasePendingBlockNumber) > block.Height {
+ return errors.New("coinbase utxo is not ready for use")
+ }
+
+ case storage.VoteUTXOType:
+ if (entry.BlockHeight + consensus.VotePendingBlockNumber) > block.Height {
+ return errors.New("Coin is within the voting lock time")
+ }
+ }
+
+ entry.SpendOutput()
+ }
+ return nil
+}
+
+func (view *UtxoViewpoint) detachCrossChainUtxo(tx *bc.Tx) error {
+ for _, prevout := range tx.MainchainOutputIDs {
+ entry, ok := view.Entries[prevout]
if !ok {
- view.Entries[prevout] = storage.NewUtxoEntry(utxoType, 0, false)
- continue
+ return errors.New("fail to find mainchain output entry")
}
+
+ if !entry.Spent {
+ return errors.New("mainchain output is unspent")
+ }
+
entry.UnspendOutput()
}
+ return nil
+}
+func (view *UtxoViewpoint) detachOutputUtxo(tx *bc.Tx, statusFail bool) error {
for _, id := range tx.TxHeader.ResultIds {
- assetID := bc.AssetID{}
entryOutput, err := tx.Entry(*id)
if err != nil {
- continue
+ return err
}
+ var assetID bc.AssetID
utxoType := storage.NormalUTXOType
switch output := entryOutput.(type) {
case *bc.IntraChainOutput:
return nil
}
-func (view *UtxoViewpoint) DetachBlock(block *bc.Block, txStatus *bc.TransactionStatus) error {
- for i := len(block.Transactions) - 1; i >= 0; i-- {
- statusFail, err := txStatus.GetStatus(i)
+func (view *UtxoViewpoint) detachSpendUtxo(tx *bc.Tx, statusFail bool) error {
+ for _, prevout := range tx.SpentOutputIDs {
+ entryOutput, err := tx.Entry(prevout)
if err != nil {
return err
}
- if err := view.DetachTransaction(block.Transactions[i], statusFail); err != nil {
- return err
+
+ var assetID bc.AssetID
+ utxoType := storage.NormalUTXOType
+ switch output := entryOutput.(type) {
+ case *bc.IntraChainOutput:
+ assetID = *output.Source.Value.AssetId
+ case *bc.VoteOutput:
+ assetID = *output.Source.Value.AssetId
+ utxoType = storage.VoteUTXOType
+ default:
+ return errors.Wrapf(bc.ErrEntryType, "entry %x has unexpected type %T", prevout.Bytes(), entryOutput)
+ }
+
+ if statusFail && assetID != *consensus.BTMAssetID {
+ continue
+ }
+
+ entry, ok := view.Entries[prevout]
+ if ok && !entry.Spent {
+ return errors.New("try to revert an unspent utxo")
}
+
+ if !ok {
+ view.Entries[prevout] = storage.NewUtxoEntry(utxoType, 0, false)
+ continue
+ }
+ entry.UnspendOutput()
}
return nil
}
-
-func (view *UtxoViewpoint) HasUtxo(hash *bc.Hash) bool {
- _, ok := view.Entries[*hash]
- return ok
-}