-
Notifications
You must be signed in to change notification settings - Fork 184
wallet: Do not download cfilters before birthday. #2506
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -198,8 +198,8 @@ func (s *Store) MainChainTip(dbtx walletdb.ReadTx) (chainhash.Hash, int32) { | |
| // If the block is already inserted and part of the main chain, an errors.Exist | ||
| // error is returned. | ||
| // | ||
| // The main chain tip may not be extended unless compact filters have been saved | ||
| // for all existing main chain blocks. | ||
| // The main chain may be extended without cfilters if this block is before the | ||
| // wallet birthday. If the filter is nil it will not be saved to the database. | ||
| func (s *Store) ExtendMainChain(ns walletdb.ReadWriteBucket, header *wire.BlockHeader, blockHash *chainhash.Hash, f *gcs2.FilterV2) error { | ||
| height := int32(header.Height) | ||
| if height < 1 { | ||
|
|
@@ -266,9 +266,12 @@ func (s *Store) ExtendMainChain(ns walletdb.ReadWriteBucket, header *wire.BlockH | |
| return err | ||
| } | ||
|
|
||
| // Save the compact filter. | ||
| bcf2Key := blockcf2.Key(&header.MerkleRoot) | ||
| return putRawCFilter(ns, blockHash[:], valueRawCFilter2(bcf2Key, f.Bytes())) | ||
| // Save the compact filter if we have it. | ||
| if f != nil { | ||
| bcf2Key := blockcf2.Key(&header.MerkleRoot) | ||
| return putRawCFilter(ns, blockHash[:], valueRawCFilter2(bcf2Key, f.Bytes())) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // ProcessedTxsBlockMarker returns the hash of the block which records the last | ||
|
|
@@ -331,6 +334,17 @@ type BirthdayState struct { | |
| SetFromHeight, SetFromTime bool | ||
| } | ||
|
|
||
| // AfterBirthday returns whether the given block header is at or after the | ||
| // birthday. If SetFromTime is true, the header's timestamp is compared against | ||
| // the birthday time. Otherwise, the header's height is compared against the | ||
| // birthday height. | ||
| func (bs *BirthdayState) AfterBirthday(h *wire.BlockHeader) bool { | ||
| if bs.SetFromTime { | ||
| return h.Timestamp.After(bs.Time) | ||
| } | ||
| return h.Height >= bs.Height | ||
| } | ||
|
|
||
| // SetBirthState sets the birthday state in the database. *BirthdayState must | ||
| // not be nil. | ||
| // | ||
|
|
@@ -402,19 +416,37 @@ func (s *Store) IsMissingMainChainCFilters(dbtx walletdb.ReadTx) bool { | |
| return len(v) != 1 || v[0] == 0 | ||
| } | ||
|
|
||
| // SetMissingMainChainCFilters sets whether we have all of the main chain | ||
| // cfilters. Should be used to set missing to false if the wallet birthday is | ||
| // moved back in time. | ||
| func (s *Store) SetMissingMainChainCFilters(dbtx walletdb.ReadWriteTx, have bool) error { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of a boolean value, we could just record the earliest block height that we have saved cfilters from. If that's 0, it's the same as this recording true. If nonzero, it's the same as false but gives us additional information to work with (and return in the function just below this).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, all current wallets are 0 so this will not need a db upgrade. Working on it.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think maybe we still want to set whether we have all filters. If we do a rescan and the wallet is shut off before all of the filters are downloaded, we need to know that when starting up.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, do you mean replace the boolean with a block number? I guess it would need an upgrade in the case of false.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I looked into doing this but unsure if it simplifies the process. If we only save a number, and zero has the meaning that we have them all, then we loose the value of having the number except while downloading. I think the current code handles that well enough though, and it is only when changing the birthday. It may also require a new db version to replace the current boolean if I understand correctly.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The birthday height would have the same meaning as a new db value, I believe.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. true, i think two fields should be used, the height/block of the first cfilter, and the last cfilter
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Am I understanding correctly in that we should replace the bool or add more. I'll tinker some more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The birthday would be where we want to save from, but it could be earlier. So another db value for where the actual first cfilter should be, and where the last one is. The last one will be the head block filter once we are synced, will we update that every block?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It still looks like more code for no gain to me unless I am mistaking something. The problem is that if we set the birthday back a second time there's a new gap, so we either need to save more variables or look through everything similarly to what we are doing now. |
||
| haveB := []byte{0} | ||
| if have { | ||
| haveB = []byte{1} | ||
| } | ||
| err := dbtx.ReadWriteBucket(wtxmgrBucketKey).Put(rootHaveCFilters, haveB) | ||
| if err != nil { | ||
| return errors.E(errors.IO, err) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // MissingCFiltersHeight returns the first main chain block height | ||
| // with a missing cfilter. Errors with NotExist when all main chain | ||
| // blocks record cfilters. | ||
| func (s *Store) MissingCFiltersHeight(dbtx walletdb.ReadTx) (int32, error) { | ||
| func MissingCFiltersHeight(dbtx walletdb.ReadTx, fromHeight int32) (int32, error) { | ||
| ns := dbtx.ReadBucket(wtxmgrBucketKey) | ||
| c := ns.NestedReadBucket(bucketBlocks).ReadCursor() | ||
| defer c.Close() | ||
| for k, v := c.First(); k != nil; k, v = c.Next() { | ||
| for k, v := c.Seek(keyBlockRecord(fromHeight)); k != nil; k, v = c.Next() { | ||
| hash := extractRawBlockRecordHash(v) | ||
| _, _, err := fetchRawCFilter2(ns, hash) | ||
| if errors.Is(err, errors.NotExist) { | ||
| height := int32(byteOrder.Uint32(k)) | ||
| return height, nil | ||
| if err != nil { | ||
| if errors.Is(err, errors.NotExist) { | ||
| height := int32(byteOrder.Uint32(k)) | ||
| return height, nil | ||
| } | ||
| return 0, errors.E(errors.IO, err) | ||
| } | ||
| } | ||
| return 0, errors.E(errors.NotExist) | ||
|
|
@@ -442,42 +474,37 @@ func (s *Store) InsertMissingCFilters(dbtx walletdb.ReadWriteTx, blockHashes []* | |
| } | ||
|
|
||
| for i, blockHash := range blockHashes { | ||
| // Ensure that blockHashes are ordered and that all previous cfilters in the | ||
| // main chain are known. | ||
| // Ensure that blockHashes are ordered. | ||
| header := existsBlockHeader(ns, blockHash[:]) | ||
| if header == nil { | ||
| return errors.E(errors.NotExist, errors.Errorf("missing header for block %v", blockHash)) | ||
| } | ||
| ok := i == 0 && *blockHash == s.chainParams.GenesisHash | ||
| var bcf2Key [gcs2.KeySize]byte | ||
| if !ok { | ||
| header := existsBlockHeader(ns, blockHash[:]) | ||
| if header == nil { | ||
| return errors.E(errors.NotExist, errors.Errorf("missing header for block %v", blockHash)) | ||
| } | ||
| parentHash := extractBlockHeaderParentHash(header) | ||
| merkleRoot := extractBlockHeaderMerkleRoot(header) | ||
| merkleRootHash, err := chainhash.NewHash(merkleRoot) | ||
| if err != nil { | ||
| return errors.E(errors.Invalid, errors.Errorf("invalid stored header %v", blockHash)) | ||
| } | ||
| bcf2Key = blockcf2.Key(merkleRootHash) | ||
| if i == 0 { | ||
| _, _, err := fetchRawCFilter2(ns, parentHash) | ||
| ok = err == nil | ||
| } else { | ||
| ok = bytes.Equal(parentHash, blockHashes[i-1][:]) | ||
| if i != 0 { | ||
| if !bytes.Equal(parentHash, blockHashes[i-1][:]) { | ||
| return errors.E(errors.Invalid, "block hashes are not ordered") | ||
| } | ||
| } | ||
| } | ||
| if !ok { | ||
| return errors.E(errors.Invalid, "block hashes are not ordered or previous cfilters are missing") | ||
| } | ||
|
|
||
| // Record cfilter for this block | ||
| err := putRawCFilter(ns, blockHash[:], valueRawCFilter2(bcf2Key, filters[i].Bytes())) | ||
| merkleRoot := extractBlockHeaderMerkleRoot(header) | ||
| merkleRootHash, err := chainhash.NewHash(merkleRoot) | ||
| if err != nil { | ||
| return errors.E(errors.Invalid, errors.Errorf("invalid stored header %v", blockHash)) | ||
| } | ||
| bcf2Key := blockcf2.Key(merkleRootHash) | ||
| err = putRawCFilter(ns, blockHash[:], valueRawCFilter2(bcf2Key, filters[i].Bytes())) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| // Mark all main chain cfilters as saved if the last block hash is the main | ||
| // chain tip. | ||
| // chain tip. Even if this is not the head block, all cfilters may be saved | ||
| // at this point. The caller may need to check and set rootHaveCFilters. | ||
| tip, _ := s.MainChainTip(dbtx) | ||
| if bytes.Equal(tip[:], blockHashes[len(blockHashes)-1][:]) { | ||
| err := ns.Put(rootHaveCFilters, []byte{1}) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.