parent
879d2cb27f
commit
85af882c13
7 changed files with 108 additions and 11 deletions
41
account.go
41
account.go
|
@ -35,7 +35,8 @@ import (
|
||||||
type Account struct {
|
type Account struct {
|
||||||
name string
|
name string
|
||||||
*wallet.Wallet
|
*wallet.Wallet
|
||||||
TxStore *txstore.Store
|
TxStore *txstore.Store
|
||||||
|
lockedOutpoints map[btcwire.OutPoint]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock locks the underlying wallet for an account.
|
// Lock locks the underlying wallet for an account.
|
||||||
|
@ -432,6 +433,44 @@ func (a *Account) exportBase64() (map[string]string, error) {
|
||||||
return m, nil
|
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
|
// Track requests btcd to send notifications of new transactions for
|
||||||
// each address stored in a wallet.
|
// each address stored in a wallet.
|
||||||
func (a *Account) Track() {
|
func (a *Account) Track() {
|
||||||
|
|
12
acctmgr.go
12
acctmgr.go
|
@ -177,9 +177,10 @@ func openSavedAccount(name string, cfg *config) (*Account, error) {
|
||||||
wlt := new(wallet.Wallet)
|
wlt := new(wallet.Wallet)
|
||||||
txs := txstore.New()
|
txs := txstore.New()
|
||||||
a := &Account{
|
a := &Account{
|
||||||
name: name,
|
name: name,
|
||||||
Wallet: wlt,
|
Wallet: wlt,
|
||||||
TxStore: txs,
|
TxStore: txs,
|
||||||
|
lockedOutpoints: map[btcwire.OutPoint]struct{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
walletPath := accountFilename("wallet.bin", name, netdir)
|
walletPath := accountFilename("wallet.bin", name, netdir)
|
||||||
|
@ -708,8 +709,9 @@ func (am *AccountManager) CreateEncryptedWallet(passphrase []byte) error {
|
||||||
// manager. Registering will fail if the new account can not be
|
// manager. Registering will fail if the new account can not be
|
||||||
// written immediately to disk.
|
// written immediately to disk.
|
||||||
a := &Account{
|
a := &Account{
|
||||||
Wallet: wlt,
|
Wallet: wlt,
|
||||||
TxStore: txstore.New(),
|
TxStore: txstore.New(),
|
||||||
|
lockedOutpoints: map[btcwire.OutPoint]struct{}{},
|
||||||
}
|
}
|
||||||
if err := am.RegisterNewAccount(a); err != nil {
|
if err := am.RegisterNewAccount(a); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -197,6 +197,12 @@ func (a *Account) txToPairs(pairs map[string]btcutil.Amount,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Locked unspent outputs are skipped.
|
||||||
|
if a.LockedOutpoint(*unspent[i].OutPoint()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
eligible = append(eligible, unspent[i])
|
eligible = append(eligible, unspent[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,8 @@ func TestFakeTxs(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a := &Account{
|
a := &Account{
|
||||||
Wallet: w,
|
Wallet: w,
|
||||||
|
lockedOutpoints: map[btcwire.OutPoint]struct{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Unlock([]byte("banana"))
|
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()
|
id = cmd.Id()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("%s\n", rpcRequest)
|
||||||
_, err := w.Write(marshalError(idPointer(cmd.Id())))
|
_, err := w.Write(marshalError(idPointer(cmd.Id())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Client sent invalid request but unable "+
|
log.Warnf("Client sent invalid request but unable "+
|
||||||
|
@ -828,10 +829,12 @@ var rpcHandlers = map[string]requestHandler{
|
||||||
"importprivkey": ImportPrivKey,
|
"importprivkey": ImportPrivKey,
|
||||||
"keypoolrefill": KeypoolRefill,
|
"keypoolrefill": KeypoolRefill,
|
||||||
"listaccounts": ListAccounts,
|
"listaccounts": ListAccounts,
|
||||||
|
"listlockunspent": ListLockUnspent,
|
||||||
"listreceivedbyaddress": ListReceivedByAddress,
|
"listreceivedbyaddress": ListReceivedByAddress,
|
||||||
"listsinceblock": ListSinceBlock,
|
"listsinceblock": ListSinceBlock,
|
||||||
"listtransactions": ListTransactions,
|
"listtransactions": ListTransactions,
|
||||||
"listunspent": ListUnspent,
|
"listunspent": ListUnspent,
|
||||||
|
"lockunspent": LockUnspent,
|
||||||
"sendfrom": SendFrom,
|
"sendfrom": SendFrom,
|
||||||
"sendmany": SendMany,
|
"sendmany": SendMany,
|
||||||
"sendtoaddress": SendToAddress,
|
"sendtoaddress": SendToAddress,
|
||||||
|
@ -853,9 +856,7 @@ var rpcHandlers = map[string]requestHandler{
|
||||||
"getwalletinfo": Unimplemented,
|
"getwalletinfo": Unimplemented,
|
||||||
"importwallet": Unimplemented,
|
"importwallet": Unimplemented,
|
||||||
"listaddressgroupings": Unimplemented,
|
"listaddressgroupings": Unimplemented,
|
||||||
"listlockunspent": Unimplemented,
|
|
||||||
"listreceivedbyaccount": Unimplemented,
|
"listreceivedbyaccount": Unimplemented,
|
||||||
"lockunspent": Unimplemented,
|
|
||||||
"move": Unimplemented,
|
"move": Unimplemented,
|
||||||
"setaccount": Unimplemented,
|
"setaccount": Unimplemented,
|
||||||
"stop": Unimplemented,
|
"stop": Unimplemented,
|
||||||
|
@ -1589,6 +1590,20 @@ func ListAccounts(icmd btcjson.Cmd) (interface{}, error) {
|
||||||
return AcctMgr.ListAccounts(cmd.MinConf), nil
|
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
|
// ListReceivedByAddress handles a listreceivedbyaddress request by returning
|
||||||
// a slice of objects, each one containing:
|
// a slice of objects, each one containing:
|
||||||
// "account": the account of the receiving address;
|
// "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)
|
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
|
// sendPairs is a helper routine to reduce duplicated code when creating and
|
||||||
// sending payment transactions.
|
// sending payment transactions.
|
||||||
func sendPairs(icmd btcjson.Cmd, account string, amounts map[string]btcutil.Amount,
|
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)
|
credits = append(credits, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -250,7 +250,6 @@ type debits struct {
|
||||||
// credit describes a transaction output which was or is spendable by wallet.
|
// credit describes a transaction output which was or is spendable by wallet.
|
||||||
type credit struct {
|
type credit struct {
|
||||||
change bool
|
change bool
|
||||||
locked bool
|
|
||||||
spentBy *BlockTxKey // nil if unspent
|
spentBy *BlockTxKey // nil if unspent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue