parent
879d2cb27f
commit
85af882c13
7 changed files with 108 additions and 11 deletions
39
account.go
39
account.go
|
@ -36,6 +36,7 @@ type Account struct {
|
|||
name string
|
||||
*wallet.Wallet
|
||||
TxStore *txstore.Store
|
||||
lockedOutpoints map[btcwire.OutPoint]struct{}
|
||||
}
|
||||
|
||||
// Lock locks the underlying wallet for an account.
|
||||
|
@ -432,6 +433,44 @@ func (a *Account) exportBase64() (map[string]string, error) {
|
|||
return m, nil
|
||||
}
|
||||
|
||||
// LockedOutpoint returns whether an outpoint has been marked as locked and
|
||||
// should not be used as an input for created transactions.
|
||||
func (a *Account) LockedOutpoint(op btcwire.OutPoint) bool {
|
||||
_, locked := a.lockedOutpoints[op]
|
||||
return locked
|
||||
}
|
||||
|
||||
// LockOutpoint marks an outpoint as locked, that is, it should not be used as
|
||||
// an input for newly created transactions.
|
||||
func (a *Account) LockOutpoint(op btcwire.OutPoint) {
|
||||
a.lockedOutpoints[op] = struct{}{}
|
||||
}
|
||||
|
||||
// UnlockOutpoint marks an outpoint as unlocked, that is, it may be used as an
|
||||
// input for newly created transactions.
|
||||
func (a *Account) UnlockOutpoint(op btcwire.OutPoint) {
|
||||
delete(a.lockedOutpoints, op)
|
||||
}
|
||||
|
||||
// ResetLockedOutpoints resets the set of locked outpoints so all may be used
|
||||
// as inputs for new transactions.
|
||||
func (a *Account) ResetLockedOutpoints() {
|
||||
a.lockedOutpoints = map[btcwire.OutPoint]struct{}{}
|
||||
}
|
||||
|
||||
// LockedOutpoints returns a slice of currently locked outpoints. This is
|
||||
// intended to be used by marshaling the result as a JSON array for
|
||||
// listlockunspent RPC results.
|
||||
func (a *Account) LockedOutpoints() []btcjson.TransactionInput {
|
||||
locked := make([]btcjson.TransactionInput, len(a.lockedOutpoints))
|
||||
i := 0
|
||||
for op := range a.lockedOutpoints {
|
||||
locked[i] = btcjson.TransactionInput{op.Hash.String(), op.Index}
|
||||
i++
|
||||
}
|
||||
return locked
|
||||
}
|
||||
|
||||
// Track requests btcd to send notifications of new transactions for
|
||||
// each address stored in a wallet.
|
||||
func (a *Account) Track() {
|
||||
|
|
|
@ -180,6 +180,7 @@ func openSavedAccount(name string, cfg *config) (*Account, error) {
|
|||
name: name,
|
||||
Wallet: wlt,
|
||||
TxStore: txs,
|
||||
lockedOutpoints: map[btcwire.OutPoint]struct{}{},
|
||||
}
|
||||
|
||||
walletPath := accountFilename("wallet.bin", name, netdir)
|
||||
|
@ -710,6 +711,7 @@ func (am *AccountManager) CreateEncryptedWallet(passphrase []byte) error {
|
|||
a := &Account{
|
||||
Wallet: wlt,
|
||||
TxStore: txstore.New(),
|
||||
lockedOutpoints: map[btcwire.OutPoint]struct{}{},
|
||||
}
|
||||
if err := am.RegisterNewAccount(a); err != nil {
|
||||
return err
|
||||
|
|
|
@ -197,6 +197,12 @@ func (a *Account) txToPairs(pairs map[string]btcutil.Amount,
|
|||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Locked unspent outputs are skipped.
|
||||
if a.LockedOutpoint(*unspent[i].OutPoint()) {
|
||||
continue
|
||||
}
|
||||
|
||||
eligible = append(eligible, unspent[i])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ func TestFakeTxs(t *testing.T) {
|
|||
}
|
||||
a := &Account{
|
||||
Wallet: w,
|
||||
lockedOutpoints: map[btcwire.OutPoint]struct{}{},
|
||||
}
|
||||
|
||||
w.Unlock([]byte("banana"))
|
||||
|
|
54
rpcserver.go
54
rpcserver.go
|
@ -728,6 +728,7 @@ func (s *rpcServer) PostClientRPC(w http.ResponseWriter, r *http.Request) {
|
|||
id = cmd.Id()
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", rpcRequest)
|
||||
_, err := w.Write(marshalError(idPointer(cmd.Id())))
|
||||
if err != nil {
|
||||
log.Warnf("Client sent invalid request but unable "+
|
||||
|
@ -828,10 +829,12 @@ var rpcHandlers = map[string]requestHandler{
|
|||
"importprivkey": ImportPrivKey,
|
||||
"keypoolrefill": KeypoolRefill,
|
||||
"listaccounts": ListAccounts,
|
||||
"listlockunspent": ListLockUnspent,
|
||||
"listreceivedbyaddress": ListReceivedByAddress,
|
||||
"listsinceblock": ListSinceBlock,
|
||||
"listtransactions": ListTransactions,
|
||||
"listunspent": ListUnspent,
|
||||
"lockunspent": LockUnspent,
|
||||
"sendfrom": SendFrom,
|
||||
"sendmany": SendMany,
|
||||
"sendtoaddress": SendToAddress,
|
||||
|
@ -853,9 +856,7 @@ var rpcHandlers = map[string]requestHandler{
|
|||
"getwalletinfo": Unimplemented,
|
||||
"importwallet": Unimplemented,
|
||||
"listaddressgroupings": Unimplemented,
|
||||
"listlockunspent": Unimplemented,
|
||||
"listreceivedbyaccount": Unimplemented,
|
||||
"lockunspent": Unimplemented,
|
||||
"move": Unimplemented,
|
||||
"setaccount": Unimplemented,
|
||||
"stop": Unimplemented,
|
||||
|
@ -1589,6 +1590,20 @@ func ListAccounts(icmd btcjson.Cmd) (interface{}, error) {
|
|||
return AcctMgr.ListAccounts(cmd.MinConf), nil
|
||||
}
|
||||
|
||||
// ListLockUnspent handles a listlockunspent request by returning an array of
|
||||
// all locked outpoints.
|
||||
func ListLockUnspent(icmd btcjson.Cmd) (interface{}, error) {
|
||||
// Due to our poor account support, this assumes only the default
|
||||
// account is available. When the keystore and account heirarchies are
|
||||
// reversed, the locked outpoints mapping will cover all accounts.
|
||||
a, err := AcctMgr.Account("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.LockedOutpoints(), nil
|
||||
}
|
||||
|
||||
// ListReceivedByAddress handles a listreceivedbyaddress request by returning
|
||||
// a slice of objects, each one containing:
|
||||
// "account": the account of the receiving address;
|
||||
|
@ -1851,6 +1866,41 @@ func ListUnspent(icmd btcjson.Cmd) (interface{}, error) {
|
|||
return AcctMgr.ListUnspent(cmd.MinConf, cmd.MaxConf, addresses)
|
||||
}
|
||||
|
||||
// LockUnspent handles the lockunspent command.
|
||||
func LockUnspent(icmd btcjson.Cmd) (interface{}, error) {
|
||||
cmd, ok := icmd.(*btcjson.LockUnspentCmd)
|
||||
if !ok {
|
||||
return nil, btcjson.ErrInternal
|
||||
}
|
||||
|
||||
// Due to our poor account support, this assumes only the default
|
||||
// account is available. When the keystore and account heirarchies are
|
||||
// reversed, the locked outpoints mapping will cover all accounts.
|
||||
a, err := AcctMgr.Account("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case cmd.Unlock && len(cmd.Transactions) == 0:
|
||||
a.ResetLockedOutpoints()
|
||||
default:
|
||||
for _, input := range cmd.Transactions {
|
||||
txSha, err := btcwire.NewShaHashFromStr(input.Txid)
|
||||
if err != nil {
|
||||
return nil, ParseError{err}
|
||||
}
|
||||
op := btcwire.OutPoint{Hash: *txSha, Index: input.Vout}
|
||||
if cmd.Unlock {
|
||||
a.UnlockOutpoint(op)
|
||||
} else {
|
||||
a.LockOutpoint(op)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// sendPairs is a helper routine to reduce duplicated code when creating and
|
||||
// sending payment transactions.
|
||||
func sendPairs(icmd btcjson.Cmd, account string, amounts map[string]btcutil.Amount,
|
||||
|
|
|
@ -566,7 +566,7 @@ func (t *txRecord) ReadFrom(r io.Reader) (int64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
c := &credit{change, false, spentBy}
|
||||
c := &credit{change, spentBy}
|
||||
credits = append(credits, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -250,7 +250,6 @@ type debits struct {
|
|||
// credit describes a transaction output which was or is spendable by wallet.
|
||||
type credit struct {
|
||||
change bool
|
||||
locked bool
|
||||
spentBy *BlockTxKey // nil if unspent
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue