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 "github.com/vapor/errors"
13 "github.com/vapor/protocol/bc"
14 "github.com/vapor/protocol/bc/types"
17 // GetAccountUtxos return all account unspent outputs
18 func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract bool, vote bool) []*account.UTXO {
19 prefix := account.UTXOPreFix
21 prefix = account.SUTXOPrefix
24 accountUtxos := []*account.UTXO{}
26 accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract)
29 confirmedUtxos := w.store.GetAccountUTXOs(prefix + id)
30 accountUtxos = append(accountUtxos, confirmedUtxos...)
31 newAccountUtxos := []*account.UTXO{}
32 for _, accountUtxo := range accountUtxos {
33 if vote && accountUtxo.Vote == nil {
37 if accountID == accountUtxo.AccountID || accountID == "" {
38 newAccountUtxos = append(newAccountUtxos, accountUtxo)
41 return newAccountUtxos
44 func (w *Wallet) attachUtxos(b *types.Block, txStatus *bc.TransactionStatus) {
45 for txIndex, tx := range b.Transactions {
46 statusFail, err := txStatus.GetStatus(txIndex)
48 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on get tx status")
52 //hand update the transaction input utxos
53 inputUtxos := txInToUtxos(tx, statusFail)
54 for _, inputUtxo := range inputUtxos {
55 if segwit.IsP2WScript(inputUtxo.ControlProgram) {
56 w.store.DeleteStardardUTXO(inputUtxo.OutputID)
58 w.store.DeleteContractUTXO(inputUtxo.OutputID)
62 //hand update the transaction output utxos
63 outputUtxos := txOutToUtxos(tx, statusFail, b.Height)
64 utxos := w.filterAccountUtxo(outputUtxos)
65 if err := w.saveUtxos(utxos); err != nil {
66 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on batchSaveUtxos")
71 func (w *Wallet) detachUtxos(b *types.Block, txStatus *bc.TransactionStatus) {
72 for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- {
73 tx := b.Transactions[txIndex]
74 for j := range tx.Outputs {
76 switch resOut := tx.Entries[*tx.ResultIds[j]].(type) {
77 case *bc.IntraChainOutput:
78 code = resOut.ControlProgram.Code
80 code = resOut.ControlProgram.Code
85 if segwit.IsP2WScript(code) {
86 w.store.DeleteStardardUTXO(*tx.ResultIds[j])
88 w.store.DeleteContractUTXO(*tx.ResultIds[j])
92 statusFail, err := txStatus.GetStatus(txIndex)
94 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on get tx status")
98 inputUtxos := txInToUtxos(tx, statusFail)
99 utxos := w.filterAccountUtxo(inputUtxos)
100 if err := w.saveUtxos(utxos); err != nil {
101 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on batchSaveUtxos")
107 func (w *Wallet) filterAccountUtxo(utxos []*account.UTXO) []*account.UTXO {
108 outsByScript := make(map[string][]*account.UTXO, len(utxos))
109 for _, utxo := range utxos {
110 scriptStr := string(utxo.ControlProgram)
111 outsByScript[scriptStr] = append(outsByScript[scriptStr], utxo)
114 result := make([]*account.UTXO, 0, len(utxos))
115 for s := range outsByScript {
116 if !segwit.IsP2WScript([]byte(s)) {
117 for _, utxo := range outsByScript[s] {
118 result = append(result, utxo)
124 sha3pool.Sum256(hash[:], []byte(s))
125 data := w.store.GetRawProgramByHash(hash)
130 cp := &account.CtrlProgram{}
131 if err := json.Unmarshal(data, cp); err != nil {
132 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail on unmarshal control program")
136 for _, utxo := range outsByScript[s] {
137 utxo.AccountID = cp.AccountID
138 utxo.Address = cp.Address
139 utxo.ControlProgramIndex = cp.KeyIndex
140 utxo.Change = cp.Change
141 result = append(result, utxo)
147 func (w *Wallet) saveUtxos(utxos []*account.UTXO) error {
148 for _, utxo := range utxos {
149 data, err := json.Marshal(utxo)
151 return errors.Wrap(err, "failed marshal accountutxo")
154 if segwit.IsP2WScript(utxo.ControlProgram) {
155 w.store.SetStandardUTXO(utxo.OutputID, data)
157 w.store.SetContractUTXO(utxo.OutputID, data)
163 func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO {
164 utxos := []*account.UTXO{}
165 for _, inpID := range tx.Tx.InputIDs {
167 e, err := tx.Entry(inpID)
171 utxo := &account.UTXO{}
172 switch inp := e.(type) {
174 resOut, err := tx.IntraChainOutput(*inp.SpentOutputId)
176 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for spedn")
179 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
182 utxo = &account.UTXO{
183 OutputID: *inp.SpentOutputId,
184 AssetID: *resOut.Source.Value.AssetId,
185 Amount: resOut.Source.Value.Amount,
186 ControlProgram: resOut.ControlProgram.Code,
187 SourceID: *resOut.Source.Ref,
188 SourcePos: resOut.Source.Position,
191 resOut, err := tx.VoteOutput(*inp.SpentOutputId)
193 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for vetoInput")
196 if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
199 utxo = &account.UTXO{
200 OutputID: *inp.SpentOutputId,
201 AssetID: *resOut.Source.Value.AssetId,
202 Amount: resOut.Source.Value.Amount,
203 ControlProgram: resOut.ControlProgram.Code,
204 SourceID: *resOut.Source.Ref,
205 SourcePos: resOut.Source.Position,
211 utxos = append(utxos, utxo)
216 func txOutToUtxos(tx *types.Tx, statusFail bool, blockHeight uint64) []*account.UTXO {
217 validHeight := uint64(0)
218 if tx.Inputs[0].InputType() == types.CoinbaseInputType {
219 validHeight = blockHeight + consensus.CoinbasePendingBlockNumber
222 utxos := []*account.UTXO{}
223 for i, out := range tx.Outputs {
224 entryOutput, err := tx.Entry(*tx.ResultIds[i])
226 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txOutToUtxos fail on get entryOutput")
230 utxo := &account.UTXO{}
231 switch bcOut := entryOutput.(type) {
232 case *bc.IntraChainOutput:
233 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
236 utxo = &account.UTXO{
237 OutputID: *tx.OutputID(i),
238 AssetID: *out.AssetAmount().AssetId,
239 Amount: out.AssetAmount().Amount,
240 ControlProgram: out.ControlProgram(),
241 SourceID: *bcOut.Source.Ref,
242 SourcePos: bcOut.Source.Position,
243 ValidHeight: validHeight,
247 if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
251 voteValidHeight := blockHeight + consensus.VotePendingBlockNumber
252 if validHeight < voteValidHeight {
253 validHeight = voteValidHeight
256 utxo = &account.UTXO{
257 OutputID: *tx.OutputID(i),
258 AssetID: *out.AssetAmount().AssetId,
259 Amount: out.AssetAmount().Amount,
260 ControlProgram: out.ControlProgram(),
261 SourceID: *bcOut.Source.Ref,
262 SourcePos: bcOut.Source.Position,
263 ValidHeight: validHeight,
268 log.WithFields(log.Fields{"module": logModule}).Warn("txOutToUtxos fail on get bcOut")
272 utxos = append(utxos, utxo)