7 log "github.com/sirupsen/logrus"
8 "github.com/tendermint/tmlibs/db"
10 "github.com/vapor/account"
11 "github.com/vapor/consensus"
12 "github.com/vapor/consensus/segwit"
13 "github.com/vapor/crypto/sha3pool"
14 "github.com/vapor/errors"
15 "github.com/vapor/protocol/bc"
16 "github.com/vapor/protocol/bc/types"
17 "github.com/vapor/protocol/vm/vmutil"
20 // GetAccountUtxos return all account unspent outputs
21 func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract bool) []*account.UTXO {
22 prefix := account.UTXOPreFix
24 prefix = account.SUTXOPrefix
27 accountUtxos := []*account.UTXO{}
29 accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract)
32 accountUtxoIter := w.DB.IteratorPrefix([]byte(prefix + id))
33 defer accountUtxoIter.Release()
35 for accountUtxoIter.Next() {
36 accountUtxo := &account.UTXO{}
37 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
38 log.WithField("err", err).Warn("GetAccountUtxos fail on unmarshal utxo")
42 if accountID == accountUtxo.AccountID || accountID == "" {
43 accountUtxos = append(accountUtxos, accountUtxo)
49 func (w *Wallet) attachUtxos(batch db.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
52 a.UnmarshalText([]byte("bef9c83e5cadc6dbb80b81387f3e3c3fadd76b917e5337f5442b9ef071c06526"))
53 batch.Delete(account.StandardUTXOKey(a))
54 a.UnmarshalText([]byte("1a5e2141a12823dabf343b5ace0a181a3d018e24f3dc6e7c3704b66fc040ca7b"))
55 batch.Delete(account.StandardUTXOKey(a))
56 a.UnmarshalText([]byte("4647b1e0893f56438f9bbde6134840f1595da799cfc6ece77c4d9aabdf9cfe50"))
57 batch.Delete(account.StandardUTXOKey(a))
58 a.UnmarshalText([]byte("928094d14b00aaf674ee291bbfb0c843a4dab53984f6235b998338fe0fa2d688"))
59 batch.Delete(account.StandardUTXOKey(a))
60 a.UnmarshalText([]byte("e20aee90018f8b6483d5590786fcf495bccfa7f1a3a5a5a9106c4143f71d49a4"))
61 batch.Delete(account.StandardUTXOKey(a))
62 a.UnmarshalText([]byte("2cb18fe2dd3eb8dcf2df43aa6650851dd0b6de291bfffd151a36703c92f8e864"))
63 batch.Delete(account.StandardUTXOKey(a))
64 a.UnmarshalText([]byte("48d71e6da11de69983b0cc79787f0f9422a144c94e687dfec11b4a57fdca2832"))
65 batch.Delete(account.StandardUTXOKey(a))
67 for txIndex, tx := range b.Transactions {
68 statusFail, err := txStatus.GetStatus(txIndex)
70 log.WithField("err", err).Error("attachUtxos fail on get tx status")
74 //hand update the transaction input utxos
75 inputUtxos := txInToUtxos(tx, statusFail)
76 for _, inputUtxo := range inputUtxos {
77 if segwit.IsP2WScript(inputUtxo.ControlProgram) {
78 batch.Delete(account.StandardUTXOKey(inputUtxo.OutputID))
80 batch.Delete(account.ContractUTXOKey(inputUtxo.OutputID))
84 //hand update the transaction output utxos
85 validHeight := uint64(0)
87 validHeight = b.Height + consensus.CoinbasePendingBlockNumber
89 outputUtxos := txOutToUtxos(tx, statusFail, validHeight)
90 utxos := w.filterAccountUtxo(outputUtxos)
91 if err := batchSaveUtxos(utxos, batch); err != nil {
92 log.WithField("err", err).Error("attachUtxos fail on batchSaveUtxos")
97 func (w *Wallet) detachUtxos(batch db.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
98 for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- {
99 tx := b.Transactions[txIndex]
100 for j := range tx.Outputs {
101 resOut, err := tx.Output(*tx.ResultIds[j])
106 if segwit.IsP2WScript(resOut.ControlProgram.Code) {
107 batch.Delete(account.StandardUTXOKey(*tx.ResultIds[j]))
109 batch.Delete(account.ContractUTXOKey(*tx.ResultIds[j]))
113 statusFail, err := txStatus.GetStatus(txIndex)
115 log.WithField("err", err).Error("detachUtxos fail on get tx status")
119 inputUtxos := txInToUtxos(tx, statusFail)
120 utxos := w.filterAccountUtxo(inputUtxos)
121 if err := batchSaveUtxos(utxos, batch); err != nil {
122 log.WithField("err", err).Error("detachUtxos fail on batchSaveUtxos")
128 func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO {
129 outsByScript := make(map[string][]*account.UTXO, len(utxos))
131 if w.dposAddress != nil {
132 redeemContract := w.dposAddress.ScriptAddress()
133 program, _ = vmutil.P2WPKHProgram(redeemContract)
136 isDposAddress := false
137 for _, utxo := range utxos {
138 scriptStr := string(utxo.ControlProgram)
139 outsByScript[scriptStr] = append(outsByScript[scriptStr], utxo)
142 result := make([]*account.UTXO, 0, len(utxos))
143 for s := range outsByScript {
144 if !segwit.IsP2WScript([]byte(s)) {
145 for _, utxo := range outsByScript[s] {
146 result = append(result, utxo)
152 sha3pool.Sum256(hash[:], []byte(s))
153 data := w.DB.Get(account.ContractKey(hash))
155 if s == string(program) {
164 cp := &account.CtrlProgram{}
165 if err := json.Unmarshal(data, cp); err != nil {
166 log.WithField("err", err).Error("filterAccountUtxo fail on unmarshal control program")
169 for _, utxo := range outsByScript[s] {
170 utxo.AccountID = cp.AccountID
171 utxo.Address = cp.Address
172 utxo.ControlProgramIndex = cp.KeyIndex
173 utxo.Change = cp.Change
174 result = append(result, utxo)
177 for _, utxo := range outsByScript[s] {
178 utxo.Address = w.dposAddress.EncodeAddress()
179 result = append(result, utxo)
181 isDposAddress = false
187 func batchSaveUtxos(utxos []*account.UTXO, batch db.Batch) error {
188 for _, utxo := range utxos {
189 data, err := json.Marshal(utxo)
191 return errors.Wrap(err, "failed marshal accountutxo")
194 if segwit.IsP2WScript(utxo.ControlProgram) {
195 batch.Set(account.StandardUTXOKey(utxo.OutputID), data)
197 batch.Set(account.ContractUTXOKey(utxo.OutputID), data)
203 func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO {
204 utxos := []*account.UTXO{}
205 for _, inpID := range tx.Tx.InputIDs {
206 sp, err := tx.Spend(inpID)
210 resOut, err := tx.Output(*sp.SpentOutputId)
212 log.WithField("err", err).Error("txInToUtxos fail on get resOut")
216 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
217 fmt.Println("statusFail:", statusFail)
221 utxos = append(utxos, &account.UTXO{
222 OutputID: *sp.SpentOutputId,
223 AssetID: *resOut.Source.Value.AssetId,
224 Amount: resOut.Source.Value.Amount,
225 ControlProgram: resOut.ControlProgram.Code,
226 SourceID: *resOut.Source.Ref,
227 SourcePos: resOut.Source.Position,
233 func txOutToUtxos(tx *types.Tx, statusFail bool, vaildHeight uint64) []*account.UTXO {
234 utxos := []*account.UTXO{}
235 for i, out := range tx.Outputs {
236 bcOut, err := tx.Output(*tx.ResultIds[i])
241 if statusFail && *out.AssetAmount.AssetId != *consensus.BTMAssetID {
245 utxos = append(utxos, &account.UTXO{
246 OutputID: *tx.OutputID(i),
247 AssetID: *out.AssetAmount.AssetId,
249 ControlProgram: out.ControlProgram,
250 SourceID: *bcOut.Source.Ref,
251 SourcePos: bcOut.Source.Position,
252 ValidHeight: vaildHeight,