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, vote bool) []*account.UTXO {
20 prefix := account.UTXOPreFix
22 prefix = account.SUTXOPrefix
25 accountUtxos := []*account.UTXO{}
27 accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract)
30 // replace with GetAccountUtxos
31 accountUtxoIter := w.DB.IteratorPrefix([]byte(prefix + id))
32 defer accountUtxoIter.Release()
34 for accountUtxoIter.Next() {
35 accountUtxo := &account.UTXO{}
36 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
37 log.WithFields(log.Fields{"module": logModule, "err": err}).Warn("GetAccountUtxos fail on unmarshal utxo")
41 if vote && accountUtxo.Vote == nil {
45 if accountID == accountUtxo.AccountID || accountID == "" {
46 accountUtxos = append(accountUtxos, accountUtxo)
52 func (w *Wallet) attachUtxos(batch dbm.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
53 for txIndex, tx := range b.Transactions {
54 statusFail, err := txStatus.GetStatus(txIndex)
56 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on get tx status")
60 //hand update the transaction input utxos
61 inputUtxos := txInToUtxos(tx, statusFail)
62 for _, inputUtxo := range inputUtxos {
63 if segwit.IsP2WScript(inputUtxo.ControlProgram) {
64 batch.Delete(account.StandardUTXOKey(inputUtxo.OutputID))
66 batch.Delete(account.ContractUTXOKey(inputUtxo.OutputID))
70 //hand update the transaction output utxos
71 outputUtxos := txOutToUtxos(tx, statusFail, b.Height)
72 utxos := w.filterAccountUtxo(outputUtxos)
73 if err := batchSaveUtxos(utxos, batch); err != nil {
74 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on batchSaveUtxos")
79 func (w *Wallet) detachUtxos(batch dbm.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
80 for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- {
81 tx := b.Transactions[txIndex]
82 for j := range tx.Outputs {
84 switch resOut := tx.Entries[*tx.ResultIds[j]].(type) {
85 case *bc.IntraChainOutput:
86 code = resOut.ControlProgram.Code
88 code = resOut.ControlProgram.Code
93 if segwit.IsP2WScript(code) {
94 batch.Delete(account.StandardUTXOKey(*tx.ResultIds[j]))
96 batch.Delete(account.ContractUTXOKey(*tx.ResultIds[j]))
100 statusFail, err := txStatus.GetStatus(txIndex)
102 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on get tx status")
106 inputUtxos := txInToUtxos(tx, statusFail)
107 utxos := w.filterAccountUtxo(inputUtxos)
108 if err := batchSaveUtxos(utxos, batch); err != nil {
109 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on batchSaveUtxos")
115 func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO {
116 outsByScript := make(map[string][]*account.UTXO, len(utxos))
117 for _, utxo := range utxos {
118 scriptStr := string(utxo.ControlProgram)
119 outsByScript[scriptStr] = append(outsByScript[scriptStr], utxo)
122 result := make([]*account.UTXO, 0, len(utxos))
123 for s := range outsByScript {
124 if !segwit.IsP2WScript([]byte(s)) {
125 for _, utxo := range outsByScript[s] {
126 result = append(result, utxo)
132 sha3pool.Sum256(hash[:], []byte(s))
133 data := w.DB.Get(account.ContractKey(hash))
138 cp := &account.CtrlProgram{}
139 if err := json.Unmarshal(data, cp); err != nil {
140 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail on unmarshal control program")
144 for _, utxo := range outsByScript[s] {
145 utxo.AccountID = cp.AccountID
146 utxo.Address = cp.Address
147 utxo.ControlProgramIndex = cp.KeyIndex
148 utxo.Change = cp.Change
149 result = append(result, utxo)
155 func batchSaveUtxos(utxos []*account.UTXO, batch dbm.Batch) error {
156 for _, utxo := range utxos {
157 data, err := json.Marshal(utxo)
159 return errors.Wrap(err, "failed marshal accountutxo")
162 if segwit.IsP2WScript(utxo.ControlProgram) {
163 batch.Set(account.StandardUTXOKey(utxo.OutputID), data)
165 batch.Set(account.ContractUTXOKey(utxo.OutputID), data)
171 func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO {
172 utxos := []*account.UTXO{}
173 for _, inpID := range tx.Tx.InputIDs {
175 e, err := tx.Entry(inpID)
179 utxo := &account.UTXO{}
180 switch inp := e.(type) {
182 resOut, err := tx.IntraChainOutput(*inp.SpentOutputId)
184 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for spedn")
187 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
190 utxo = &account.UTXO{
191 OutputID: *inp.SpentOutputId,
192 AssetID: *resOut.Source.Value.AssetId,
193 Amount: resOut.Source.Value.Amount,
194 ControlProgram: resOut.ControlProgram.Code,
195 SourceID: *resOut.Source.Ref,
196 SourcePos: resOut.Source.Position,
199 resOut, err := tx.VoteOutput(*inp.SpentOutputId)
201 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for vetoInput")
204 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
207 utxo = &account.UTXO{
208 OutputID: *inp.SpentOutputId,
209 AssetID: *resOut.Source.Value.AssetId,
210 Amount: resOut.Source.Value.Amount,
211 ControlProgram: resOut.ControlProgram.Code,
212 SourceID: *resOut.Source.Ref,
213 SourcePos: resOut.Source.Position,
219 utxos = append(utxos, utxo)
224 func txOutToUtxos(tx *types.Tx, statusFail bool, blockHeight uint64) []*account.UTXO {
225 validHeight := uint64(0)
226 if tx.Inputs[0].InputType() == types.CoinbaseInputType {
227 validHeight = blockHeight + consensus.CoinbasePendingBlockNumber
230 utxos := []*account.UTXO{}
231 for i, out := range tx.Outputs {
232 entryOutput, err := tx.Entry(*tx.ResultIds[i])
234 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txOutToUtxos fail on get entryOutput")
238 utxo := &account.UTXO{}
239 switch bcOut := entryOutput.(type) {
240 case *bc.IntraChainOutput:
241 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
244 utxo = &account.UTXO{
245 OutputID: *tx.OutputID(i),
246 AssetID: *out.AssetAmount().AssetId,
247 Amount: out.AssetAmount().Amount,
248 ControlProgram: out.ControlProgram(),
249 SourceID: *bcOut.Source.Ref,
250 SourcePos: bcOut.Source.Position,
251 ValidHeight: validHeight,
255 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
259 voteValidHeight := blockHeight + consensus.VotePendingBlockNumber
260 if validHeight < voteValidHeight {
261 validHeight = voteValidHeight
264 utxo = &account.UTXO{
265 OutputID: *tx.OutputID(i),
266 AssetID: *out.AssetAmount().AssetId,
267 Amount: out.AssetAmount().Amount,
268 ControlProgram: out.ControlProgram(),
269 SourceID: *bcOut.Source.Ref,
270 SourcePos: bcOut.Source.Position,
271 ValidHeight: validHeight,
276 log.WithFields(log.Fields{"module": logModule}).Warn("txOutToUtxos fail on get bcOut")
280 utxos = append(utxos, utxo)