6 log "github.com/sirupsen/logrus"
8 "github.com/vapor/account"
9 "github.com/vapor/consensus"
10 "github.com/vapor/consensus/segwit"
11 "github.com/vapor/crypto/sha3pool"
12 dbm "github.com/vapor/database/leveldb"
13 "github.com/vapor/errors"
14 "github.com/vapor/protocol/bc"
15 "github.com/vapor/protocol/bc/types"
18 // GetAccountUtxos return all account unspent outputs
19 func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract bool) []*account.UTXO {
20 prefix := account.UTXOPreFix
22 prefix = account.SUTXOPrefix
25 accountUtxos := []*account.UTXO{}
27 accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract)
30 accountUtxoIter := w.DB.IteratorPrefix([]byte(prefix + id))
31 defer accountUtxoIter.Release()
33 for accountUtxoIter.Next() {
34 accountUtxo := &account.UTXO{}
35 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
36 log.WithFields(log.Fields{"module": logModule, "err": err}).Warn("GetAccountUtxos fail on unmarshal utxo")
40 if accountID == accountUtxo.AccountID || accountID == "" {
41 accountUtxos = append(accountUtxos, accountUtxo)
47 func (w *Wallet) attachUtxos(batch dbm.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
48 for txIndex, tx := range b.Transactions {
49 statusFail, err := txStatus.GetStatus(txIndex)
51 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on get tx status")
55 //hand update the transaction input utxos
56 inputUtxos := txInToUtxos(tx, statusFail)
57 for _, inputUtxo := range inputUtxos {
58 if segwit.IsP2WScript(inputUtxo.ControlProgram) {
59 batch.Delete(account.StandardUTXOKey(inputUtxo.OutputID))
61 batch.Delete(account.ContractUTXOKey(inputUtxo.OutputID))
65 //hand update the transaction output utxos
66 validHeight := uint64(0)
68 validHeight = b.Height + consensus.CoinbasePendingBlockNumber
70 outputUtxos := txOutToUtxos(tx, statusFail, validHeight)
71 utxos := w.filterAccountUtxo(outputUtxos)
72 if err := batchSaveUtxos(utxos, batch); err != nil {
73 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on batchSaveUtxos")
78 func (w *Wallet) detachUtxos(batch dbm.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
79 for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- {
80 tx := b.Transactions[txIndex]
81 for j := range tx.Outputs {
82 resOut, err := tx.IntraChainOutput(*tx.ResultIds[j])
87 if segwit.IsP2WScript(resOut.ControlProgram.Code) {
88 batch.Delete(account.StandardUTXOKey(*tx.ResultIds[j]))
90 batch.Delete(account.ContractUTXOKey(*tx.ResultIds[j]))
94 statusFail, err := txStatus.GetStatus(txIndex)
96 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on get tx status")
100 inputUtxos := txInToUtxos(tx, statusFail)
101 utxos := w.filterAccountUtxo(inputUtxos)
102 if err := batchSaveUtxos(utxos, batch); err != nil {
103 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on batchSaveUtxos")
109 func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO {
110 outsByScript := make(map[string][]*account.UTXO, len(utxos))
111 for _, utxo := range utxos {
112 scriptStr := string(utxo.ControlProgram)
113 outsByScript[scriptStr] = append(outsByScript[scriptStr], utxo)
116 result := make([]*account.UTXO, 0, len(utxos))
117 for s := range outsByScript {
118 if !segwit.IsP2WScript([]byte(s)) {
119 for _, utxo := range outsByScript[s] {
120 result = append(result, utxo)
126 sha3pool.Sum256(hash[:], []byte(s))
127 data := w.DB.Get(account.ContractKey(hash))
132 cp := &account.CtrlProgram{}
133 if err := json.Unmarshal(data, cp); err != nil {
134 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail on unmarshal control program")
138 for _, utxo := range outsByScript[s] {
139 utxo.AccountID = cp.AccountID
140 utxo.Address = cp.Address
141 utxo.ControlProgramIndex = cp.KeyIndex
142 utxo.Change = cp.Change
143 result = append(result, utxo)
149 func batchSaveUtxos(utxos []*account.UTXO, batch dbm.Batch) error {
150 for _, utxo := range utxos {
151 data, err := json.Marshal(utxo)
153 return errors.Wrap(err, "failed marshal accountutxo")
156 if segwit.IsP2WScript(utxo.ControlProgram) {
157 batch.Set(account.StandardUTXOKey(utxo.OutputID), data)
159 batch.Set(account.ContractUTXOKey(utxo.OutputID), data)
165 func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO {
166 utxos := []*account.UTXO{}
167 for _, inpID := range tx.Tx.InputIDs {
168 sp, err := tx.Spend(inpID)
173 entryOutput, err := tx.Entry(*sp.SpentOutputId)
175 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get entryOutput")
179 utxo := &account.UTXO{}
180 switch resOut := entryOutput.(type) {
181 case *bc.IntraChainOutput:
182 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
185 utxo = &account.UTXO{
186 OutputID: *sp.SpentOutputId,
187 AssetID: *resOut.Source.Value.AssetId,
188 Amount: resOut.Source.Value.Amount,
189 ControlProgram: resOut.ControlProgram.Code,
190 SourceID: *resOut.Source.Ref,
191 SourcePos: resOut.Source.Position,
195 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
198 utxo = &account.UTXO{
199 OutputID: *sp.SpentOutputId,
200 AssetID: *resOut.Source.Value.AssetId,
201 Amount: resOut.Source.Value.Amount,
202 ControlProgram: resOut.ControlProgram.Code,
203 SourceID: *resOut.Source.Ref,
204 SourcePos: resOut.Source.Position,
209 log.WithFields(log.Fields{"module": logModule}).Error("txInToUtxos fail on get resOut")
213 utxos = append(utxos, utxo)
218 func txOutToUtxos(tx *types.Tx, statusFail bool, vaildHeight uint64) []*account.UTXO {
219 utxos := []*account.UTXO{}
220 for i, out := range tx.Outputs {
221 entryOutput, err := tx.Entry(*tx.ResultIds[i])
223 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txOutToUtxos fail on get entryOutput")
227 utxo := &account.UTXO{}
228 switch bcOut := entryOutput.(type) {
229 case *bc.IntraChainOutput:
230 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
233 utxo = &account.UTXO{
234 OutputID: *tx.OutputID(i),
235 AssetID: *out.AssetAmount().AssetId,
236 Amount: out.AssetAmount().Amount,
237 ControlProgram: out.ControlProgram(),
238 SourceID: *bcOut.Source.Ref,
239 SourcePos: bcOut.Source.Position,
240 ValidHeight: vaildHeight,
244 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
247 utxo = &account.UTXO{
248 OutputID: *tx.OutputID(i),
249 AssetID: *out.AssetAmount().AssetId,
250 Amount: out.AssetAmount().Amount,
251 ControlProgram: out.ControlProgram(),
252 SourceID: *bcOut.Source.Ref,
253 SourcePos: bcOut.Source.Position,
254 ValidHeight: vaildHeight,
259 log.WithFields(log.Fields{"module": logModule}).Error("txOutToUtxos fail on get bcOut")
263 utxos = append(utxos, utxo)