Skip to content
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

client: bitcoin spv #1230

Merged
merged 22 commits into from
Oct 21, 2021
Merged

client: bitcoin spv #1230

merged 22 commits into from
Oct 21, 2021

Conversation

buck54321
Copy link
Member

Add a built-in Bitcoin SPV wallet. Uses neutrino and btcwallet.

client/asset:

Add Create and Exists methods to the Driver. Create is used to
create a "seeded" (native, built-in) wallet, and should
not be used for non-seeded wallets. Exists checks the existence of
a wallet. Exists is written so far to check even RPC wallet
existence, though it's only used for seeded wallets right now.

Adds the WalletDefinition type. WalletInfo has a new field,
AvailableWallets []*WalletDefinition.

WalletConfig type has a new field, Type string, which should match
the Type field of one of the AvailableWallets. Existing backends
must handle Type == "" for pre-existing wallets.

client/asset/btc:

Quite a few changes to configuration parsing to more clearly
separate RPC config from wallet settings. New spvWallet type
manages the *btcwallet.Wallet and *neutrino.ChainService. The
Wallet interface was already defined, so the work was just
implementing the methods using direct calls to wallet methods.

Everything is run through an interface for testing. Existing tests
required relatively few modifications to work with spv as well.
There are just a couple of new unit tests specific to the more
nuanced spv methods. The regnet_test.go/livetest.go test is adapted
to run an all-SPV trio for one test.

client/core: Work with new wallet definitions and give special handling
to seeded wallets.

front-end:

NewWalletForm and WalletConfigForm updated to work with
wallet definitions. Wallet type is selected via a tab at the top of
the NewWalletForm. Seeded wallets take no user-supplied password
(enforced in core). Special handling of headers and options so that
creating a seeded wallet can be very close to a 1-button, 1-click
experience.

A new form is added after the new wallet form conditionally if the
wallet balance is lower than required for the fee. This will always
be the case for the first native spv wallet. The new form shows a
deposit address and the current balance, and directs the user to
fund the address for registration fees. The balance is checked in
a loop and progresses back to the fee confirmation form when the
balance is sufficient. When working on simnet, just fund it from
the alpha node in harness-ctl.

client/db:

Store the new Type field as part of the wallet. I wrote
an upgrade function to store an empty string for the new fields
position in the database, but we're not populating it anyway,
and existing wallets handle that, so I don't see a good reason
to "upgrade".

@buck54321
Copy link
Member Author

buck54321 commented Oct 12, 2021

I still have concerns with potentially performing long unnecessary scans. Let's say the taker's swap is sitting in mempool due to fee volatility. Every 5 seconds (the interval of the TickerQueue), the maker will call AuditContract, which is going to calculate a start block based on match time (not our tx), which will result in ~ 20 blocks too many, and then will scan from there to the tip. The scan will go every 5 seconds until the match is found or revoked.

We could avoid these scans if we had some kind of checkpoint system. A way to say "we already looked for this output, and didn't see it through block X, so restart the scan there (if not orphaned)". Sorta like what we do with FindRedemption.


Update: solution in bbd1669

type CreateWalletParams struct {
Type string
Seed []byte
Pass []byte
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the password be a string since a string is passed to wallet.Unlock?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Good point. I'm tempted to go the other way though, and change the (Wallet).Unlock method to accept a []byte.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't that require a bunch of changes for non seeded wallets? The geth node takes a string to lock/unlock the private key as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will require changing existing implementations, but it's not a big job. The thing about string passwords is that once we're done with them, we're at the mercy of the garbage collector to get rid of the sensitive data. But we can zero a []byte. We even have a type for it, encode.PassBytes. It's true that eth will still be converting to string. So will dcr and btc-rpc, but we can now avoid strings for btc-spv, and we should take the opportunity to make things more secure.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, makes sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we had a dex.Bytes type for passwords.

Copy link
Member

@chappjc chappjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review. Looks really solid so far. No major issues.

// 3. We don't see a mempool. We're blind to new transactions until they are
// mined. This requires special handling by the caller. We've been
// anticipating this, so Core and Swapper are permissive of missing acks for
// audit requests.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

although Core responds to the audit request based on the txData, right? This is a note to self to check

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@chappjc chappjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few more comments in client/asset/btc before I move to core and webserver

if len(msgTx.TxOut) < int(vout+1) {
return nil, 0, fmt.Errorf("wallet transaction %s found, but not enough outputs for vout %d", txHash, vout)
}
return msgTx.TxOut[vout], confs, nil
Copy link
Member

@chappjc chappjc Oct 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case where walletTransaction found it, it returns a non-nil txout regardless of spent status, which is at odds with the docs for getTxOut. Only for WalletTransactionNotFound where it calls scanFilters does it emulate gettxout returning nil when spent.
Basically, the behavior matches for non-wallet txns, but for wallet txns, it will never appear spent. Are we ok with that?
If it's a wallet output too, could check txDetails.Credits[].Spent? If its not in the Credits slice, then it's a non-wallet-output (right?) like own swap, and then I don't know... just return as if not spent I guess?

Comment on lines 1164 to 1165
w.log.Debugf("A spending input was found during the scan but the output " +
"itself wasn't found. Was the startBlockHeight early enough?")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warnf? What's the severity and resolution if this happens? clear the checkpoint so it starts over from match time?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warnf seems appropriate. We don't store a *scanCheckpoint in this case.

This place this ends up affecting us is in SwapConfirmation, we return confs = 0 (which is wrong) and spent = true. In situations where we're looking for the counter-party's swap, we revoke if it's found to be spent, without inspecting the confs, so we're all good there. When it's our output, we'll know the block and won't end up here, but even if we did, we just end up sending out some inaccurate Data-severity notifications to the UI until the match progresses.

@chappjc

This comment has been minimized.

@chappjc
Copy link
Member

chappjc commented Oct 13, 2021

Got an intermittent AuditContract error in livetest.

EDIT: Appears resolved with latest commits.

////////// SPV WALLET W/O SPLIT //////////
2021-10-13 14:10:17.131 [INF] TEST: btc:alpha- has reported a new block, error = <nil>
2021-10-13 14:10:19.438 [INF] TEST[alpha]: alpha with address bcrt1qt3585tn3sfes6mdh955pzu2ws4ul2rcjzrfw4c is synced with balance = &{Available:8400000000 Immature:0 Locked:0} 

2021-10-13 14:10:23.563 [INF] TEST: btc:beta- has reported a new block, error = <nil>
2021-10-13 14:10:25.830 [INF] TEST[beta]: beta with address bcrt1qvz6jpkkz9tqdrzvpta5anw9y5x2ak47ahhtsjk is synced with balance = &{Available:8400000000 Immature:0 Locked:0} 

2021-10-13 14:10:29.483 [INF] TEST: btc:beta- has reported a new block, error = <nil>
2021-10-13 14:10:29.959 [INF] TEST: btc:alpha-gamma has reported a new block, error = <nil>
2021-10-13 14:10:32.281 [INF] TEST[gamma]: gamma with address bcrt1qsj2tsn6w3wzel8ygj63len3msctmnag77u69ys is synced with balance = &{Available:8400000000 Immature:0 Locked:0} 

2021-10-13 14:10:33.063 [INF] TEST: btc:alpha- has reported a new block, error = <nil>
2021-10-13 14:10:34.813 [INF] TEST: Wallets configured
2021-10-13 14:10:34.813 [INF] TEST: alpha 84.000000 available, 0.000000 immature, 0.000000 locked
2021-10-13 14:10:34.813 [INF] TEST: beta 84.000000 available, 0.000000 immature, 0.000000 locked
2021-10-13 14:10:34.813 [INF] TEST: gamma 84.000000 available, 0.000000 immature, 0.000000 locked
2021-10-13 14:10:34.813 [INF] TEST: Testing FundOrder
2021-10-13 14:10:34.814 [INF] TEST[gamma]: Funding 0.06 btc order with coins [638fe21ae4b031b43f74e12ad9d1361b65af41cfc38ebe7f5ae557fa9c5e6d06:0] worth 1
2021-10-13 14:10:34.814 [INF] TEST[gamma]: Funding 0.06 btc order with coins [8b6a5f9d4b7147844d3f559c8cb7e026f88a9813381b1ea1c0a3de1a9b25153a:0] worth 3
2021-10-13 14:10:34.814 [INF] TEST[gamma]: Funding 0.06 btc order with coins [638fe21ae4b031b43f74e12ad9d1361b65af41cfc38ebe7f5ae557fa9c5e6d06:0] worth 1
2021-10-13 14:10:34.814 [INF] TEST[gamma]: Funding 0.02 btc order with coins [638fe21ae4b031b43f74e12ad9d1361b65af41cfc38ebe7f5ae557fa9c5e6d06:0] worth 1
2021-10-13 14:10:34.814 [INF] TEST[gamma]: Funding 0.04 btc order with coins [8b6a5f9d4b7147844d3f559c8cb7e026f88a9813381b1ea1c0a3de1a9b25153a:0] worth 3
2021-10-13 14:10:34.814 [INF] TEST: Testing Swap
2021-10-13 14:10:34.815 [WRN] TEST[gamma]: Unable to get optimal fee rate, using fallback of 100: EstimateSmartFee not available on spv
2021-10-13 14:10:34.815 [WRN] TEST[gamma]: Unable to get optimal fee rate, using fallback of 100: EstimateSmartFee not available on spv
2021-10-13 14:10:39.816 [INF] TEST: Sent 2 swaps
2021-10-13 14:10:39.816 [INF] TEST:       Swap # 1: 9a4a5f686ab7e8e5b13593353636ac5cb84dd7cc97dd558c90754b0fe59f45e3:0
2021-10-13 14:10:39.816 [INF] TEST:       Swap # 2: 9a4a5f686ab7e8e5b13593353636ac5cb84dd7cc97dd558c90754b0fe59f45e3:1
2021-10-13 14:10:39.979 [INF] TEST: btc:alpha-gamma has reported a new block, error = <nil>
2021-10-13 14:10:40.483 [INF] TEST: btc:beta- has reported a new block, error = <nil>
2021-10-13 14:10:42.821 [INF] TEST: Testing AuditContract
    livetest.go:359: error auditing contract: error finding unspent contract: 9a4a5f686ab7e8e5b13593353636ac5cb84dd7cc97dd558c90754b0fe59f45e3:0 : output 9a4a5f686ab7e8e5b13593353636ac5cb84dd7cc97dd558c90754b0fe59f45e3:0 not found
--- FAIL: TestWallet (31.90s)

@chappjc

This comment has been minimized.

@chappjc
Copy link
Member

chappjc commented Oct 13, 2021

Here's a weird one that's not related to logging at all, all inside neutrino and btcd code..

EDIT: looks to be a flaw with the btcd AddrManager's API design. See here.

==================
WARNING: DATA RACE
Write at 0x00c0004251a0 by goroutine 54:
  github.com/btcsuite/btcd/addrmgr.(*AddrManager).updateAddress()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/addrmgr/addrmanager.go:189 +0x3d5
  github.com/btcsuite/btcd/addrmgr.(*AddrManager).AddAddresses()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/addrmgr/addrmanager.go:605 +0xfe
  github.com/lightninglabs/neutrino.(*ServerPeer).OnAddr()
      /home/jon/go/pkg/mod/github.com/lightninglabs/[email protected]/neutrino.go:378 +0x6f2
  github.com/lightninglabs/neutrino.(*ServerPeer).OnAddr-fm()
      /home/jon/go/pkg/mod/github.com/lightninglabs/[email protected]/neutrino.go:318 +0x4d
  github.com/btcsuite/btcd/peer.(*Peer).inHandler()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/peer/peer.go:1399 +0x1403
  github.com/btcsuite/btcd/peer.(*Peer).start·dwrap·5()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/peer/peer.go:2158 +0x39

Previous read at 0x00c0004251a0 by goroutine 242:
  github.com/btcsuite/btcd/addrmgr.(*KnownAddress).NetAddress()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/addrmgr/knownaddress.go:28 +0x1da
  github.com/lightninglabs/neutrino.NewChainService.func3()
      /home/jon/go/pkg/mod/github.com/lightninglabs/[email protected]/neutrino.go:773 +0x1e6
  github.com/btcsuite/btcd/connmgr.(*ConnManager).NewConnReq()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/connmgr/connmanager.go:391 +0x347
  github.com/btcsuite/btcd/connmgr.(*ConnManager).Start·dwrap·7()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/connmgr/connmanager.go:530 +0x39

Goroutine 54 (running) created at:
  github.com/btcsuite/btcd/peer.(*Peer).start()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/peer/peer.go:2158 +0x477
  github.com/btcsuite/btcd/peer.(*Peer).AssociateConnection.func1()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/peer/peer.go:2193 +0x34

Goroutine 242 (running) created at:
  github.com/btcsuite/btcd/connmgr.(*ConnManager).Start()
      /home/jon/go/pkg/mod/github.com/btcsuite/[email protected]/connmgr/connmanager.go:530 +0x1ed
  github.com/lightninglabs/neutrino.(*ChainService).Start·dwrap·21()
      /home/jon/go/pkg/mod/github.com/lightninglabs/[email protected]/neutrino.go:1541 +0x39
==================

Copy link
Member

@chappjc chappjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's it for my first pass. Just about everything looks fine.
Clearly there's something funny with the intermittent audit failures in livetest, and a whole lot of data races with neutrino and/or logging, but conceptually I don't see anything really wrong.

Comment on lines +1738 to +1704
if coinNotFound {
return nil, asset.CoinNotFoundError
}
Copy link
Member

@chappjc chappjc Oct 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so more actual auditing of the txData, but not totally passing just yet if it's not found on the network...
And if we decided to just pass the audit from txData and ack to server, we could just remove this check?
Any reason to delay on that change, just one step at a time? I think @itswisdomagain was proposing to simply audit a second time when the counterparty contract is confirmed, before broadcasting anything of our own.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe we should just audit (and re-broadcast?) the tx data in AuditContract without finding the output on-chain. We'll ack based on that, and then worry about finding the transaction with SwapConfirmation and be more permissive of CoinNotFound error there. I don't personally see a lot of value in double checking that the on-chain contract validates the same as the txData, but it looks like @itswisdomagain thought it was a good idea. What do you think @itswisdomagain? Can we get rid of a coin search in AuditContract altogether.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and be more permissive of CoinNotFound error there

I guess this is the twist.

I don't personally see a lot of value in double checking that the on-chain contract validates the same as the txData

Felt prudent to me too, but I'm not totally set on that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had some concerns about the possibility of finding an on-chain contract that differs from the txData that was validated, differs enough to have failed audit checks but after further consideration, the possibility became less real to me. Still felt safer to repeat audit before broadcasting a counter-swap or redeeming, but I'm not set on that either.

Wrote my initial thoughts on this topic here: #788 (comment).

@chappjc
Copy link
Member

chappjc commented Oct 14, 2021

Here's a weird one that's not related to logging at all, all inside neutrino and btcd code..

This does look like a btcd API design issue that makes the btcd/addrmgr.(*AddrManager).GetAddress method essentially unusable by consumers. Considering a PR: https://github.com/btcsuite/btcd/compare/master...chappjc:safe-addr-accessors?expand=1
Simply changing GetAddress to return a copy works too, but breaks their unit tests, and apparently the intended semantics of the API, even if it is impossible to use without a data race except in a contrived scenario like in the addrmgr unit tests.

neutrino has a virtually identical newAddressFunc closure that triggers this unavoidable data race, which I observed in my previous comment: https://github.com/lightninglabs/neutrino/blob/51686db787e01a3709b1583af3e20e916811d585/neutrino.go#L767-L773

If anyone else would care to look at it and give feedback I'd appreciate it. I'm not decided yet if I should raise the issue with the neutrino and/or btc devs. I've pinged @davecgh for his eyes on this.

Copy link
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some inline comments.

Comment on lines +1885 to +1895

if coinNotFound {
return nil, asset.CoinNotFoundError
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong. This doesn't change since the last check and there's no positive/non-err return path between there and here. It could return much earlier.

@chappjc
Copy link
Member

chappjc commented Oct 14, 2021

Different race, but also related to logging. I see a bunch that are with livetest.go:63 and livetest.go:69 like this.

Seems like it's happening because btc.NewWallet is called in createWallet in regnet_test.go and again in spvConstructor just below it. Unless I'm mistaken, we want createWallet to return the wallet to spvConstructor

EDIT: Hmm, now I see you are doing a cm.Disconnect in createWallet, so that it is a temporary wallet instance. However, It appears that there's no synchronization in Disconnect to make sure the w.stop() is actually executed before unblocking.

EDIT 2: confirmed that at least some of the test races are because wallet shutdown wasn't waiting on stop. The annoying global neutrino/btcsuite loggers were also a deal breaker since you cannot set them up again with a running spvWallet, like we have with three of them in regnet_test.go. Messy hacking to illustrate where I arrived: 0b0759d

Copy link
Member

@itswisdomagain itswisdomagain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't really gone through all the changes yet, was just going through the changes to the asset.Driver interface and have a question. Will do a more thorough review later.

return newError(passwordErr, "cannot set a password on a seeded wallet")
}

exists, err := asset.WalletExists(assetID, form.Type, c.assetDataDirectory(assetID), form.Config, c.net)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The btc driver implementation of this method (haven't checked others) expects that this method may be called for RPC wallets as well. Is that the case or is that planned?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but I think I'm going to remove this RPC checking capability and just doc that the method is for seeded wallets only. Does that work?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with me.

Although the seeded vs built-in terminology can be confusing I think because as has been pointed out, most wallets are seeded even the external ones, when really when we say "seeded" we mean created and managed by the dex process. Internally we have solidified the seeded concept though, even for wallets like eth where we're shoehorning the seed because geth has no idea what a seed is for.

Whatever makes sense to you though. These are code docs, not user-facing messages.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internally we have solidified the seeded concept though

I generally prefer to use descriptive terms rather than potentially confusing terms which meanings may need to be explained to new/other devs. Yes, the seeded concept has been clarified but docs and variable names should also reflect this clarification so the confusion don't re-arise tomorrow.

@@ -22,6 +22,27 @@ const (
ErrUnsupported = dex.ErrorKind("unsupported")
)

type WalletDefinition struct {
// If seeded is true, the Setup method will be provided a detereministic
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create method instead of Setup method.

Comment on lines +254 to +255
// Probably not worth doing. Just let decodeWallet_v0 append the nil and pass it
// up the chain.
Copy link
Member

@JoeGruffins JoeGruffins Oct 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should still increment a database version. If one had a wallet update, silently, and tried to use the previous version with the same database, they might not know why it no longer works.

Trying it out, that is going back to master from this pr, there is this log, that doesn't stop anything, but I think upping the db version would and should.

 [ERR] CORE: error loading wallets from database: DecodeWallet error: unknown DecodeWallet version 1

And then there is no indication on the UI, it spins for a while, and you can add wallets again, but if you reset the app they don't load again.

Copy link
Member

@itswisdomagain itswisdomagain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass. A few wording change suggestions but as previously discussed, no need to waste time on them right away if they aren't straightforward.

Next, I'll look at the btc spv changes in particular and the btc AuditContract and SwapConfirmations changes.

)

var (
driversMtx sync.RWMutex
drivers = make(map[uint32]Driver)
)

// CreateWalletParams are the parameters for internal wallet creation. The
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to bring this up and doesn't need to be addressed right away or at all even, but this sounds like these parameters are only meant for builtin/internal wallets. If dev wordings in other places are selected to accommodate the possibility of using CreateWallet with external wallets, might as well do so here.

Comment on lines 21 to 23
// CreateWalletParams are the parameters for internal wallet creation. The
// Settings provided should be the same wallet configuration settings passed to
// Create.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked about this in #1225, I do not completely understand these references to Settings. Is my understanding below correct?

The settings provided - the Settings field of this CreateWalletParams
wallet configuration settings passed to ... - the settings passed to OpenWallet?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this is saying it's the same settings map in WalletConfig, passed to OpenWallet, as you say.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Same typo here as there. Sorry about that.

AvailableWallets: []*asset.WalletDefinition{{
Type: walletTypeRPC,
Tab: "External",
Description: "Connect to bitcoind",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also uses a bitcoind binary? Maybe a comment if so, quite surprising and confusing. I also noticed above that walletTypeRPC = "bitcoindRPC".

Comment on lines +41 to +45
netPorts = dexbtc.NetPorts{
Mainnet: "8332",
Testnet: "18332",
Simnet: "18443",
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a biggie, but doesn't this increase the scope and lifetime of this variable, compared to when it was just local to a function?

@@ -2092,8 +2313,8 @@ func (btc *ExchangeWallet) Address() (string, error) {

// PayFee sends the dex registration fee. Transaction fees are in addition to
// the registration fee, and the fee rate is taken from the DEX configuration.
func (btc *ExchangeWallet) PayFee(address string, regFee uint64) (asset.Coin, error) {
txHash, vout, sent, err := btc.send(address, regFee, btc.feeRateWithFallback(1, 0), false)
func (btc *ExchangeWallet) PayFee(address string, regFee, feeRateSuggestion uint64) (asset.Coin, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need similar changes for refund redeem and withdraw I think.
Just need to be careful not to make server RPCs to fetch the rate on paths that block other actions.

@buck54321
Copy link
Member Author

buck54321 commented Oct 21, 2021

Looks like our balance check is a race with the wallet's internal scan after a block notification. It took two blocks to see a balance when it should have been one. Damn.

buck54321 and others added 21 commits October 21, 2021 10:58
Add a built-in Bitcoin SPV wallet. Uses neutrino and btcwallet.

client/asset:

Add Create and Exists methods to the Driver. Create is used to
create a "seeded" (native, built-in) wallet, and should
not be used for non-seeded wallets. Exists checks the existence of
a wallet. Exists is written so far to check even RPC wallet
existence, though it's only used for seeded wallets in production.

Adds the WalletDefinition type. WalletInfo has a new field,
AvailableWallets []*WalletDefinition.

WalletConfig type has a new field, Type string, which should match
the Type field of one of the AvailableWallets. Existing backends
must handle Type == "" for pre-existing wallets.

client/asset/{dcr,ltc,bch,eth}: minor adaptations to fit the
Driver and WalletDefinition changes.

client/asset/btc:

Quite a few changes to configuration parsing to more clearly
separate RPC config from wallet settings. New spvWallet type
manages the *btcwallet.Wallet and *neutrino.ChainService. The
Wallet interface was already defined, so the work was just
implementing the methods using direct calls to wallet methods.

Everything is run through an interface for testing. Existing tests
required relatively few modifications to work with spv as well.
There are just a couple of new unit tests specific to the more
nuanced spv methods. The regnet_test.go/livetest.go test is adapted
to run an all-SPV trio for one test.

client/core: Work with wallet definition and give special handling
to seeded wallets.

front-end:

NewWalletForm and WalletConfigForm updated to work with
wallet definitions. Wallet type is selected via a tab at the top of
the NewWalletForm. Seeded wallets take no user-supplied password
(enforced in core). Special handling of headers and options so that
creating a seeded wallet can be very close to a 1-button, 1-click
experience.

A new form is added after the new wallet form conditionally if the
wallet balance is lower than required for the fee. This will always
be the case for the first native spv wallet. The new form shows a
deposit address and the current balance, and directs the user to
fund the address for registration fees. The balance is checked in
a loop and progresses back to the fee confirmation form when the
balance is sufficient. When working on simnet, just fund it from
the alpha node in harness-ctl.

client/db: Store the new Type field as part of the wallet. I wrote
an upgrader to add the new field, but we're not populating it
anyway, and existing wallets handle that, so I don't see a good
reason to "upgrade".
Refactor initialization and connection. Move nearly all instantiation
to connect. There is an assumption here that the caller will never
try to use an unconnected wallet to do anything. Upon inspection, I
think this is how we've designed things, but it's worth mentioning
since calling some methods before connecting will definitely panic
now.

Log btcwallet and neutrino output to a rotating log file.

Combine loadChainClient and startWallet functions into a single
(*spvWallet).startWallet method.
Unlike bitcoind, btcwallet has option to subtract fees from sent
value. Implement send with subtract from scratch.
@buck54321
Copy link
Member Author

We still have a lag in reporting correct balance, but other issues should be addressed now.

Comment on lines +2088 to +2092
oldDef, err := walletDefinition(assetID, oldWallet.walletType)
// Error can be normal if the wallet was created before wallet types
// were a thing. Just assume this is an old wallet and therefore not
// seeded.
if err == nil && oldDef.Seeded {
Copy link
Member

@chappjc chappjc Oct 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already have oldDef from 30 lines up before if walletDef.Seeded.
It also looks like you solved the problem in the comment with LegacyWalletIndex

Copy link
Member

@chappjc chappjc Oct 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be good without the walletDefinition call and using the oldDef you have like:

if oldDef.Seeded && oldWallet.connected() {
	oldWallet.Disconnect()
	restartOnFail = true
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NVM, I have some updates that will follow. Going to merge this shortly.

Copy link
Member

@chappjc chappjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working well for the most part. We're all going to start iterating on issues we find.

I have a ReconfigureWallet update coming to address setting the password on the seeded wallet.

@chappjc chappjc merged commit d202f6d into decred:master Oct 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants