Skip to content

Commit

Permalink
Begin abstraction of wallet syncing out of the wallet package.
Browse files Browse the repository at this point in the history
This change begins the removal of the dcrd JSON-RPC client dependency
from the wallet package and type.  All notification processing to keep
the wallet up to date has been moved to the chain package.

This change is being performed to make switching to other network
syncing backends easier in the future.  Most importantly, it is
required for any kind of support for both RPC and SPV network
backends.

To avoid some churn, the wallet must still be associated with an
object that provides basic network operations such as loading data
filters, fetching headers, and transaction publishing.  This object is
now an interface type rather than the concrete RPC client type.  At
the moment, this interface is currently too narrow to allow an SPV
backend as a drop in replacement, but the goal is to remove methods
from it until this becomes possible.

There are still some features in the wallet package which do require
the RPC client.  These features either take the RPC client as a method
parameter or assert that the network backend is the RPC client.

This change also uses this API break as an opportunity to begin
passing around context.Context for request-scoped cancellation and
timeouts.  The methods of the network backend interface all take a
context, and while it is not used by the backend, they will be used
for any future implementations.
  • Loading branch information
jrick committed Oct 12, 2017
1 parent 768542b commit 90f42ca
Show file tree
Hide file tree
Showing 21 changed files with 1,102 additions and 936 deletions.
6 changes: 3 additions & 3 deletions apperrors/code_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions apperrors/error.go
Expand Up @@ -191,6 +191,18 @@ const (
// ErrExhaustedAccount indicates that all possible addresses for an account
// have been derived and no more can be created.
ErrExhaustedAccount

// ErrDisconnected indicates that the operation could not be completed due
// to being disconnected from the Decred network.
ErrDisconnected

// ErrUnsupported indicates the operation is unsupported.
ErrUnsupported

// ErrHighFees indicates the transaction pays much more fee that should be
// required, and an error is returned to prevent an accident where a
// significant amount of value is lost to fees.
ErrHighFees
)

// E describes an application-level error. An error code is provided to
Expand Down
118 changes: 118 additions & 0 deletions chain/backendwrapper.go
@@ -0,0 +1,118 @@
// Copyright (c) 2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package chain

import (
"context"
"encoding/hex"

"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/dcrutil"
rpcclient "github.com/decred/dcrd/rpcclient"
"github.com/decred/dcrd/wire"
"github.com/decred/dcrwallet/apperrors"
"github.com/decred/dcrwallet/wallet"
"github.com/jrick/bitset"
)

type rpcBackend struct {
rpcClient *rpcclient.Client
}

var _ wallet.NetworkBackend = (*rpcBackend)(nil)

// BackendFromRPCClient creates a wallet network backend from an RPC client.
func BackendFromRPCClient(rpcClient *rpcclient.Client) wallet.NetworkBackend {
return &rpcBackend{rpcClient}
}

// RPCClientFromBackend returns the RPC client used to create a wallet network
// backend. This errors if the backend was not created using
// BackendFromRPCClient.
func RPCClientFromBackend(n wallet.NetworkBackend) (*rpcclient.Client, error) {
b, ok := n.(*rpcBackend)
if !ok {
return nil, apperrors.New(apperrors.ErrUnsupported,
"this operation requires the network backend to be the consensus RPC server")
}
return b.rpcClient, nil
}

func (b *rpcBackend) GetHeaders(ctx context.Context, blockLocators []chainhash.Hash, hashStop *chainhash.Hash) ([][]byte, error) {
r, err := b.rpcClient.GetHeaders(blockLocators, hashStop)
if err != nil {
return nil, err
}
headers := make([][]byte, 0, len(r.Headers))
for _, hexHeader := range r.Headers {
header, err := hex.DecodeString(hexHeader)
if err != nil {
return nil, err
}
headers = append(headers, header)
}
return headers, nil
}

func (b *rpcBackend) LoadTxFilter(ctx context.Context, reload bool, addrs []dcrutil.Address, outpoints []wire.OutPoint) error {
return b.rpcClient.LoadTxFilter(reload, addrs, outpoints)
}

func (b *rpcBackend) PublishTransaction(ctx context.Context, tx *wire.MsgTx) error {
// High fees are hardcoded and allowed here since transactions created by
// the wallet perform their own high fee check if high fees are disabled.
// This matches the lack of any high fee checking when publishing
// transactions over the wire protocol.
_, err := b.rpcClient.SendRawTransaction(tx, true)
return err
}

func (b *rpcBackend) AddressesUsed(ctx context.Context, addrs []dcrutil.Address) (bitset.Bytes, error) {
hexBitSet, err := b.rpcClient.ExistsAddresses(addrs)
if err != nil {
return nil, err
}
return hex.DecodeString(hexBitSet)
}

func (b *rpcBackend) Rescan(ctx context.Context, blocks []chainhash.Hash) ([]*wallet.RescannedBlock, error) {
r, err := b.rpcClient.Rescan(blocks)
if err != nil {
return nil, err
}
discoveredData := make([]*wallet.RescannedBlock, 0, len(r.DiscoveredData))
for _, d := range r.DiscoveredData {
blockHash, err := chainhash.NewHashFromStr(d.Hash)
if err != nil {
return nil, err
}
txs := make([][]byte, 0, len(d.Transactions))
for _, txHex := range d.Transactions {
tx, err := hex.DecodeString(txHex)
if err != nil {
return nil, err
}
txs = append(txs, tx)
}
rescannedBlock := &wallet.RescannedBlock{
BlockHash: *blockHash,
Transactions: txs,
}
discoveredData = append(discoveredData, rescannedBlock)
}
return discoveredData, nil
}

func (b *rpcBackend) StakeDifficulty(ctx context.Context) (dcrutil.Amount, error) {
r, err := b.rpcClient.GetStakeDifficulty()
if err != nil {
return 0, err
}
return dcrutil.NewAmount(r.NextStakeDifficulty)
}

func (b *rpcBackend) GetBlockHash(ctx context.Context, height int32) (*chainhash.Hash, error) {
return b.rpcClient.GetBlockHash(int64(height))
}

0 comments on commit 90f42ca

Please sign in to comment.