diff --git a/chain/sync.go b/chain/sync.go index 41fff3262..1d60651ad 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -627,6 +627,10 @@ func (s *Syncer) Run(ctx context.Context) (err error) { log.Infof("Transactions synced through block %v height %d", &tipHash, tipHeight) } + if err := s.wallet.CheckBirthState(ctx, rescanPoint); err != nil { + return err + } + err = s.waitRPCSync(ctx, int64(tipHeight)) if err != nil { return err diff --git a/spv/sync.go b/spv/sync.go index cb1c6ce43..fb3008be6 100644 --- a/spv/sync.go +++ b/spv/sync.go @@ -1762,6 +1762,11 @@ func (s *Syncer) initialSyncRescan(ctx context.Context) error { if err != nil { return err } + + if err := s.wallet.CheckBirthState(ctx, rescanPoint); err != nil { + return err + } + if rescanPoint == nil { // The wallet is already up to date with transactions in all // blocks. Load the data filters to check for transactions in diff --git a/wallet/rescan.go b/wallet/rescan.go index 8b5ad6d0c..084656140 100644 --- a/wallet/rescan.go +++ b/wallet/rescan.go @@ -494,9 +494,13 @@ func (w *Wallet) rescanPoint(dbtx walletdb.ReadTx) (*chainhash.Hash, error) { return &rescanPoint, nil } -// SetBirthState sets the birthday state in the database. +// SetBirthState sets the birthday state in the database. This should be called +// before syncing is started. func (w *Wallet) SetBirthState(ctx context.Context, bs *udb.BirthdayState) error { const op errors.Op = "wallet.SetBirthState" + if bs == nil { + return errors.E(op, errors.Invalid, "nil birthday state") + } err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error { return udb.SetBirthState(dbtx, bs) }) @@ -506,7 +510,8 @@ func (w *Wallet) SetBirthState(ctx context.Context, bs *udb.BirthdayState) error return nil } -// BirthState returns the birthday state. +// BirthState returns the birthday state. Will return a nil state if none has +// been set. func (w *Wallet) BirthState(ctx context.Context) (bs *udb.BirthdayState, err error) { const op errors.Op = "wallet.BirthState" err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { diff --git a/wallet/udb/txmined.go b/wallet/udb/txmined.go index b6d8869a4..417e6b484 100644 --- a/wallet/udb/txmined.go +++ b/wallet/udb/txmined.go @@ -331,7 +331,8 @@ type BirthdayState struct { SetFromHeight, SetFromTime bool } -// SetBirthState sets the birthday state in the database. +// SetBirthState sets the birthday state in the database. *BirthdayState must +// not be nil. // // [0:1] Options (1 byte) // [1:33] Birthblock block header hash (32 bytes) @@ -361,7 +362,8 @@ func SetBirthState(dbtx walletdb.ReadWriteTx, bs *BirthdayState) error { return ns.Put(rootBirthState, v) } -// BirthState returns the current birthday state. +// BirthState returns the current birthday state. Will return nil if none has +// been set. func BirthState(dbtx walletdb.ReadTx) *BirthdayState { ns := dbtx.ReadBucket(wtxmgrBucketKey) const ( diff --git a/wallet/wallet.go b/wallet/wallet.go index 0aca87703..3d62a7c66 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -5897,3 +5897,84 @@ func (w *Wallet) ProcessedTickets(ctx context.Context) ([]*VSPTicket, error) { return managedTickets, nil } + +// CheckBirthState sets the wallet birthstate if required and if we already have +// enough info to do so. +func (w *Wallet) CheckBirthState(ctx context.Context, rescanPoint *chainhash.Hash) error { + birthState, err := w.BirthState(ctx) + if err != nil { + return err + } + + if birthState != nil && (birthState.SetFromTime || birthState.SetFromHeight) { + var syncedHeader *wire.BlockHeader + if rescanPoint != nil { + syncedHeader, err = w.BlockHeader(ctx, rescanPoint) + if err != nil { + return err + } + } else { + tipHash, _ := w.MainChainTip(ctx) + syncedHeader, err = w.BlockHeader(ctx, &tipHash) + if err != nil { + return err + } + } + if birthState.SetFromTime { + if syncedHeader.Timestamp.After(birthState.Time) { + h := syncedHeader + for { + if h.Height == 0 { + bh := h.BlockHash() + birthState.Hash = bh + birthState.Height = 0 + birthState.SetFromTime = false + if err := w.SetBirthState(ctx, birthState); err != nil { + return err + } + log.Infof("Set wallet birthday to block 0 (%v).", bh) + break + } + h, err = w.BlockHeader(ctx, &h.PrevBlock) + if err != nil { + return err + } + if h.Timestamp.Before(birthState.Time) { + bh := h.PrevBlock + height := h.Height - 1 + birthState.Hash = bh + birthState.Height = height + birthState.SetFromTime = false + if err := w.SetBirthState(ctx, birthState); err != nil { + return err + } + log.Infof("Set wallet birthday to block %d (%v).", height, bh) + break + } + } + } + } + if birthState.SetFromHeight { + if syncedHeader.Height >= birthState.Height { + h := syncedHeader + for { + if h.Height == birthState.Height { + bh := h.BlockHash() + birthState.Hash = bh + birthState.SetFromHeight = false + if err := w.SetBirthState(ctx, birthState); err != nil { + return err + } + log.Infof("Set wallet birthday to block %d (%v).", h.Height, bh) + break + } + h, err = w.BlockHeader(ctx, &h.PrevBlock) + if err != nil { + return err + } + } + } + } + } + return nil +}