[lbry] coin selection and balance no longer include stakes
This commit is contained in:
parent
4c6e495f86
commit
d5328e1834
12 changed files with 111 additions and 66 deletions
rpc
wallet
wtxmgr
|
@ -203,6 +203,7 @@ message FundTransactionRequest {
|
||||||
int32 required_confirmations = 3;
|
int32 required_confirmations = 3;
|
||||||
bool include_immature_coinbases = 4;
|
bool include_immature_coinbases = 4;
|
||||||
bool include_change_script = 5;
|
bool include_change_script = 5;
|
||||||
|
bool include_stakes = 6;
|
||||||
}
|
}
|
||||||
message FundTransactionResponse {
|
message FundTransactionResponse {
|
||||||
message PreviousOutput {
|
message PreviousOutput {
|
||||||
|
|
|
@ -585,7 +585,7 @@ func getBalance(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
accountName = *cmd.Account
|
accountName = *cmd.Account
|
||||||
}
|
}
|
||||||
if accountName == "*" {
|
if accountName == "*" {
|
||||||
balance, err = w.CalculateBalance(int32(*cmd.MinConf))
|
balance, _, err = w.CalculateBalance(int32(*cmd.MinConf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -640,7 +640,7 @@ func getInfo(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient) (
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bal, err := w.CalculateBalance(1)
|
bal, staked, err := w.CalculateBalance(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -649,6 +649,8 @@ func getInfo(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient) (
|
||||||
// to using the manager version.
|
// to using the manager version.
|
||||||
info.WalletVersion = int32(waddrmgr.LatestMgrVersion)
|
info.WalletVersion = int32(waddrmgr.LatestMgrVersion)
|
||||||
info.Balance = bal.ToBTC()
|
info.Balance = bal.ToBTC()
|
||||||
|
_ = staked // TODO: add this to lbcd:
|
||||||
|
// info.Staked = staked.ToBTC()
|
||||||
info.PaytxFee = float64(txrules.DefaultRelayFeePerKb)
|
info.PaytxFee = float64(txrules.DefaultRelayFeePerKb)
|
||||||
// We don't set the following since they don't make much sense in the
|
// We don't set the following since they don't make much sense in the
|
||||||
// wallet architecture:
|
// wallet architecture:
|
||||||
|
@ -834,7 +836,6 @@ func lookupKeyScope(kind *string) (waddrmgr.KeyScope, error) {
|
||||||
if kind == nil {
|
if kind == nil {
|
||||||
return waddrmgr.KeyScopeBIP0044, nil
|
return waddrmgr.KeyScopeBIP0044, nil
|
||||||
}
|
}
|
||||||
// must be one of legacy / p2pkh or p2sh-p2wkh / p2sh-segwit, or p2wkh / bech32
|
|
||||||
switch strings.ToLower(*kind) {
|
switch strings.ToLower(*kind) {
|
||||||
case "legacy", "p2pkh": // could add "default" but it might be confused with the 1st parameter
|
case "legacy", "p2pkh": // could add "default" but it might be confused with the 1st parameter
|
||||||
return waddrmgr.KeyScopeBIP0044, nil
|
return waddrmgr.KeyScopeBIP0044, nil
|
||||||
|
|
|
@ -306,6 +306,7 @@ func (s *walletServer) FundTransaction(ctx context.Context, req *pb.FundTransact
|
||||||
policy := wallet.OutputSelectionPolicy{
|
policy := wallet.OutputSelectionPolicy{
|
||||||
Account: req.Account,
|
Account: req.Account,
|
||||||
RequiredConfirmations: req.RequiredConfirmations,
|
RequiredConfirmations: req.RequiredConfirmations,
|
||||||
|
IncludeStakes: req.IncludeStakes,
|
||||||
}
|
}
|
||||||
unspentOutputs, err := s.wallet.UnspentOutputs(policy)
|
unspentOutputs, err := s.wallet.UnspentOutputs(policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -892,6 +892,7 @@ type FundTransactionRequest struct {
|
||||||
RequiredConfirmations int32 `protobuf:"varint,3,opt,name=required_confirmations,json=requiredConfirmations" json:"required_confirmations,omitempty"`
|
RequiredConfirmations int32 `protobuf:"varint,3,opt,name=required_confirmations,json=requiredConfirmations" json:"required_confirmations,omitempty"`
|
||||||
IncludeImmatureCoinbases bool `protobuf:"varint,4,opt,name=include_immature_coinbases,json=includeImmatureCoinbases" json:"include_immature_coinbases,omitempty"`
|
IncludeImmatureCoinbases bool `protobuf:"varint,4,opt,name=include_immature_coinbases,json=includeImmatureCoinbases" json:"include_immature_coinbases,omitempty"`
|
||||||
IncludeChangeScript bool `protobuf:"varint,5,opt,name=include_change_script,json=includeChangeScript" json:"include_change_script,omitempty"`
|
IncludeChangeScript bool `protobuf:"varint,5,opt,name=include_change_script,json=includeChangeScript" json:"include_change_script,omitempty"`
|
||||||
|
IncludeStakes bool `protobuf:"varint,6,opt,name=include_stakes,json=includeStakes" json:"include_stakes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *FundTransactionRequest) Reset() { *m = FundTransactionRequest{} }
|
func (m *FundTransactionRequest) Reset() { *m = FundTransactionRequest{} }
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/lbryio/lbcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
btcutil "github.com/lbryio/lbcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
@ -41,8 +42,18 @@ type OutputKind byte
|
||||||
const (
|
const (
|
||||||
OutputKindNormal OutputKind = iota
|
OutputKindNormal OutputKind = iota
|
||||||
OutputKindCoinbase
|
OutputKindCoinbase
|
||||||
|
OutputKindStake
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func isStake(pkScript []byte) bool {
|
||||||
|
if len(pkScript) > 0 &&
|
||||||
|
(pkScript[0] == txscript.OP_CLAIMNAME || pkScript[0] == txscript.OP_SUPPORTCLAIM ||
|
||||||
|
pkScript[0] == txscript.OP_UPDATECLAIM) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionOutput describes an output that was or is at least partially
|
// TransactionOutput describes an output that was or is at least partially
|
||||||
// controlled by the wallet. Depending on context, this could refer to an
|
// controlled by the wallet. Depending on context, this could refer to an
|
||||||
// unspent output, or a spent one.
|
// unspent output, or a spent one.
|
||||||
|
|
|
@ -316,6 +316,11 @@ func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx,
|
||||||
if err != nil || len(addrs) != 1 {
|
if err != nil || len(addrs) != 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isStake(output.PkScript) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
scopedMgr, addrAcct, err := w.Manager.AddrAccount(addrmgrNs, addrs[0])
|
scopedMgr, addrAcct, err := w.Manager.AddrAccount(addrmgrNs, addrs[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -29,6 +29,7 @@ var (
|
||||||
type OutputSelectionPolicy struct {
|
type OutputSelectionPolicy struct {
|
||||||
Account uint32
|
Account uint32
|
||||||
RequiredConfirmations int32
|
RequiredConfirmations int32
|
||||||
|
IncludeStakes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *OutputSelectionPolicy) meetsRequiredConfs(txHeight, curHeight int32) bool {
|
func (p *OutputSelectionPolicy) meetsRequiredConfs(txHeight, curHeight int32) bool {
|
||||||
|
@ -84,6 +85,13 @@ func (w *Wallet) UnspentOutputs(policy OutputSelectionPolicy) ([]*TransactionOut
|
||||||
outputSource = OutputKindCoinbase
|
outputSource = OutputKindCoinbase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isStake(output.PkScript) {
|
||||||
|
if !policy.IncludeStakes {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
outputSource = OutputKindStake
|
||||||
|
}
|
||||||
|
|
||||||
result := &TransactionOutput{
|
result := &TransactionOutput{
|
||||||
OutPoint: output.OutPoint,
|
OutPoint: output.OutPoint,
|
||||||
Output: wire.TxOut{
|
Output: wire.TxOut{
|
||||||
|
|
|
@ -1462,16 +1462,17 @@ func (w *Wallet) AccountAddresses(account uint32) (addrs []btcutil.Address, err
|
||||||
// a UTXO must be in a block. If confirmations is 1 or greater,
|
// a UTXO must be in a block. If confirmations is 1 or greater,
|
||||||
// the balance will be calculated based on how many how many blocks
|
// the balance will be calculated based on how many how many blocks
|
||||||
// include a UTXO.
|
// include a UTXO.
|
||||||
func (w *Wallet) CalculateBalance(confirms int32) (btcutil.Amount, error) {
|
func (w *Wallet) CalculateBalance(confirms int32) (btcutil.Amount, btcutil.Amount, error) {
|
||||||
var balance btcutil.Amount
|
var balance btcutil.Amount
|
||||||
|
var staked btcutil.Amount
|
||||||
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||||
txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
|
txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
|
||||||
var err error
|
var err error
|
||||||
blk := w.Manager.SyncedTo()
|
blk := w.Manager.SyncedTo()
|
||||||
balance, err = w.TxStore.Balance(txmgrNs, confirms, blk.Height)
|
balance, staked, err = w.TxStore.Balance(txmgrNs, confirms, blk.Height)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
return balance, err
|
return balance, staked, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Balances records total, spendable (by policy), and immature coinbase
|
// Balances records total, spendable (by policy), and immature coinbase
|
||||||
|
@ -1511,7 +1512,7 @@ func (w *Wallet) CalculateAccountBalances(account uint32, confirms int32) (Balan
|
||||||
if err == nil && len(addrs) > 0 {
|
if err == nil && len(addrs) > 0 {
|
||||||
_, outputAcct, err = w.Manager.AddrAccount(addrmgrNs, addrs[0])
|
_, outputAcct, err = w.Manager.AddrAccount(addrmgrNs, addrs[0])
|
||||||
}
|
}
|
||||||
if err != nil || outputAcct != account {
|
if err != nil || outputAcct != account || isStake(output.PkScript) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2478,6 +2479,9 @@ func (w *Wallet) AccountBalances(scope waddrmgr.KeyScope,
|
||||||
if err != nil || len(addrs) == 0 {
|
if err != nil || len(addrs) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if isStake(output.PkScript) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
outputAcct, err := manager.AddrAccount(addrmgrNs, addrs[0])
|
outputAcct, err := manager.AddrAccount(addrmgrNs, addrs[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
@ -2651,7 +2655,8 @@ func (w *Wallet) ListUnspent(minconf, maxconf int32,
|
||||||
ScriptPubKey: hex.EncodeToString(output.PkScript),
|
ScriptPubKey: hex.EncodeToString(output.PkScript),
|
||||||
Amount: output.Amount.ToBTC(),
|
Amount: output.Amount.ToBTC(),
|
||||||
Confirmations: int64(confs),
|
Confirmations: int64(confs),
|
||||||
Spendable: spendable,
|
Spendable: spendable, // presently false for stakes
|
||||||
|
// TODO: add an IsStake flag here to lbcd
|
||||||
}
|
}
|
||||||
|
|
||||||
// BUG: this should be a JSON array so that all
|
// BUG: this should be a JSON array so that all
|
||||||
|
|
27
wtxmgr/db.go
27
wtxmgr/db.go
|
@ -543,9 +543,7 @@ func keyCredit(txHash *chainhash.Hash, index uint32, block *Block) []byte {
|
||||||
func valueUnspentCredit(cred *credit) []byte {
|
func valueUnspentCredit(cred *credit) []byte {
|
||||||
v := make([]byte, 9)
|
v := make([]byte, 9)
|
||||||
byteOrder.PutUint64(v, uint64(cred.amount))
|
byteOrder.PutUint64(v, uint64(cred.amount))
|
||||||
if cred.change {
|
v[8] = cred.flags
|
||||||
v[8] |= 1 << 1
|
|
||||||
}
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,13 +596,13 @@ func fetchRawCreditAmountSpent(v []byte) (btcutil.Amount, bool, error) {
|
||||||
|
|
||||||
// fetchRawCreditAmountChange returns the amount of the credit and whether the
|
// fetchRawCreditAmountChange returns the amount of the credit and whether the
|
||||||
// credit is marked as change.
|
// credit is marked as change.
|
||||||
func fetchRawCreditAmountChange(v []byte) (btcutil.Amount, bool, error) {
|
func fetchRawCreditAmountChange(v []byte) (btcutil.Amount, byte, error) {
|
||||||
if len(v) < 9 {
|
if len(v) < 9 {
|
||||||
str := fmt.Sprintf("%s: short read (expected %d bytes, read %d)",
|
str := fmt.Sprintf("%s: short read (expected %d bytes, read %d)",
|
||||||
bucketCredits, 9, len(v))
|
bucketCredits, 9, len(v))
|
||||||
return 0, false, storeError(ErrData, str, nil)
|
return 0, 0, storeError(ErrData, str, nil)
|
||||||
}
|
}
|
||||||
return btcutil.Amount(byteOrder.Uint64(v)), v[8]&(1<<1) != 0, nil
|
return btcutil.Amount(byteOrder.Uint64(v)), v[8], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchRawCreditUnspentValue returns the unspent value for a raw credit key.
|
// fetchRawCreditUnspentValue returns the unspent value for a raw credit key.
|
||||||
|
@ -1037,12 +1035,10 @@ func deleteRawUnmined(ns walletdb.ReadWriteBucket, k []byte) error {
|
||||||
// [8] Flags (1 byte)
|
// [8] Flags (1 byte)
|
||||||
// 0x02: Change
|
// 0x02: Change
|
||||||
|
|
||||||
func valueUnminedCredit(amount btcutil.Amount, change bool) []byte {
|
func valueUnminedCredit(amount btcutil.Amount, flags byte) []byte {
|
||||||
v := make([]byte, 9)
|
v := make([]byte, 9)
|
||||||
byteOrder.PutUint64(v, uint64(amount))
|
byteOrder.PutUint64(v, uint64(amount))
|
||||||
if change {
|
v[8] = flags
|
||||||
v[8] = 1 << 1
|
|
||||||
}
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,14 +1067,13 @@ func fetchRawUnminedCreditAmount(v []byte) (btcutil.Amount, error) {
|
||||||
return btcutil.Amount(byteOrder.Uint64(v)), nil
|
return btcutil.Amount(byteOrder.Uint64(v)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchRawUnminedCreditAmountChange(v []byte) (btcutil.Amount, bool, error) {
|
func fetchRawUnminedCreditAmountChange(v []byte) (btcutil.Amount, byte, error) {
|
||||||
if len(v) < 9 {
|
if len(v) < 9 {
|
||||||
str := "short unmined credit value"
|
str := "short unmined credit value"
|
||||||
return 0, false, storeError(ErrData, str, nil)
|
return 0, 0, storeError(ErrData, str, nil)
|
||||||
}
|
}
|
||||||
amt := btcutil.Amount(byteOrder.Uint64(v))
|
amt := btcutil.Amount(byteOrder.Uint64(v))
|
||||||
change := v[8]&(1<<1) != 0
|
return amt, v[8], nil
|
||||||
return amt, change, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func existsRawUnminedCredit(ns walletdb.ReadBucket, k []byte) []byte {
|
func existsRawUnminedCredit(ns walletdb.ReadBucket, k []byte) []byte {
|
||||||
|
@ -1146,14 +1141,14 @@ func (it *unminedCreditIterator) readElem() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
amount, change, err := fetchRawUnminedCreditAmountChange(it.cv)
|
amount, flags, err := fetchRawUnminedCreditAmountChange(it.cv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
it.elem.Index = index
|
it.elem.Index = index
|
||||||
it.elem.Amount = amount
|
it.elem.Amount = amount
|
||||||
it.elem.Change = change
|
it.elem.Change = (flags & ChangeFlag) > 0
|
||||||
// Spent intentionally not set
|
// Spent intentionally not set
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -232,7 +232,7 @@ func Example_basicUsage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the one confirmation balance.
|
// Print the one confirmation balance.
|
||||||
bal, err := s.Balance(b, 1, 100)
|
bal, _, err := s.Balance(b, 1, 100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
|
|
75
wtxmgr/tx.go
75
wtxmgr/tx.go
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/lbryio/lbcd/blockchain"
|
"github.com/lbryio/lbcd/blockchain"
|
||||||
"github.com/lbryio/lbcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/lbryio/lbcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
btcutil "github.com/lbryio/lbcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
"github.com/lbryio/lbcwallet/walletdb"
|
"github.com/lbryio/lbcwallet/walletdb"
|
||||||
|
@ -24,6 +25,9 @@ import (
|
||||||
const (
|
const (
|
||||||
// TxLabelLimit is the length limit we impose on transaction labels.
|
// TxLabelLimit is the length limit we impose on transaction labels.
|
||||||
TxLabelLimit = 500
|
TxLabelLimit = 500
|
||||||
|
|
||||||
|
ChangeFlag = 2
|
||||||
|
StakeFlag = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -114,7 +118,7 @@ type credit struct {
|
||||||
outPoint wire.OutPoint
|
outPoint wire.OutPoint
|
||||||
block Block
|
block Block
|
||||||
amount btcutil.Amount
|
amount btcutil.Amount
|
||||||
change bool
|
flags byte
|
||||||
spentBy indexedIncidence // Index == ^uint32(0) if unspent
|
spentBy indexedIncidence // Index == ^uint32(0) if unspent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,14 +308,14 @@ func (s *Store) updateMinedBalance(ns walletdb.ReadWriteBucket, rec *TxRecord,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
amount, change, err := fetchRawUnminedCreditAmountChange(it.cv)
|
amount, flags, err := fetchRawUnminedCreditAmountChange(it.cv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cred.outPoint.Index = index
|
cred.outPoint.Index = index
|
||||||
cred.amount = amount
|
cred.amount = amount
|
||||||
cred.change = change
|
cred.flags = flags
|
||||||
|
|
||||||
if err := putUnspentCredit(ns, &cred); err != nil {
|
if err := putUnspentCredit(ns, &cred); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -494,6 +498,11 @@ func (s *Store) AddCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *Blo
|
||||||
// bool return specifies whether the unspent output is newly added (true) or a
|
// bool return specifies whether the unspent output is newly added (true) or a
|
||||||
// duplicate (false).
|
// duplicate (false).
|
||||||
func (s *Store) addCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) (bool, error) {
|
func (s *Store) addCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) (bool, error) {
|
||||||
|
flags := isStake(rec.MsgTx.TxOut[index])
|
||||||
|
if change {
|
||||||
|
flags |= ChangeFlag
|
||||||
|
}
|
||||||
|
|
||||||
if block == nil {
|
if block == nil {
|
||||||
// If the outpoint that we should mark as credit already exists
|
// If the outpoint that we should mark as credit already exists
|
||||||
// within the store, either as unconfirmed or confirmed, then we
|
// within the store, either as unconfirmed or confirmed, then we
|
||||||
|
@ -507,7 +516,7 @@ func (s *Store) addCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *Blo
|
||||||
rec.Hash.String())
|
rec.Hash.String())
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
v := valueUnminedCredit(btcutil.Amount(rec.MsgTx.TxOut[index].Value), change)
|
v := valueUnminedCredit(btcutil.Amount(rec.MsgTx.TxOut[index].Value), flags)
|
||||||
return true, putRawUnminedCredit(ns, k, v)
|
return true, putRawUnminedCredit(ns, k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,7 +536,7 @@ func (s *Store) addCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *Blo
|
||||||
},
|
},
|
||||||
block: block.Block,
|
block: block.Block,
|
||||||
amount: txOutAmt,
|
amount: txOutAmt,
|
||||||
change: change,
|
flags: flags,
|
||||||
spentBy: indexedIncidence{index: ^uint32(0)},
|
spentBy: indexedIncidence{index: ^uint32(0)},
|
||||||
}
|
}
|
||||||
v = valueUnspentCredit(&cred)
|
v = valueUnspentCredit(&cred)
|
||||||
|
@ -548,6 +557,15 @@ func (s *Store) addCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *Blo
|
||||||
return true, putUnspent(ns, &cred.outPoint, &block.Block)
|
return true, putUnspent(ns, &cred.outPoint, &block.Block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isStake(out *wire.TxOut) byte {
|
||||||
|
if len(out.PkScript) > 0 &&
|
||||||
|
(out.PkScript[0] == txscript.OP_CLAIMNAME || out.PkScript[0] == txscript.OP_SUPPORTCLAIM ||
|
||||||
|
out.PkScript[0] == txscript.OP_UPDATECLAIM) {
|
||||||
|
return StakeFlag
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// Rollback removes all blocks at height onwards, moving any transactions within
|
// Rollback removes all blocks at height onwards, moving any transactions within
|
||||||
// each block to the unconfirmed pool.
|
// each block to the unconfirmed pool.
|
||||||
func (s *Store) Rollback(ns walletdb.ReadWriteBucket, height int32) error {
|
func (s *Store) Rollback(ns walletdb.ReadWriteBucket, height int32) error {
|
||||||
|
@ -710,12 +728,12 @@ func (s *Store) rollback(ns walletdb.ReadWriteBucket, height int32) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
amt, change, err := fetchRawCreditAmountChange(v)
|
amt, flags, err := fetchRawCreditAmountChange(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
outPointKey := canonicalOutPoint(&rec.Hash, uint32(i))
|
outPointKey := canonicalOutPoint(&rec.Hash, uint32(i))
|
||||||
unminedCredVal := valueUnminedCredit(amt, change)
|
unminedCredVal := valueUnminedCredit(amt, flags)
|
||||||
err = putRawUnminedCredit(ns, outPointKey, unminedCredVal)
|
err = putRawUnminedCredit(ns, outPointKey, unminedCredVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -918,14 +936,14 @@ func (s *Store) UnspentOutputs(ns walletdb.ReadBucket) ([]Credit, error) {
|
||||||
//
|
//
|
||||||
// Balance may return unexpected results if syncHeight is lower than the block
|
// Balance may return unexpected results if syncHeight is lower than the block
|
||||||
// height of the most recent mined transaction in the store.
|
// height of the most recent mined transaction in the store.
|
||||||
func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32) (btcutil.Amount, error) {
|
func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32) (btcutil.Amount, btcutil.Amount, error) {
|
||||||
bal, err := fetchMinedBalance(ns)
|
bal, err := fetchMinedBalance(ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtract the balance for each credit that is spent by an unmined
|
// Subtract the balance for each credit that is spent by an unmined transaction or moved to stake.
|
||||||
// transaction.
|
var staked btcutil.Amount
|
||||||
var op wire.OutPoint
|
var op wire.OutPoint
|
||||||
var block Block
|
var block Block
|
||||||
err = ns.NestedReadBucket(bucketUnspent).ForEach(func(k, v []byte) error {
|
err = ns.NestedReadBucket(bucketUnspent).ForEach(func(k, v []byte) error {
|
||||||
|
@ -938,14 +956,15 @@ func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, c := existsCredit(ns, &op.Hash, op.Index, &block)
|
||||||
|
amt, flags, err := fetchRawCreditAmountChange(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Subtract the output's amount if it's locked.
|
// Subtract the output's amount if it's locked.
|
||||||
_, _, isLocked := isLockedOutput(ns, op, s.clock.Now())
|
_, _, isLocked := isLockedOutput(ns, op, s.clock.Now())
|
||||||
if isLocked {
|
if isLocked {
|
||||||
_, v := existsCredit(ns, &op.Hash, op.Index, &block)
|
|
||||||
amt, err := fetchRawCreditAmount(v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bal -= amt
|
bal -= amt
|
||||||
|
|
||||||
// To prevent decrementing the balance twice if the
|
// To prevent decrementing the balance twice if the
|
||||||
|
@ -954,22 +973,20 @@ func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32)
|
||||||
}
|
}
|
||||||
|
|
||||||
if existsRawUnminedInput(ns, k) != nil {
|
if existsRawUnminedInput(ns, k) != nil {
|
||||||
_, v := existsCredit(ns, &op.Hash, op.Index, &block)
|
|
||||||
amt, err := fetchRawCreditAmount(v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bal -= amt
|
bal -= amt
|
||||||
|
} else if (flags & StakeFlag) > 0 {
|
||||||
|
bal -= amt
|
||||||
|
staked += amt
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(Error); ok {
|
if _, ok := err.(Error); ok {
|
||||||
return 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
str := "failed iterating unspent outputs"
|
str := "failed iterating unspent outputs"
|
||||||
return 0, storeError(ErrDatabase, str, err)
|
return 0, 0, storeError(ErrDatabase, str, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrement the balance for any unspent credit with less than
|
// Decrement the balance for any unspent credit with less than
|
||||||
|
@ -992,7 +1009,7 @@ func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32)
|
||||||
txHash := &block.transactions[i]
|
txHash := &block.transactions[i]
|
||||||
rec, err := fetchTxRecord(ns, txHash, &block.Block)
|
rec, err := fetchTxRecord(ns, txHash, &block.Block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
numOuts := uint32(len(rec.MsgTx.TxOut))
|
numOuts := uint32(len(rec.MsgTx.TxOut))
|
||||||
for i := uint32(0); i < numOuts; i++ {
|
for i := uint32(0); i < numOuts; i++ {
|
||||||
|
@ -1017,7 +1034,7 @@ func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32)
|
||||||
}
|
}
|
||||||
amt, spent, err := fetchRawCreditAmountSpent(v)
|
amt, spent, err := fetchRawCreditAmountSpent(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
if spent {
|
if spent {
|
||||||
continue
|
continue
|
||||||
|
@ -1031,7 +1048,7 @@ func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if blockIt.err != nil {
|
if blockIt.err != nil {
|
||||||
return 0, blockIt.err
|
return 0, 0, blockIt.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If unmined outputs are included, increment the balance for each
|
// If unmined outputs are included, increment the balance for each
|
||||||
|
@ -1064,14 +1081,14 @@ func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(Error); ok {
|
if _, ok := err.(Error); ok {
|
||||||
return 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
str := "failed to iterate over unmined credits bucket"
|
str := "failed to iterate over unmined credits bucket"
|
||||||
return 0, storeError(ErrDatabase, str, err)
|
return 0, 0, storeError(ErrDatabase, str, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bal, nil
|
return bal, staked, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutTxLabel validates transaction labels and writes them to disk if they
|
// PutTxLabel validates transaction labels and writes them to disk if they
|
||||||
|
|
|
@ -526,14 +526,14 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
|
||||||
t.Fatalf("%s: got error: %v", test.name, err)
|
t.Fatalf("%s: got error: %v", test.name, err)
|
||||||
}
|
}
|
||||||
s = tmpStore
|
s = tmpStore
|
||||||
bal, err := s.Balance(ns, 1, TstRecvCurrentHeight)
|
bal, _, err := s.Balance(ns, 1, TstRecvCurrentHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s: Confirmed Balance failed: %v", test.name, err)
|
t.Fatalf("%s: Confirmed Balance failed: %v", test.name, err)
|
||||||
}
|
}
|
||||||
if bal != test.bal {
|
if bal != test.bal {
|
||||||
t.Fatalf("%s: balance mismatch: expected: %d, got: %d", test.name, test.bal, bal)
|
t.Fatalf("%s: balance mismatch: expected: %d, got: %d", test.name, test.bal, bal)
|
||||||
}
|
}
|
||||||
unc, err := s.Balance(ns, 0, TstRecvCurrentHeight)
|
unc, _, err := s.Balance(ns, 0, TstRecvCurrentHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s: Unconfirmed Balance failed: %v", test.name, err)
|
t.Fatalf("%s: Unconfirmed Balance failed: %v", test.name, err)
|
||||||
}
|
}
|
||||||
|
@ -626,7 +626,7 @@ func TestFindingSpentCredits(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bal, err := s.Balance(ns, 1, TstSignedTxBlockDetails.Height)
|
bal, _, err := s.Balance(ns, 1, TstSignedTxBlockDetails.Height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -809,7 +809,7 @@ func TestCoinbases(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tst := range balTests {
|
for i, tst := range balTests {
|
||||||
bal, err := s.Balance(ns, tst.minConf, tst.height)
|
bal, _, err := s.Balance(ns, tst.minConf, tst.height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
@ -890,7 +890,7 @@ func TestCoinbases(t *testing.T) {
|
||||||
}
|
}
|
||||||
balTestsBeforeMaturity := balTests
|
balTestsBeforeMaturity := balTests
|
||||||
for i, tst := range balTests {
|
for i, tst := range balTests {
|
||||||
bal, err := s.Balance(ns, tst.minConf, tst.height)
|
bal, _, err := s.Balance(ns, tst.minConf, tst.height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
@ -973,7 +973,7 @@ func TestCoinbases(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tst := range balTests {
|
for i, tst := range balTests {
|
||||||
bal, err := s.Balance(ns, tst.minConf, tst.height)
|
bal, _, err := s.Balance(ns, tst.minConf, tst.height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
@ -1006,7 +1006,7 @@ func TestCoinbases(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
for i, tst := range balTests {
|
for i, tst := range balTests {
|
||||||
bal, err := s.Balance(ns, tst.minConf, tst.height)
|
bal, _, err := s.Balance(ns, tst.minConf, tst.height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
@ -1026,7 +1026,7 @@ func TestCoinbases(t *testing.T) {
|
||||||
}
|
}
|
||||||
balTests = balTestsBeforeMaturity
|
balTests = balTestsBeforeMaturity
|
||||||
for i, tst := range balTests {
|
for i, tst := range balTests {
|
||||||
bal, err := s.Balance(ns, tst.minConf, tst.height)
|
bal, _, err := s.Balance(ns, tst.minConf, tst.height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
@ -1072,7 +1072,7 @@ func TestCoinbases(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tst := range balTests {
|
for i, tst := range balTests {
|
||||||
bal, err := s.Balance(ns, tst.minConf, tst.height)
|
bal, _, err := s.Balance(ns, tst.minConf, tst.height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
@ -1247,7 +1247,7 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tst := range balTests {
|
for i, tst := range balTests {
|
||||||
bal, err := s.Balance(ns, tst.minConf, tst.height)
|
bal, _, err := s.Balance(ns, tst.minConf, tst.height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
@ -1392,7 +1392,7 @@ func TestRemoveUnminedTx(t *testing.T) {
|
||||||
commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
|
commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
b, err := store.Balance(ns, minConfs, maturityHeight)
|
b, _, err := store.Balance(ns, minConfs, maturityHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to retrieve balance: %v", err)
|
t.Fatalf("unable to retrieve balance: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2389,7 +2389,7 @@ func assertBalance(t *testing.T, s *Store, ns walletdb.ReadWriteBucket,
|
||||||
if confirmed {
|
if confirmed {
|
||||||
minConf = 1
|
minConf = 1
|
||||||
}
|
}
|
||||||
balance, err := s.Balance(ns, minConf, blockHeight)
|
balance, _, err := s.Balance(ns, minConf, blockHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue