client/asset/dcr: native Decred SPV wallet#1633
Conversation
chappjc
left a comment
There was a problem hiding this comment.
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.
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.
| RPCPass string `ini:"password"` | ||
| RPCListen string `ini:"rpclisten"` | ||
| RPCCert string `ini:"rpccert"` | ||
| type WalletConfig struct { |
There was a problem hiding this comment.
nit: Comment on exported struct.
| 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.
nit: Comment on exported struct.
| // 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) |
| return false, err | ||
| } | ||
| netDir := filepath.Join(dataDir, chainParams.Name) | ||
| // timeout and recoverWindow arguments borrowed from btcwallet directly. |
There was a problem hiding this comment.
comment references btcwallet.
There was a problem hiding this comment.
And doesn't even apply here.
| // hdr, err := dcr.wallet.GetBlockHeader(ctx, newTipHash) | ||
| // if err != nil { | ||
| // go dcr.tipChange(fmt.Errorf("error setting new tip: %w", err)) | ||
| // continue | ||
| // } |
| 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 |
| return w.PublishTransaction(ctx, tx, w.spv) | ||
| } | ||
|
|
||
| // GetBlockHeader returns block header info for the specified block hash. The |
|
|
||
| _, tipHeight := w.MainChainTip(ctx) | ||
| if tipHeight < int32(hdr.Height) { | ||
| return nil, fmt.Errorf("sumpin's wrong with our tip") |
| outputTotal += dcrutil.Amount(output.Value) | ||
| } | ||
| fee = debitTotal - outputTotal | ||
| negFeeF64 = (-fee).ToCoin() |
| } | ||
|
|
||
| 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.
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.
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. |
| ret.Amount = (creditTotal - debitTotal).ToCoin() | ||
| ret.Fee = negFeeF64 |
There was a problem hiding this comment.
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.
chappjc
left a comment
There was a problem hiding this comment.
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.
loadRPCConfig is unused. Hmmm
Doesn't look like newRPCWallet has all the defaults that this function provided.
| // NOTE: Why GapPolicyIgnore instead of GapPolicyWrap? Can we recover from an | ||
| // ignored gap policy without extreme measures? |
There was a problem hiding this comment.
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 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.
| // DRAFT NOTE: Maybe? | ||
| if walletCfg.PrimaryAccount == "" { | ||
| walletCfg.PrimaryAccount = defaultAcctName | ||
| } |
|
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.
chappjc
left a comment
There was a problem hiding this comment.
All good. Manually tested: (1) success, (2) refund maker DCR contract, and (3) find maker redemption with hacked ghosting.
Use dcrwallet's
*wallet.Walletin a similar fashion to thespvWalletin 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.