package wallet
import (
- "encoding/json"
-
log "github.com/sirupsen/logrus"
"github.com/vapor/account"
"github.com/vapor/consensus"
"github.com/vapor/consensus/segwit"
"github.com/vapor/crypto/sha3pool"
- dbm "github.com/vapor/database/leveldb"
- "github.com/vapor/errors"
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
)
// GetAccountUtxos return all account unspent outputs
func (w *Wallet) GetAccountUtxos(accountID string, id string, unconfirmed, isSmartContract bool, vote bool) []*account.UTXO {
- prefix := account.UTXOPreFix
- if isSmartContract {
- prefix = account.SUTXOPrefix
- }
-
accountUtxos := []*account.UTXO{}
if unconfirmed {
accountUtxos = w.AccountMgr.ListUnconfirmedUtxo(accountID, isSmartContract)
}
- accountUtxoIter := w.DB.IteratorPrefix([]byte(prefix + id))
- defer accountUtxoIter.Release()
-
- for accountUtxoIter.Next() {
- accountUtxo := &account.UTXO{}
- if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
- log.WithFields(log.Fields{"module": logModule, "err": err}).Warn("GetAccountUtxos fail on unmarshal utxo")
- continue
- }
+ confirmedUTXOs, err := w.Store.ListAccountUTXOs(id, isSmartContract)
+ if err != nil {
+ log.WithFields(log.Fields{"module": logModule, "err": err}).Error("GetAccountUtxos fail.")
+ }
+ accountUtxos = append(accountUtxos, confirmedUTXOs...)
+ newAccountUtxos := []*account.UTXO{}
+ for _, accountUtxo := range accountUtxos {
if vote && accountUtxo.Vote == nil {
continue
}
-
if accountID == accountUtxo.AccountID || accountID == "" {
- accountUtxos = append(accountUtxos, accountUtxo)
+ newAccountUtxos = append(newAccountUtxos, accountUtxo)
}
}
- return accountUtxos
+ return newAccountUtxos
}
-func (w *Wallet) attachUtxos(batch dbm.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
+func (w *Wallet) attachUtxos(b *types.Block, txStatus *bc.TransactionStatus, store WalletStore) {
for txIndex, tx := range b.Transactions {
statusFail, err := txStatus.GetStatus(txIndex)
if err != nil {
inputUtxos := txInToUtxos(tx, statusFail)
for _, inputUtxo := range inputUtxos {
if segwit.IsP2WScript(inputUtxo.ControlProgram) {
- batch.Delete(account.StandardUTXOKey(inputUtxo.OutputID))
+ w.AccountMgr.DeleteStandardUTXO(inputUtxo.OutputID)
} else {
- batch.Delete(account.ContractUTXOKey(inputUtxo.OutputID))
+ store.DeleteContractUTXO(inputUtxo.OutputID)
}
}
//hand update the transaction output utxos
- validHeight := uint64(0)
- if txIndex == 0 {
- validHeight = b.Height + consensus.CoinbasePendingBlockNumber
- }
- outputUtxos := txOutToUtxos(tx, statusFail, validHeight)
+ outputUtxos := txOutToUtxos(tx, statusFail, b.Height)
utxos := w.filterAccountUtxo(outputUtxos)
- if err := batchSaveUtxos(utxos, batch); err != nil {
- log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on batchSaveUtxos")
+ if err := w.saveUtxos(utxos, store); err != nil {
+ log.WithFields(log.Fields{"module": logModule, "err": err}).Error("attachUtxos fail on saveUtxos")
}
}
}
-func (w *Wallet) detachUtxos(batch dbm.Batch, b *types.Block, txStatus *bc.TransactionStatus) {
+func (w *Wallet) detachUtxos(b *types.Block, txStatus *bc.TransactionStatus, store WalletStore) {
for txIndex := len(b.Transactions) - 1; txIndex >= 0; txIndex-- {
tx := b.Transactions[txIndex]
for j := range tx.Outputs {
code := []byte{}
switch resOut := tx.Entries[*tx.ResultIds[j]].(type) {
case *bc.IntraChainOutput:
+ if resOut.Source.Value.Amount == uint64(0) {
+ continue
+ }
code = resOut.ControlProgram.Code
case *bc.VoteOutput:
code = resOut.ControlProgram.Code
}
if segwit.IsP2WScript(code) {
- batch.Delete(account.StandardUTXOKey(*tx.ResultIds[j]))
+ w.AccountMgr.DeleteStandardUTXO(*tx.ResultIds[j])
} else {
- batch.Delete(account.ContractUTXOKey(*tx.ResultIds[j]))
+ store.DeleteContractUTXO(*tx.ResultIds[j])
}
}
inputUtxos := txInToUtxos(tx, statusFail)
utxos := w.filterAccountUtxo(inputUtxos)
- if err := batchSaveUtxos(utxos, batch); err != nil {
+ if err := w.saveUtxos(utxos, store); err != nil {
log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detachUtxos fail on batchSaveUtxos")
return
}
var hash [32]byte
sha3pool.Sum256(hash[:], []byte(s))
- data := w.DB.Get(account.ContractKey(hash))
- if data == nil {
+ cp, err := w.AccountMgr.GetControlProgram(bc.NewHash(hash))
+ if err != nil && err != account.ErrFindCtrlProgram {
+ log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail.")
continue
}
-
- cp := &account.CtrlProgram{}
- if err := json.Unmarshal(data, cp); err != nil {
- log.WithFields(log.Fields{"module": logModule, "err": err}).Error("filterAccountUtxo fail on unmarshal control program")
+ if cp == nil {
continue
}
return result
}
-func batchSaveUtxos(utxos []*account.UTXO, batch dbm.Batch) error {
+func (w *Wallet) saveUtxos(utxos []*account.UTXO, store WalletStore) error {
for _, utxo := range utxos {
- data, err := json.Marshal(utxo)
- if err != nil {
- return errors.Wrap(err, "failed marshal accountutxo")
- }
-
if segwit.IsP2WScript(utxo.ControlProgram) {
- batch.Set(account.StandardUTXOKey(utxo.OutputID), data)
+ if err := w.AccountMgr.SetStandardUTXO(utxo.OutputID, utxo); err != nil {
+ return err
+ }
} else {
- batch.Set(account.ContractUTXOKey(utxo.OutputID), data)
+ if err := store.SetContractUTXO(utxo.OutputID, utxo); err != nil {
+ return err
+ }
}
}
return nil
func txInToUtxos(tx *types.Tx, statusFail bool) []*account.UTXO {
utxos := []*account.UTXO{}
for _, inpID := range tx.Tx.InputIDs {
- sp, err := tx.Spend(inpID)
- if err != nil {
- continue
- }
- entryOutput, err := tx.Entry(*sp.SpentOutputId)
+ e, err := tx.Entry(inpID)
if err != nil {
- log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get entryOutput")
continue
}
-
utxo := &account.UTXO{}
- switch resOut := entryOutput.(type) {
- case *bc.IntraChainOutput:
+ switch inp := e.(type) {
+ case *bc.Spend:
+ resOut, err := tx.IntraChainOutput(*inp.SpentOutputId)
+ if err != nil {
+ log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for spedn")
+ continue
+ }
if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
continue
}
utxo = &account.UTXO{
- OutputID: *sp.SpentOutputId,
+ OutputID: *inp.SpentOutputId,
AssetID: *resOut.Source.Value.AssetId,
Amount: resOut.Source.Value.Amount,
ControlProgram: resOut.ControlProgram.Code,
SourceID: *resOut.Source.Ref,
SourcePos: resOut.Source.Position,
}
-
- case *bc.VoteOutput:
+ case *bc.VetoInput:
+ resOut, err := tx.VoteOutput(*inp.SpentOutputId)
+ if err != nil {
+ log.WithFields(log.Fields{"module": logModule, "err": err}).Error("txInToUtxos fail on get resOut for vetoInput")
+ continue
+ }
if statusFail && *resOut.Source.Value.AssetId != *consensus.BTMAssetID {
continue
}
utxo = &account.UTXO{
- OutputID: *sp.SpentOutputId,
+ OutputID: *inp.SpentOutputId,
AssetID: *resOut.Source.Value.AssetId,
Amount: resOut.Source.Value.Amount,
ControlProgram: resOut.ControlProgram.Code,
SourcePos: resOut.Source.Position,
Vote: resOut.Vote,
}
-
default:
- log.WithFields(log.Fields{"module": logModule}).Error("txInToUtxos fail on get resOut")
continue
}
-
utxos = append(utxos, utxo)
}
return utxos
}
-func txOutToUtxos(tx *types.Tx, statusFail bool, vaildHeight uint64) []*account.UTXO {
+func txOutToUtxos(tx *types.Tx, statusFail bool, blockHeight uint64) []*account.UTXO {
+ validHeight := uint64(0)
+ if tx.Inputs[0].InputType() == types.CoinbaseInputType {
+ validHeight = blockHeight + consensus.ActiveNetParams.CoinbasePendingBlockNumber
+ }
+
utxos := []*account.UTXO{}
for i, out := range tx.Outputs {
entryOutput, err := tx.Entry(*tx.ResultIds[i])
utxo := &account.UTXO{}
switch bcOut := entryOutput.(type) {
case *bc.IntraChainOutput:
- if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
+ if (statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID) || out.AssetAmount().Amount == uint64(0) {
continue
}
utxo = &account.UTXO{
ControlProgram: out.ControlProgram(),
SourceID: *bcOut.Source.Ref,
SourcePos: bcOut.Source.Position,
- ValidHeight: vaildHeight,
+ ValidHeight: validHeight,
}
case *bc.VoteOutput:
if statusFail && *out.AssetAmount().AssetId != *consensus.BTMAssetID {
continue
}
+
+ voteValidHeight := blockHeight + consensus.ActiveNetParams.VotePendingBlockNumber
+ if validHeight < voteValidHeight {
+ validHeight = voteValidHeight
+ }
+
utxo = &account.UTXO{
OutputID: *tx.OutputID(i),
AssetID: *out.AssetAmount().AssetId,
ControlProgram: out.ControlProgram(),
SourceID: *bcOut.Source.Ref,
SourcePos: bcOut.Source.Position,
- ValidHeight: vaildHeight,
+ ValidHeight: validHeight,
Vote: bcOut.Vote,
}