OSDN Git Service

update gm
authorLonelyPale <lonelypale@126.com>
Thu, 11 Nov 2021 09:32:09 +0000 (17:32 +0800)
committerLonelyPale <lonelypale@126.com>
Thu, 11 Nov 2021 09:32:09 +0000 (17:32 +0800)
wallet/utxo.go
wallet/utxo_gm.go [new file with mode: 0644]

index 4ed5ead..48885a3 100644 (file)
@@ -1,3 +1,5 @@
+// +build !gm
+
 package wallet
 
 import (
diff --git a/wallet/utxo_gm.go b/wallet/utxo_gm.go
new file mode 100644 (file)
index 0000000..dcbec83
--- /dev/null
@@ -0,0 +1,258 @@
+// +build gm
+
+package wallet
+
+import (
+       "encoding/json"
+
+       log "github.com/sirupsen/logrus"
+
+       "github.com/bytom/bytom/account"
+       "github.com/bytom/bytom/consensus"
+       "github.com/bytom/bytom/consensus/segwit"
+       sm3util "github.com/bytom/bytom/crypto/sm3"
+       dbm "github.com/bytom/bytom/database/leveldb"
+       "github.com/bytom/bytom/errors"
+       "github.com/bytom/bytom/protocol/bc"
+       "github.com/bytom/bytom/protocol/bc/types"
+)
+
+// GetAccountUtxos return all account unspent outputs
+func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract, vote bool) []*account.UTXO {
+       prefix := account.UTXOPreFix
+       if isSmartContract {
+               prefix = account.SUTXOPrefix
+       }
+
+       accountUtxos := []*account.UTXO{}
+       if unconfirmed {
+               accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract)
+       }
+
+       accountUtxoIter := w.DB.IteratorPrefix([]byte(prefix + id))
+       defer accountUtxoIter.Release()
+
+       for accountUtxoIter.Next() {
+               accountUtxo := &account.UTXO{}
+               if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "err": err}).Warn("GetAccountUtxos fail on unmarshal utxo")
+                       continue
+               }
+
+               if vote && accountUtxo.Vote == nil {
+                       continue
+               }
+
+               if accountID == accountUtxo.AccountID || accountID == "" {
+                       accountUtxos = append(accountUtxos, accountUtxo)
+               }
+       }
+       return accountUtxos
+}
+
+func (w *Wallet) attachUtxos(batch dbm.Batch, b *types.Block) {
+       for _, tx := range b.Transactions {
+               // hand update the transaction input utxos
+               inputUtxos := txInToUtxos(tx)
+               for _, inputUtxo := range inputUtxos {
+                       if segwit.IsP2WScript(inputUtxo.ControlProgram) {
+                               batch.Delete(account.StandardUTXOKey(inputUtxo.OutputID))
+                       } else {
+                               batch.Delete(account.ContractUTXOKey(inputUtxo.OutputID))
+                       }
+               }
+
+               // hand update the transaction output utxos
+               outputUtxos := txOutToUtxos(tx, b.Height)
+               utxos := w.filterAccountUtxo(outputUtxos)
+               if err := batchSaveUtxos(utxos, batch); err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on batchSaveUtxos")
+               }
+       }
+}
+
+func (w *Wallet) detachUtxos(batch dbm.Batch, b *types.Block) {
+       for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- {
+               tx := b.Transactions[txIndex]
+               for j := range tx.Outputs {
+                       resOut, err := tx.OriginalOutput(*tx.ResultIds[j])
+                       if err != nil {
+                               continue
+                       }
+
+                       if segwit.IsP2WScript(resOut.ControlProgram.Code) {
+                               batch.Delete(account.StandardUTXOKey(*tx.ResultIds[j]))
+                       } else {
+                               batch.Delete(account.ContractUTXOKey(*tx.ResultIds[j]))
+                       }
+               }
+
+               inputUtxos := txInToUtxos(tx)
+               utxos := w.filterAccountUtxo(inputUtxos)
+               if err := batchSaveUtxos(utxos, batch); err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on batchSaveUtxos")
+                       return
+               }
+       }
+}
+
+func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO {
+       outsByScript := make(map[string][]*account.UTXO, len(utxos))
+       for _, utxo := range utxos {
+               scriptStr := string(utxo.ControlProgram)
+               outsByScript[scriptStr] = append(outsByScript[scriptStr], utxo)
+       }
+
+       result := make([]*account.UTXO, 0, len(utxos))
+       for s := range outsByScript {
+               if !segwit.IsP2WScript([]byte(s)) {
+                       continue
+               }
+
+               var hash [32]byte
+               sm3util.Sum(hash[:], []byte(s))
+               data := w.DB.Get(account.ContractKey(hash))
+               if data == nil {
+                       continue
+               }
+
+               cp := &account.CtrlProgram{}
+               if err := json.Unmarshal(data, cp); err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail on unmarshal control program")
+                       continue
+               }
+
+               for _, utxo := range outsByScript[s] {
+                       utxo.AccountID = cp.AccountID
+                       utxo.Address = cp.Address
+                       utxo.ControlProgramIndex = cp.KeyIndex
+                       utxo.Change = cp.Change
+                       result = append(result, utxo)
+               }
+       }
+       return result
+}
+
+func batchSaveUtxos(utxos []*account.UTXO, batch dbm.Batch) error {
+       for _, utxo := range utxos {
+               data, err := json.Marshal(utxo)
+               if err != nil {
+                       return errors.Wrap(err, "failed marshal accountutxo")
+               }
+
+               if segwit.IsP2WScript(utxo.ControlProgram) {
+                       batch.Set(account.StandardUTXOKey(utxo.OutputID), data)
+               } else {
+                       batch.Set(account.ContractUTXOKey(utxo.OutputID), data)
+               }
+       }
+       return nil
+}
+
+func txInToUtxos(tx *types.Tx) []*account.UTXO {
+       utxos := []*account.UTXO{}
+       for _, inpID := range tx.Tx.InputIDs {
+               var utxo *account.UTXO
+               e, ok := tx.Entries[inpID]
+               if !ok {
+                       continue
+               }
+
+               switch inp := e.(type) {
+               case *bc.Spend:
+                       resOut, err := tx.OriginalOutput(*inp.SpentOutputId)
+                       if err != nil {
+                               log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut")
+                               continue
+                       }
+
+                       utxo = &account.UTXO{
+                               OutputID:       *inp.SpentOutputId,
+                               AssetID:        *resOut.Source.Value.AssetId,
+                               Amount:         resOut.Source.Value.Amount,
+                               ControlProgram: resOut.ControlProgram.Code,
+                               SourceID:       *resOut.Source.Ref,
+                               SourcePos:      resOut.Source.Position,
+                       }
+               case *bc.VetoInput:
+                       resOut, err := tx.VoteOutput(*inp.SpentOutputId)
+                       if err != nil {
+                               log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for vetoInput")
+                               continue
+                       }
+                       if *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
+                               continue
+                       }
+                       utxo = &account.UTXO{
+                               OutputID:       *inp.SpentOutputId,
+                               AssetID:        *resOut.Source.Value.AssetId,
+                               Amount:         resOut.Source.Value.Amount,
+                               ControlProgram: resOut.ControlProgram.Code,
+                               SourceID:       *resOut.Source.Ref,
+                               SourcePos:      resOut.Source.Position,
+                               Vote:           resOut.Vote,
+                       }
+               default:
+                       continue
+               }
+               utxos = append(utxos, utxo)
+       }
+       return utxos
+}
+
+func txOutToUtxos(tx *types.Tx, blockHeight uint64) []*account.UTXO {
+       utxos := []*account.UTXO{}
+       for i, out := range tx.Outputs {
+               validHeight := uint64(0)
+               entryOutput, ok := tx.Entries[*tx.ResultIds[i]]
+               if !ok {
+                       log.WithFields(log.Fields{"module": logModule}).Error("txOutToUtxos fail on get entryOutput")
+                       continue
+               }
+
+               var utxo *account.UTXO
+               switch bcOut := entryOutput.(type) {
+               case *bc.OriginalOutput:
+                       if out.AssetAmount.Amount == uint64(0) {
+                               continue
+                       }
+
+                       if tx.Inputs[0].InputType() == types.CoinbaseInputType {
+                               validHeight = blockHeight + consensus.CoinbasePendingBlockNumber
+                       }
+
+                       utxo = &account.UTXO{
+                               OutputID:       *tx.OutputID(i),
+                               AssetID:        *out.AssetAmount.AssetId,
+                               Amount:         out.AssetAmount.Amount,
+                               ControlProgram: out.ControlProgram,
+                               SourceID:       *bcOut.Source.Ref,
+                               SourcePos:      bcOut.Source.Position,
+                               ValidHeight:    validHeight,
+                       }
+
+               case *bc.VoteOutput:
+                       voteValidHeight := blockHeight + consensus.VotePendingBlockNums(blockHeight)
+                       if validHeight < voteValidHeight {
+                               validHeight = voteValidHeight
+                       }
+
+                       utxo = &account.UTXO{
+                               OutputID:       *tx.OutputID(i),
+                               AssetID:        *out.AssetAmount.AssetId,
+                               Amount:         out.AssetAmount.Amount,
+                               ControlProgram: out.ControlProgram,
+                               SourceID:       *bcOut.Source.Ref,
+                               SourcePos:      bcOut.Source.Position,
+                               ValidHeight:    validHeight,
+                               Vote:           bcOut.Vote,
+                       }
+
+               default:
+                       log.WithFields(log.Fields{"module": logModule}).Warn("txOutToUtxos fail on get bcOut")
+                       continue
+               }
+               utxos = append(utxos, utxo)
+       }
+       return utxos
+}