-
Notifications
You must be signed in to change notification settings - Fork 602
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
CurrentAddress - mark first address used and return new address #238
Conversation
This solves part of the problem I was facing. I think the first generated external address should be also be added to first rescan job. Right now, despite calling .CurrentAddress, the first address isn't included in the rescan job. This issue might need to be fixed in the wallet package. Also, IMO .NewAddress should be changed to .newAddress (un-exported) |
What about erroring on last in/external address if none have been created yet? It should never have been creating new addresses to begin with. Other option here is to keep the api similar but write the address to the db, but this changes an otherwise read-only call to one that may require a db update. |
@jrick That will work too. It means that .CurrentAddress can implement the error handling for .LastExternalAddress and call .NewAddress. Although, this approach would mean that the first rescan job will have 0 addresses. It would scan for the first address only after a call to .CurrentAddress (Not ideal, I'd much rather have the first address be included on the first rescan job. But that is something that can be modified by wallet developers) |
The rescan with 0 addresses is actually intentional because the response that btcwallet gets after the finished rescan is the block that the wallet is marked in sync with. However, btcd should detect the 0 address 0 outpoint rescan and not actually load blocks and compare addresses against empty maps in that case. One thing that appears broken with the current waddrmgr is that when a full rescan is performed (I noticed after explicitly dropping all transaction history on my intwtxmgr branch) the rescan starts from the genesis block and not the latest seen block just before the first address was created. If this is fixed, then the rescan can be avoided, and when there are still no addresses, that initial starting sync block can be continuously updated, until the first address is created. I also like this solution since addresses never exist before the user requests them (external) or they're needed for change (internal), and it will help avoid address reuse. Another thought: since the rescan for the 0 address case is only being used to mark what block the wallet is in sync with, and we can continuously update this block from the websocket notifications as new blocks are attached, the rescan can be avoided. But we still need an RPC round trip to fetch the current block height/hash, so I don't think it really matters either way, and leaving it in the rescan means less code in wallet. This has gotten a little rambly, but does that make sense? |
7852baf
to
f68e78a
Compare
return acctInfo.lastExternalAddr, nil | ||
} | ||
|
||
return nil, managerError(ErrAddressNotFound, "address not found", nil) |
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.
Since this isn't looking up a particular address by string, hash160, etc., can you change the error description string to something like "no previous external address"?
Also, need a similar error for LastInternalAddress below.
Please update the PR title and description, since this problem is being fixed by a different method. |
3570331
to
7f7903f
Compare
Yup, I agree about not creating addresses before they are required. Updated |
dcea4e0
to
3818ac7
Compare
@@ -1031,6 +1019,18 @@ func fetchAddressByHash(tx walletdb.Tx, addrHash []byte) (interface{}, error) { | |||
return nil, managerError(ErrDatabase, str, nil) | |||
} | |||
|
|||
// fetchAddressUsed returns true if the provided address id was flagged as used. | |||
func fetchAddressUsed(tx walletdb.Tx, addressID []byte) bool { |
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.
As discussed on IRC, I'd like to see all usage of the phrase "address ID" replaced with something like "Hash160", since it is the ripemd160 after sha256 of the pubkey, or the script. This is ok for now, but please fix this in another pr.
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.
OK. I'm going to keep any new vars in this PR as addressID so I can find and replace all of them in the other PR.
The way .CurrentAddress is implemented it would generate only 1 .NewAddress and stop. I don't think it will work well, when a wallet is being restored from a seed. 1st address, 0 balance, 2 transactions .CurrentAddress in this scenario would be expected to return the 3rd ext address. It should recursively generate a new address until it comes across an address with 0 balance and 0 transactions. (I've assumed change addresses are generated from a different branch and hence the numbers) |
CurrentAddress only exists to implement the getaccountaddress rpc. It's ok if it's not terribly useful for recovering from a seed, since it wasn't designed for that use case. In fact I wouldn't mind moving the CurrentAddress implementation from the wallet package into rpcserver.go in main, since it's only to be used for that. Doing so would also help to discourage address reuse, since a previously shown address will never be re-shown in case nothing was received by it. |
859721f
to
4775040
Compare
|
||
// MarkUsed updates the used flag for the provided address. | ||
func (m *Manager) MarkUsed(address btcutil.Address) error { | ||
addressID := address.ScriptAddress() |
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.
@jrick I think this was the external API call you mentioned which takes addressID
so I hope you're fine with changing the signature. We might need to notify any callers but since this was added only recently I don't think anyone would be using it.
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.
as long as it's a different type, finding and unbreaking callers is easy.
@jrick @davecgh I think the larger problem is that we're storing the mutable |
I think the used flag is still correct. If a transaction is sent to some address, and the address is marked "used", then if that transaction is removed from wtxmgr (perhaps it was double spent), the address shouldn't become unused. Once used, the address should never become unused again. |
Yeah, just checked wtxmgr and looks like it is ranging over all transactions. OK, let me test this out and prep for review. Thanks. |
Where is it ranging transactions? Only place I can remember it doing that and filtering by address is |
Well, let's keep things simple. Until waddrmgr and wtxmgr are properly combined (this will happen after the current wtxmgr integration is merged), we'll keep the transaction iterating to detect used-ness. Once combined, we'll introduce this flag again. |
Yeah, I was checking if |
@@ -1028,28 +1028,32 @@ func testMarkUsed(tc *testContext) bool { | |||
addrHash := expectedAddr1.addressHash | |||
addr, err := btcutil.NewAddressPubKeyHash(addrHash, chainParams) | |||
|
|||
maddr, err := tc.manager.Address(addr) | |||
if err != nil { | |||
tc.t.Errorf("%s: unexpected error: %v", prefix, err) |
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't continue with the rest of this test, either return here of make this fatal
c90346f
to
1f90d3d
Compare
All of the waddrmgr changes look ok now but we need handling in Wallet.CurrentAddress so that if the error is because no addresses exist yet, it handles it by creating the first external address. |
1f90d3d
to
4157111
Compare
Updated |
Thanks, going to test this out but the diff looks ok now. Can you squash? |
9f8fb3b
to
96b20e4
Compare
Sure, done. |
sorry, rebase on master too |
96b20e4
to
74208f9
Compare
OK, rebased. |
Fixed
Last(In/Ex)ternalAddress
to make sureCurrentAddress
returns a new address when the first and subsequent addresses are flagged as used.