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/protocol/bc"
11 "github.com/vapor/protocol/bc/types"
14 // GetAccountUtxos return all account unspent outputs
15 func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract bool, vote bool) []*account.UTXO {
16 accountUtxos := []*account.UTXO{}
18 accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract)
21 confirmedUTXOs, err := w.Store.ListAccountUTXOs(id, isSmartContract)
23 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("GetAccountUtxos fail.")
25 accountUtxos = append(accountUtxos, confirmedUTXOs...)
27 newAccountUtxos := []*account.UTXO{}
28 for _, accountUtxo := range accountUtxos {
29 if vote && accountUtxo.Vote == nil {
32 if accountID == accountUtxo.AccountID || accountID == "" {
33 newAccountUtxos = append(newAccountUtxos, accountUtxo)
36 return newAccountUtxos
39 func (w *Wallet) attachUtxos(b *types.Block, txStatus *bc.TransactionStatus, store WalletStore) {
40 for txIndex, tx := range b.Transactions {
41 statusFail, err := txStatus.GetStatus(txIndex)
43 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on get tx status")
47 //hand update the transaction input utxos
48 inputUtxos := txInToUtxos(tx, statusFail)
49 for _, inputUtxo := range inputUtxos {
50 if segwit.IsP2WScript(inputUtxo.ControlProgram) {
51 w.AccountMgr.DeleteStandardUTXO(inputUtxo.OutputID)
53 store.DeleteContractUTXO(inputUtxo.OutputID)
57 //hand update the transaction output utxos
58 outputUtxos := txOutToUtxos(tx, statusFail, b.Height)
59 utxos := w.filterAccountUtxo(outputUtxos)
60 if err := w.saveUtxos(utxos, store); err != nil {
61 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on saveUtxos")
66 func (w *Wallet) detachUtxos(b *types.Block, txStatus *bc.TransactionStatus, store WalletStore) {
67 for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- {
68 tx := b.Transactions[txIndex]
69 for j := range tx.Outputs {
71 switch resOut := tx.Entries[*tx.ResultIds[j]].(type) {
72 case *bc.IntraChainOutput:
73 if resOut.Source.Value.Amount == uint64(0) {
76 code = resOut.ControlProgram.Code
78 code = resOut.ControlProgram.Code
83 if segwit.IsP2WScript(code) {
84 w.AccountMgr.DeleteStandardUTXO(*tx.ResultIds[j])
86 store.DeleteContractUTXO(*tx.ResultIds[j])
90 statusFail, err := txStatus.GetStatus(txIndex)
92 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on get tx status")
96 inputUtxos := txInToUtxos(tx, statusFail)
97 utxos := w.filterAccountUtxo(inputUtxos)
98 if err := w.saveUtxos(utxos, store); err != nil {
99 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on batchSaveUtxos")
105 func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO {
106 outsByScript := make(map[string][]*account.UTXO, len(utxos))
107 for _, utxo := range utxos {
108 scriptStr := string(utxo.ControlProgram)
109 outsByScript[scriptStr] = append(outsByScript[scriptStr], utxo)
112 result := make([]*account.UTXO, 0, len(utxos))
113 for s := range outsByScript {
114 if !segwit.IsP2WScript([]byte(s)) {
115 for _, utxo := range outsByScript[s] {
116 result = append(result, utxo)
122 sha3pool.Sum256(hash[:], []byte(s))
123 cp, err := w.AccountMgr.GetControlProgram(bc.NewHash(hash))
124 if err != nil && err != account.ErrFindCtrlProgram {
125 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail.")
132 for _, utxo := range outsByScript[s] {
133 utxo.AccountID = cp.AccountID
134 utxo.Address = cp.Address
135 utxo.ControlProgramIndex = cp.KeyIndex
136 utxo.Change = cp.Change
137 result = append(result, utxo)
143 func (w *Wallet) saveUtxos(utxos []*account.UTXO, store WalletStore) error {
144 for _, utxo := range utxos {
145 if segwit.IsP2WScript(utxo.ControlProgram) {
146 if err := w.AccountMgr.SetStandardUTXO(utxo.OutputID, utxo); err != nil {
150 if err := store.SetContractUTXO(utxo.OutputID, utxo); err != nil {
158 func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO {
159 utxos := []*account.UTXO{}
160 for _, inpID := range tx.Tx.InputIDs {
162 e, err := tx.Entry(inpID)
166 utxo := &account.UTXO{}
167 switch inp := e.(type) {
169 resOut, err := tx.IntraChainOutput(*inp.SpentOutputId)
171 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for spedn")
174 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
177 utxo = &account.UTXO{
178 OutputID: *inp.SpentOutputId,
179 AssetID: *resOut.Source.Value.AssetId,
180 Amount: resOut.Source.Value.Amount,
181 ControlProgram: resOut.ControlProgram.Code,
182 SourceID: *resOut.Source.Ref,
183 SourcePos: resOut.Source.Position,
186 resOut, err := tx.VoteOutput(*inp.SpentOutputId)
188 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for vetoInput")
191 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
194 utxo = &account.UTXO{
195 OutputID: *inp.SpentOutputId,
196 AssetID: *resOut.Source.Value.AssetId,
197 Amount: resOut.Source.Value.Amount,
198 ControlProgram: resOut.ControlProgram.Code,
199 SourceID: *resOut.Source.Ref,
200 SourcePos: resOut.Source.Position,
206 utxos = append(utxos, utxo)
211 func txOutToUtxos(tx *types.Tx, statusFail bool, blockHeight uint64) []*account.UTXO {
212 utxos := []*account.UTXO{}
213 for i, out := range tx.Outputs {
214 validHeight := uint64(0)
215 entryOutput, err := tx.Entry(*tx.ResultIds[i])
217 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txOutToUtxos fail on get entryOutput")
221 utxo := &account.UTXO{}
222 switch bcOut := entryOutput.(type) {
223 case *bc.IntraChainOutput:
224 if (statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID) || out.AssetAmount().Amount == uint64(0) {
228 if tx.Inputs[0].InputType() == types.CoinbaseInputType {
229 validHeight = blockHeight + consensus.ActiveNetParams.CoinbasePendingBlockNumber
232 utxo = &account.UTXO{
233 OutputID: *tx.OutputID(i),
234 AssetID: *out.AssetAmount().AssetId,
235 Amount: out.AssetAmount().Amount,
236 ControlProgram: out.ControlProgram(),
237 SourceID: *bcOut.Source.Ref,
238 SourcePos: bcOut.Source.Position,
239 ValidHeight: validHeight,
243 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
247 voteValidHeight := blockHeight + consensus.ActiveNetParams.VotePendingBlockNumber
248 if validHeight < voteValidHeight {
249 validHeight = voteValidHeight
252 utxo = &account.UTXO{
253 OutputID: *tx.OutputID(i),
254 AssetID: *out.AssetAmount().AssetId,
255 Amount: out.AssetAmount().Amount,
256 ControlProgram: out.ControlProgram(),
257 SourceID: *bcOut.Source.Ref,
258 SourcePos: bcOut.Source.Position,
259 ValidHeight: validHeight,
264 log.WithFields(log.Fields{"module": logModule}).Warn("txOutToUtxos fail on get bcOut")
268 utxos = append(utxos, utxo)