diff --git a/waddrmgr/db.go b/waddrmgr/db.go index f0a9b12..ae49dc9 100644 --- a/waddrmgr/db.go +++ b/waddrmgr/db.go @@ -194,6 +194,7 @@ var ( // Sync related key names (sync bucket). syncedToName = []byte("syncedto") startBlockName = []byte("startblock") + birthdayName = []byte("birthday") // Account related key names (account bucket). acctNumAcctsName = []byte("numaccts") @@ -1489,6 +1490,37 @@ func putStartBlock(ns walletdb.ReadWriteBucket, bs *BlockStamp) error { return nil } +// fetchBirthday loads the manager's bithday timestamp from the database. +func fetchBirthday(ns walletdb.ReadBucket) (time.Time, error) { + bucket := ns.NestedReadBucket(syncBucketName) + + var t time.Time + + buf := bucket.Get(birthdayName) + if len(buf) != 8 { + str := "malformed birthday stored in database" + return t, managerError(ErrDatabase, str, nil) + } + + t = time.Unix(int64(binary.BigEndian.Uint64(buf)), 0) + return t, nil +} + +// putBirthday stores the provided birthday timestamp to the database. +func putBirthday(ns walletdb.ReadWriteBucket, t time.Time) error { + bucket := ns.NestedReadWriteBucket(syncBucketName) + + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(t.Unix())) + + err := bucket.Put(birthdayName, buf) + if err != nil { + str := "failed to store birthday" + return managerError(ErrDatabase, str, err) + } + return nil +} + // managerExists returns whether or not the manager has already been created // in the given database namespace. func managerExists(ns walletdb.ReadBucket) bool { diff --git a/waddrmgr/manager.go b/waddrmgr/manager.go index 77ea6e0..7a27849 100644 --- a/waddrmgr/manager.go +++ b/waddrmgr/manager.go @@ -9,6 +9,7 @@ import ( "crypto/sha512" "fmt" "sync" + "time" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg" @@ -249,6 +250,7 @@ type Manager struct { chainParams *chaincfg.Params addrs map[addrKey]ManagedAddress syncState syncState + birthday time.Time watchingOnly bool locked bool closed bool @@ -1985,12 +1987,13 @@ func (m *Manager) Decrypt(keyType CryptoKeyType, in []byte) ([]byte, error) { func newManager(chainParams *chaincfg.Params, masterKeyPub *snacl.SecretKey, masterKeyPriv *snacl.SecretKey, cryptoKeyPub EncryptorDecryptor, cryptoKeyPrivEncrypted, cryptoKeyScriptEncrypted []byte, syncInfo *syncState, - privPassphraseSalt [saltSize]byte) *Manager { + birthday time.Time, privPassphraseSalt [saltSize]byte) *Manager { return &Manager{ chainParams: chainParams, addrs: make(map[addrKey]ManagedAddress), syncState: *syncInfo, + birthday: birthday, locked: true, acctInfo: make(map[uint32]*accountInfo), masterKeyPub: masterKeyPub, @@ -2124,6 +2127,10 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte, chainParams *chai if err != nil { return nil, maybeConvertDbError(err) } + birthday, err := fetchBirthday(ns) + if err != nil { + return nil, maybeConvertDbError(err) + } // When not a watching-only manager, set the master private key params, // but don't derive it now since the manager starts off locked. @@ -2174,7 +2181,7 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte, chainParams *chai // call to new with the values loaded from the database. mgr := newManager(chainParams, &masterKeyPub, &masterKeyPriv, cryptoKeyPub, cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo, - privPassphraseSalt) + birthday, privPassphraseSalt) mgr.watchingOnly = watchingOnly return mgr, nil } @@ -2438,6 +2445,11 @@ func Create(ns walletdb.ReadWriteBucket, seed, pubPassphrase, privPassphrase []b if err != nil { return err } + // Use 48 hours as margin of safety for wallet birthday. + err = putBirthday(ns, time.Now().Add(-48*time.Hour)) + if err != nil { + return err + } // Save the information for the imported account to the database. err = putAccountInfo(ns, ImportedAddrAccount, nil, diff --git a/waddrmgr/sync.go b/waddrmgr/sync.go index 21e2648..b55870a 100644 --- a/waddrmgr/sync.go +++ b/waddrmgr/sync.go @@ -5,6 +5,8 @@ package waddrmgr import ( + "time" + "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcwallet/walletdb" ) @@ -87,3 +89,23 @@ func (m *Manager) BlockHash(ns walletdb.ReadBucket, height int32) ( return fetchBlockHash(ns, height) } + +// Birthday returns the birthday, or earliest time a key could have been used, +// for the manager. +func (m *Manager) Birthday() time.Time { + m.mtx.Lock() + defer m.mtx.Unlock() + + return m.birthday +} + +// SetBirthday sets the birthday, or earliest time a key could have been used, +// for the manager. +func (m *Manager) SetBirthday(ns walletdb.ReadWriteBucket, + birthday time.Time) error { + m.mtx.Lock() + defer m.mtx.Unlock() + + m.birthday = birthday + return putBirthday(ns, birthday) +}