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
wallet: Add birthday. #2347
wallet: Add birthday. #2347
Conversation
592825b
to
e52f2f2
Compare
wallet/udb/txmined.go
Outdated
// | ||
// [0:32] Block header hash (32 bytes) | ||
// [32:40] Birthday time (8 bytes) | ||
func (s *Store) SetBirthday(dbtx walletdb.ReadWriteTx, birthday *time.Time) (*chainhash.Hash, 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.
This looks like a mess, so if there is anything simpler to do, please make a suggestion. It goes backwards using an approximation based on the amount of time to the birthday compared to the lifetime of the chain. Then it goes forward one at a time until it goes past the time we want and uses the header before that.
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 really think the birthday should internally be just "birthblock" (height+hash), and then that can be specified and recorded precisely.
You can then have multiple ways of specifying the birthblock when creating the wallet: by height or hash directly, by timestamp (like you do here), by "decred day" (like aezeed does) or any other method.
As you notice, you'll need two sets of states: the user-specified birthday, set when invoking with --create
(e.g. --birthtimestamp
, --birthheight
, etc) and then a separate state that records the actual determined birthday hash and height (because you can't determine the hash before sync and sync may take an arbitrary amount of time to complete).
Instead of doing this process here, this can probably (haven't thought this through completely) be done inside the main db tx of wallet.ChainSwitch()
: if the birthblock hash is not set, then go through the logic of determining the birthday within the set of the batch of headers being processed. This has the advantage of working both for SPV and RPC sync modes, avoids going through this process multiple times and meshes perfectly with the initial sync process, such that by the end of the initial sync, the birthblock will be set and ready to be used as the initial rescan point.
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.
Thank you! Doing what you said in wallet.ChainSwitch()
I think. Please look again, the changes also https://github.com/decred/dcrwallet/compare/e52f2f26d5e210dde5d0b1c01aa1b8a46f4d4099..80145bce22dd5604c93e2c6e25cd5c72a0063885
Now allowing the consumer to set the intent to set a birthday, so two separate values in the db now.
If this looks ok, I guess we still want to use the birthday for rescans with default values, so I will add that if I can.
wallet/udb/txmined.go
Outdated
// | ||
// [0:32] Block header hash (32 bytes) | ||
// [32:40] Birthday time (8 bytes) | ||
func (s *Store) SetBirthday(dbtx walletdb.ReadWriteTx, birthday *time.Time) (*chainhash.Hash, 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.
I really think the birthday should internally be just "birthblock" (height+hash), and then that can be specified and recorded precisely.
You can then have multiple ways of specifying the birthblock when creating the wallet: by height or hash directly, by timestamp (like you do here), by "decred day" (like aezeed does) or any other method.
As you notice, you'll need two sets of states: the user-specified birthday, set when invoking with --create
(e.g. --birthtimestamp
, --birthheight
, etc) and then a separate state that records the actual determined birthday hash and height (because you can't determine the hash before sync and sync may take an arbitrary amount of time to complete).
Instead of doing this process here, this can probably (haven't thought this through completely) be done inside the main db tx of wallet.ChainSwitch()
: if the birthblock hash is not set, then go through the logic of determining the birthday within the set of the batch of headers being processed. This has the advantage of working both for SPV and RPC sync modes, avoids going through this process multiple times and meshes perfectly with the initial sync process, such that by the end of the initial sync, the birthblock will be set and ready to be used as the initial rescan point.
e52f2f2
to
80145bc
Compare
aa31212
to
3da2ec7
Compare
I'm not sure if I need to do anything for the rescans. They will start from zero by default but you can set a block header or height. Should the birthday info be included in walletinfo or a new rpc call to help there? |
internal/prompt/prompt.go
Outdated
// Birthday prompts for a wallet birthday. | ||
func Birthday(reader *bufio.Reader) (*time.Time, error) { | ||
for { | ||
fmt.Printf("Do you have a wallet birthday we should rescan from? (enter date as YYYY-MM-DD, or 'no') [no]: ") |
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.
Personally, I'd also add support to accept a block height here, so the user can specify either a date or the exact block height, but this should have input from @jrick to double check.
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 seems reasonable, although it's not too difficult to look up a similar date using the block explorer for whatever height you want to use.
the prompt could accept either input, if it's an integer, use block height, if it can be parsed as a date, use that instead.
Yeah, I'm pretty sure you want both account/address discovery and rescans to start from the birthday (when it is set) for performance reasons, unless a specific block height/hash has been specified on a particular rescan call. |
Hard to do this without changing a lot I think. Default height is zero so there's not a way to differentiate between default height and if a user really wants to rescan from zero. They may want to rescan before the birthday for example if some data was imported into the wallet. I guess we can have -1 be a special case? Would that be ok? |
3da2ec7
to
1486597
Compare
@matheusd Just one birthstate now. Added the 24 hour buffer. It is missing a lot of comments. Still unsure:
|
wallet/chainntfns.go
Outdated
// In the case that the birthday will not be reached, | ||
// store a hash for initial sync to start from. Do not | ||
// store the tip hash as that will cause w.rescanPoint | ||
// to return nil. | ||
bh := n.Header.PrevBlock | ||
if err := w.txStore.UpdateProcessedTxsBlockMarker(dbtx, &bh); err != nil { | ||
return 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.
I'm not sure you need this case. At this point in the code, you know the birthday will bet set at some point in the future (in which case one of the prior branches will be reached), so what's the need to do this for every batch of blocks until you reach the birthday height?
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.
If the birthday is set in the future then, although we don't need to, we will scan from genesis.
I will remove the check anyway though since this is user error I suppose. If the birthday is set to a time or block in the future, it is not fatal.
I guess I will try adding the birthblock to walletinfo? imo that is enough, but we can do more... now or later. |
1486597
to
9449b2c
Compare
Added allowing restoring from block to the prompt. Moved the birthday verification there. Added methods for rpc and grpc to see the birthday https://github.com/decred/dcrwallet/compare/1486597870ae26b392ded4fd46648f96e8c3db10..9449b2c2fe8fb3ce846b87f891209f1b0a8dc7c5 |
db16f9f
to
c1e6035
Compare
Is this ready for testing or are you planning on adding any other features to this? Asking so I can start functional testing on it... |
Yes it is ready. I don't plan to add anything unless requested. |
I could add a unit test for the db functions SetBirthState and BirthState. I have one half made. |
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.
Functional testing worked. Only have a couple more nits on the code, and then a few more general questions:
- Thoughts on adding a db migration to add the birthday state to existing wallets? This would ensure every wallet has the birthday state (with existing wallets pointing to genesis)
- Just to double check, but additional
rescanwallet
with no arguments will NOT rely on the birthday to perform the rescan, right? It will be up to the users to callwalletinfo
and then use the birthblock as argument to ensure the rescan only goes through blocks after the birthblock. - We should probably modify the account and address discovery procedures in SPV mode to use blocks only after the birthblock. This would make these procedures significantly faster, specially for new wallets (which will have a very recent birthblock). The relevant place to start is here
Yes that keeps this pr simple. I think this work can still be done though if necessary. As I said I think this will require changes to both the grpc and rpc api to distinguish between a rescan from the height 0 and from birthday. |
Just rebased. |
Added a db upgrade https://github.com/decred/dcrwallet/compare/475cabd342ed69e40b9b31831a187837c24358f3..70da522eb5cb5163e44d3091a217c0bde51f9804 I changed that one spot for spv wallets that you showed and looking if there are others... Are there other spots that need changing in |
This is looking pretty good:
One more thing I remembered is adding the birthday ({time,height}) to the |
Add time and height params to the createwallet grpc request https://github.com/decred/dcrwallet/compare/70da522eb5cb5163e44d3091a217c0bde51f9804..47e49d626db7d68ed51d51fc409e04fa5bd66116 Also change the main if block to exclude block zero although it seems to always start from block one anyhow |
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.
LGTM!
Nice work!
Add a birthday state to the database than can be used when creating a new wallet to restore from a certain date or time.
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.
great work
closes #2346
closes #320