package account
import (
+ "bytes"
"container/list"
- "encoding/json"
"sort"
"sync"
"sync/atomic"
"time"
log "github.com/sirupsen/logrus"
-
- dbm "github.com/vapor/database/leveldb"
"github.com/vapor/errors"
"github.com/vapor/protocol/bc"
)
var (
ErrInsufficient = errors.New("reservation found insufficient funds")
ErrImmature = errors.New("reservation found immature funds")
+ ErrVoteLock = errors.New("Locked by the vote")
ErrReserved = errors.New("reservation found outputs already reserved")
ErrMatchUTXO = errors.New("can't find utxo with given hash")
ErrReservation = errors.New("couldn't find reservation")
Amount uint64
SourcePos uint64
ControlProgram []byte
+ Vote []byte
AccountID string
Address string
ControlProgramIndex uint64
// `sync/atomic` expects the first word in an allocated struct to be 64-bit
// aligned on both ARM and x86-32. See https://goo.gl/zW7dgq for more details.
nextIndex uint64
- db dbm.DB
+ store AccountStore
mtx sync.RWMutex
currentHeight func() uint64
reservations map[uint64]*reservation
}
-func newUtxoKeeper(f func() uint64, walletdb dbm.DB) *utxoKeeper {
+func newUtxoKeeper(f func() uint64, store AccountStore) *utxoKeeper {
uk := &utxoKeeper{
- db: walletdb,
+ store: store,
currentHeight: f,
unconfirmed: make(map[bc.Hash]*UTXO),
reserved: make(map[bc.Hash]uint64),
}
}
-func (uk *utxoKeeper) Reserve(accountID string, assetID *bc.AssetID, amount uint64, useUnconfirmed bool, exp time.Time) (*reservation, error) {
+func (uk *utxoKeeper) Reserve(accountID string, assetID *bc.AssetID, amount uint64, useUnconfirmed bool, vote []byte, exp time.Time) (*reservation, error) {
uk.mtx.Lock()
defer uk.mtx.Unlock()
- utxos, immatureAmount := uk.findUtxos(accountID, assetID, useUnconfirmed)
+ utxos, immatureAmount := uk.findUtxos(accountID, assetID, useUnconfirmed, vote)
optUtxos, optAmount, reservedAmount := uk.optUTXOs(utxos, amount)
if optAmount+reservedAmount+immatureAmount < amount {
return nil, ErrInsufficient
}
if optAmount+reservedAmount < amount {
+ if vote != nil {
+ return nil, ErrVoteLock
+ }
return nil, ErrImmature
}
}
}
-func (uk *utxoKeeper) findUtxos(accountID string, assetID *bc.AssetID, useUnconfirmed bool) ([]*UTXO, uint64) {
+func (uk *utxoKeeper) findUtxos(accountID string, assetID *bc.AssetID, useUnconfirmed bool, vote []byte) ([]*UTXO, uint64) {
immatureAmount := uint64(0)
currentHeight := uk.currentHeight()
utxos := []*UTXO{}
appendUtxo := func(u *UTXO) {
- if u.AccountID != accountID || u.AssetID != *assetID {
+ if u.AccountID != accountID || u.AssetID != *assetID || !bytes.Equal(u.Vote, vote) {
return
}
+
if u.ValidHeight > currentHeight {
immatureAmount += u.Amount
} else {
}
}
- utxoIter := uk.db.IteratorPrefix([]byte(UTXOPreFix))
- defer utxoIter.Release()
- for utxoIter.Next() {
- u := &UTXO{}
- if err := json.Unmarshal(utxoIter.Value(), u); err != nil {
- log.WithFields(log.Fields{"module": logModule, "err": err}).Error("utxoKeeper findUtxos fail on unmarshal utxo")
- continue
- }
- appendUtxo(u)
+ UTXOs, err := uk.store.ListUTXOs()
+ if err != nil {
+ log.WithFields(log.Fields{"module": logModule, "err": err}).Error("utxoKeeper findUtxos fail on unmarshal utxo")
}
+
+ for _, UTXO := range UTXOs {
+ appendUtxo(UTXO)
+ }
+
if !useUnconfirmed {
return utxos, immatureAmount
}
if u, ok := uk.unconfirmed[outHash]; useUnconfirmed && ok {
return u, nil
}
-
- u := &UTXO{}
- if data := uk.db.Get(StandardUTXOKey(outHash)); data != nil {
- return u, json.Unmarshal(data, u)
- }
- if data := uk.db.Get(ContractUTXOKey(outHash)); data != nil {
- return u, json.Unmarshal(data, u)
- }
- return nil, ErrMatchUTXO
+ return uk.store.GetUTXO(outHash)
}
func (uk *utxoKeeper) optUTXOs(utxos []*UTXO, amount uint64) ([]*UTXO, uint64, uint64) {