-
Notifications
You must be signed in to change notification settings - Fork 127
client/asset/dcr: native Decred SPV wallet #1633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome. I just did a turbo scan through. Very excited for this. Looks straightforward based on the BTC SPV so I bet this can get in short order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was able to get the client into a totally unresponsive state at one point, but no idea if it was because of these changes.
Otherwise testing well.
Do you think a version somewhere needs to be upped for these changes? If I were to make an spv wallet and go back a version it would no longer be visible. I don't know what would happen if I then attempted to make another wallet and then went back to this diff.
The simnet tests are not working.
client/asset/dcr/config.go
Outdated
RPCPass string `ini:"password"` | ||
RPCListen string `ini:"rpclisten"` | ||
RPCCert string `ini:"rpccert"` | ||
type WalletConfig struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Comment on exported struct.
client/asset/dcr/config.go
Outdated
if err := config.Unmapify(settings, cfg); err != nil { | ||
return nil, nil, fmt.Errorf("error parsing config: %w", err) | ||
} | ||
type RPCConfig struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Comment on exported struct.
client/asset/dcr/dcr.go
Outdated
// Exists checks the existence of the wallet. Part of the Creator interface. | ||
func (d *Driver) Exists(walletType, dataDir string, _ map[string]string, net dex.Network) (bool, error) { | ||
if walletType != walletTypeSPV { | ||
return false, fmt.Errorf("no Bitcoin wallet of type %q available", walletType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bitcoin -> Decred
client/asset/dcr/dcr.go
Outdated
return false, err | ||
} | ||
netDir := filepath.Join(dataDir, chainParams.Name) | ||
// timeout and recoverWindow arguments borrowed from btcwallet directly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment references btcwallet
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And doesn't even apply here.
client/asset/dcr/dcr.go
Outdated
// hdr, err := dcr.wallet.GetBlockHeader(ctx, newTipHash) | ||
// if err != nil { | ||
// go dcr.tipChange(fmt.Errorf("error setting new tip: %w", err)) | ||
// continue | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can remove.
client/asset/dcr/spv.go
Outdated
dcrw, err := wallet.Open(ctx, walletConfig(db, w.chainParams)) | ||
if err != nil { | ||
// If this function does not return to completion the database must be | ||
// closed. Otherwise, because the database is locked on opens, any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
opens -> open
client/asset/dcr/spv.go
Outdated
return w.PublishTransaction(ctx, tx, w.spv) | ||
} | ||
|
||
// GetBlockHeader returns block header info for the specified block hash. The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
returns the/a block header
client/asset/dcr/spv.go
Outdated
|
||
_, tipHeight := w.MainChainTip(ctx) | ||
if tipHeight < int32(hdr.Height) { | ||
return nil, fmt.Errorf("sumpin's wrong with our tip") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Nothing to format.
client/asset/dcr/spv.go
Outdated
outputTotal += dcrutil.Amount(output.Value) | ||
} | ||
fee = debitTotal - outputTotal | ||
negFeeF64 = (-fee).ToCoin() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can just set ret.Fee
here.
} | ||
|
||
func newSpvSyncer(w *wallet.Wallet, netDir string, connectPeers []string) *spv.Syncer { | ||
addr := &net.TCPAddr{IP: net.ParseIP("::1"), Port: 0} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have ipv6 turned off at the kernel level so I would think that I can't use this, but seems fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code mirrors what dcrwallet does internally, but interestingly this address seems to be unused by dcrwallet anyway. If you trace into p2p/peering.go, and look for where extaddr
is used, it seems to go nowhere. It gets passed to (*LocalPeer).newMsgVersion
, but it is unused there, using net.Conn.LocalAddr()
instead.
Almost as if nil
would work. Maybe we keep it like this with ipv6 to match dcrwallet, but make a comment because it's clearly confusing multiple people.
I think an asset version is a value coordinated between client and server as an indication of more fundamental changes like contract structure or block/tx serializations. |
Not sure which way you are going to go for conflict resolution, but clearly the user-set account names should stay RPC-only since the SPV wallet should manage the accounts even when we tackle mixing support for the native DCR wallet. |
client/asset/dcr/spv.go
Outdated
ret.Amount = (creditTotal - debitTotal).ToCoin() | ||
ret.Fee = negFeeF64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TLDR: I'm generally uneasy about computing anything reliably from the Credits
and Debits
slices (namely Fee
or Amount
), but fortunately the full GetTransactionResult
is mostly unsued, so can we consider paring down the interface to make it both easier to satisfy and more reliable?
Lately I have concluded we should avoid relying on both the Credits
and Debits
slices from TxDetails
(or the Amount
and Fee
fields that we compute from them or get from gettransaction
on the RPC side) because they are very often incomplete or incorrect. (Both BTC and DCR)
As you noted above, Fee can only be determined if every input is a debit
, and this is indeed trouble with mix transactions, just as an example. Inspecting one of those in my wallet, the "fee"
field is omitted, and "amount"
is something negative like "amount": -0.002399
which is presumably indicative of the net change to my wallet's balance.
The help for gettransaction
hints at more general conditions "fee": n.nnn, (numeric) The total input value minus the total output value, or 0 if 'txid' is not a sent transaction
. Mixed transactions are just one instance.
I chatted with @martonp about this here: #1555 (comment)
To be clear, I don't think the issue is what we are doing with the output from dcrWallet.TxDetails
, but that it has weak assurances about what it will include in Credits
and Debits
, plus the conditions are different between unconfirmed and confirmed txns. In both unminedTxDetails
and minedTxDetails
, it just grabs what credits and debits it has, and looking through real wallet transactions they are often missing.
Anyway, for the purpose of tx fee, Marton did it the hard but reliable way by pulling prevout tnxs for the input values, and in the Electrum PR I've pared down the GetTransactionResult
type because it's nigh impossible to completely satisfy. I think this is OK since consumers generally just want (1) confs, (2) block info, and (3) raw tx hex. Scanning through this PR, I think that's still the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feeling great about this. About 2/3 of it reviewed.
|
||
// Get network settings. Zero value is mainnet, but unknown non-zero cfg.Net | ||
// is an error. | ||
func loadRPCConfig(settings map[string]string, network dex.Network) (*rpcConfig, *chaincfg.Params, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loadRPCConfig
is unused. Hmmm
Doesn't look like newRPCWallet
has all the defaults that this function provided.
client/asset/dcr/spv.go
Outdated
// NOTE: Why GapPolicyIgnore instead of GapPolicyWrap? Can we recover from an | ||
// ignored gap policy without extreme measures? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we are trying a little too hard to avoid address re-use. In the Electrum PR, I spent some time looking at where we are burning through addresses, and I decided to add a refundAddress
method to client/asset/btc.Wallet
where the addresses are very rarely used and we can tolerate some reuse.
dcrdex/client/asset/btc/wallet.go
Lines 32 to 34 in c63ef10
// The refund addresses are almost never used, so we might tolerate some | |
// address reuse if the backend requires. | |
refundAddress() (btcutil.Address, error) |
The other big source of wasted addresses is the address provided in orders (if canceled), when the address really could be provided closer to match time although with more messaging, so that is not as easy.
Regarding recovery, when we implement Rescanner, we can use the DiscoverAddresses method with a provided gap limit. Also, I haven't gotten far enough with the review, but we should ensure the seed can be used in an external dcrwallet for manual recovery.
client/asset/dcr/dcr.go
Outdated
// DRAFT NOTE: Maybe? | ||
if walletCfg.PrimaryAccount == "" { | ||
walletCfg.PrimaryAccount = defaultAcctName | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks correct.
It looks like loadbot needs a |
That settles it. We're updating CI to tidy loadbot too. |
Use dcrwallet's *wallet.Wallet in a similar fashion to the spvWallet in the btc client package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good. Manually tested: (1) success, (2) refund maker DCR contract, and (3) find maker redemption with hacked ghosting.
Use dcrwallet's
*wallet.Wallet
in a similar fashion to thespvWallet
in client/btc.This is ready for review, but I don't want to merge until this is rebased on top of #1632 and tests implemented.