4 log "github.com/sirupsen/logrus"
6 "github.com/vapor/account"
7 "github.com/vapor/consensus"
8 "github.com/vapor/consensus/segwit"
9 "github.com/vapor/crypto/sha3pool"
10 "github.com/vapor/database"
11 "github.com/vapor/protocol/bc"
12 "github.com/vapor/protocol/bc/types"
15 // GetAccountUtxos return all account unspent outputs
16 func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract bool, vote bool) []*account.UTXO {
17 prefix := database.UTXOPrefix
19 prefix = database.SUTXOPrefix
22 accountUtxos := []*account.UTXO{}
24 accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract)
27 confirmedUTXOs, err := w.store.ListAccountUTXOs(string(prefix) + id)
29 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("GetAccountUtxos fail.")
31 accountUtxos = append(accountUtxos, confirmedUTXOs...)
33 newAccountUtxos := []*account.UTXO{}
34 for _, accountUtxo := range accountUtxos {
35 if vote && accountUtxo.Vote == nil {
38 if accountID == accountUtxo.AccountID || accountID == "" {
39 newAccountUtxos = append(newAccountUtxos, accountUtxo)
42 return newAccountUtxos
45 func (w *Wallet) attachUtxos(b *types.Block, txStatus *bc.TransactionStatus) {
46 for txIndex, tx := range b.Transactions {
47 statusFail, err := txStatus.GetStatus(txIndex)
49 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on get tx status")
53 //hand update the transaction input utxos
54 inputUtxos := txInToUtxos(tx, statusFail)
55 for _, inputUtxo := range inputUtxos {
56 if segwit.IsP2WScript(inputUtxo.ControlProgram) {
57 w.AccountMgr.DeleteStandardUTXO(inputUtxo.OutputID)
59 w.store.DeleteContractUTXO(inputUtxo.OutputID)
63 //hand update the transaction output utxos
64 outputUtxos := txOutToUtxos(tx, statusFail, b.Height)
65 utxos := w.filterAccountUtxo(outputUtxos)
66 if err := w.saveUtxos(utxos); err != nil {
67 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on saveUtxos")
72 func (w *Wallet) detachUtxos(b *types.Block, txStatus *bc.TransactionStatus) {
73 for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- {
74 tx := b.Transactions[txIndex]
75 for j := range tx.Outputs {
77 switch resOut := tx.Entries[*tx.ResultIds[j]].(type) {
78 case *bc.IntraChainOutput:
79 code = resOut.ControlProgram.Code
81 code = resOut.ControlProgram.Code
86 if segwit.IsP2WScript(code) {
87 w.AccountMgr.DeleteStandardUTXO(*tx.ResultIds[j])
89 w.store.DeleteContractUTXO(*tx.ResultIds[j])
93 statusFail, err := txStatus.GetStatus(txIndex)
95 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on get tx status")
99 inputUtxos := txInToUtxos(tx, statusFail)
100 utxos := w.filterAccountUtxo(inputUtxos)
101 if err := w.saveUtxos(utxos); err != nil {
102 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on batchSaveUtxos")
108 func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO {
109 outsByScript := make(map[string][]*account.UTXO, len(utxos))
110 for _, utxo := range utxos {
111 scriptStr := string(utxo.ControlProgram)
112 outsByScript[scriptStr] = append(outsByScript[scriptStr], utxo)
115 result := make([]*account.UTXO, 0, len(utxos))
116 for s := range outsByScript {
117 if !segwit.IsP2WScript([]byte(s)) {
118 for _, utxo := range outsByScript[s] {
119 result = append(result, utxo)
125 sha3pool.Sum256(hash[:], []byte(s))
126 cp, err := w.store.GetControlProgram(bc.NewHash(hash))
128 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail.")
135 for _, utxo := range outsByScript[s] {
136 utxo.AccountID = cp.AccountID
137 utxo.Address = cp.Address
138 utxo.ControlProgramIndex = cp.KeyIndex
139 utxo.Change = cp.Change
140 result = append(result, utxo)
146 func (w *Wallet) saveUtxos(utxos []*account.UTXO) error {
147 for _, utxo := range utxos {
148 if segwit.IsP2WScript(utxo.ControlProgram) {
149 if err := w.AccountMgr.SetStandardUTXO(utxo.OutputID, utxo); err != nil {
153 if err := w.store.SetContractUTXO(utxo.OutputID, utxo); err != nil {
161 func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO {
162 utxos := []*account.UTXO{}
163 for _, inpID := range tx.Tx.InputIDs {
165 e, err := tx.Entry(inpID)
169 utxo := &account.UTXO{}
170 switch inp := e.(type) {
172 resOut, err := tx.IntraChainOutput(*inp.SpentOutputId)
174 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for spedn")
177 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
180 utxo = &account.UTXO{
181 OutputID: *inp.SpentOutputId,
182 AssetID: *resOut.Source.Value.AssetId,
183 Amount: resOut.Source.Value.Amount,
184 ControlProgram: resOut.ControlProgram.Code,
185 SourceID: *resOut.Source.Ref,
186 SourcePos: resOut.Source.Position,
189 resOut, err := tx.VoteOutput(*inp.SpentOutputId)
191 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for vetoInput")
194 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
197 utxo = &account.UTXO{
198 OutputID: *inp.SpentOutputId,
199 AssetID: *resOut.Source.Value.AssetId,
200 Amount: resOut.Source.Value.Amount,
201 ControlProgram: resOut.ControlProgram.Code,
202 SourceID: *resOut.Source.Ref,
203 SourcePos: resOut.Source.Position,
209 utxos = append(utxos, utxo)
214 func txOutToUtxos(tx *types.Tx, statusFail bool, blockHeight uint64) []*account.UTXO {
215 validHeight := uint64(0)
216 if tx.Inputs[0].InputType() == types.CoinbaseInputType {
217 validHeight = blockHeight + consensus.CoinbasePendingBlockNumber
220 utxos := []*account.UTXO{}
221 for i, out := range tx.Outputs {
222 entryOutput, err := tx.Entry(*tx.ResultIds[i])
224 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txOutToUtxos fail on get entryOutput")
228 utxo := &account.UTXO{}
229 switch bcOut := entryOutput.(type) {
230 case *bc.IntraChainOutput:
231 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
234 utxo = &account.UTXO{
235 OutputID: *tx.OutputID(i),
236 AssetID: *out.AssetAmount().AssetId,
237 Amount: out.AssetAmount().Amount,
238 ControlProgram: out.ControlProgram(),
239 SourceID: *bcOut.Source.Ref,
240 SourcePos: bcOut.Source.Position,
241 ValidHeight: validHeight,
245 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
249 voteValidHeight := blockHeight + consensus.VotePendingBlockNumber
250 if validHeight < voteValidHeight {
251 validHeight = voteValidHeight
254 utxo = &account.UTXO{
255 OutputID: *tx.OutputID(i),
256 AssetID: *out.AssetAmount().AssetId,
257 Amount: out.AssetAmount().Amount,
258 ControlProgram: out.ControlProgram(),
259 SourceID: *bcOut.Source.Ref,
260 SourcePos: bcOut.Source.Position,
261 ValidHeight: validHeight,
266 log.WithFields(log.Fields{"module": logModule}).Warn("txOutToUtxos fail on get bcOut")
270 utxos = append(utxos, utxo)