From: LonelyPale Date: Thu, 11 Nov 2021 09:32:09 +0000 (+0800) Subject: update gm X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=b6c24192839c577d2dc20385898cc06ce4ec043d;p=bytom%2Fbytom.git update gm --- diff --git a/wallet/utxo.go b/wallet/utxo.go index 4ed5ead2..48885a3c 100644 --- a/wallet/utxo.go +++ b/wallet/utxo.go @@ -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 index 00000000..dcbec83d --- /dev/null +++ b/wallet/utxo_gm.go @@ -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 +}