neoneo-go/pkg/core/storage/helpers.go
Roman Khimov 4395cea344 storage: fix reading overlapping hash blocks from the DB
In the unlikely event of overlapping hash block written to the DB we might end
up with wrong hash list. That happened to me for some reason when synching
with the testnet leading to the following keys with respective values:
  150000 -> 2000 hashes
  152000 -> 2000 hashes
  153999 -> 2000 hashes

Reading it hashes number 153999 and 154000 got the same values and the chain
couldn't sync correctly.
2019-09-11 20:33:41 +03:00

95 lines
2.5 KiB
Go

package storage
import (
"bytes"
"encoding/binary"
"sort"
"github.com/CityOfZion/neo-go/pkg/util"
)
// Version will attempt to get the current version stored in the
// underlying Store.
func Version(s Store) (string, error) {
version, err := s.Get(SYSVersion.Bytes())
return string(version), err
}
// PutVersion will store the given version in the underlying Store.
func PutVersion(s Store, v string) error {
return s.Put(SYSVersion.Bytes(), []byte(v))
}
// CurrentBlockHeight returns the current block height found in the
// underlying Store.
func CurrentBlockHeight(s Store) (uint32, error) {
b, err := s.Get(SYSCurrentBlock.Bytes())
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint32(b[32:36]), nil
}
// CurrentHeaderHeight returns the current header height and hash from
// the underlying Store.
func CurrentHeaderHeight(s Store) (i uint32, h util.Uint256, err error) {
var b []byte
b, err = s.Get(SYSCurrentHeader.Bytes())
if err != nil {
return
}
i = binary.LittleEndian.Uint32(b[32:36])
h, err = util.Uint256DecodeReverseBytes(b[:32])
return
}
// uint32Slice attaches the methods of Interface to []int, sorting in increasing order.
type uint32Slice []uint32
func (p uint32Slice) Len() int { return len(p) }
func (p uint32Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p uint32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// HeaderHashes returns a sorted list of header hashes retrieved from
// the given underlying Store.
func HeaderHashes(s Store) ([]util.Uint256, error) {
hashMap := make(map[uint32][]util.Uint256)
s.Seek(IXHeaderHashList.Bytes(), func(k, v []byte) {
storedCount := binary.LittleEndian.Uint32(k[1:])
hashes, err := read2000Uint256Hashes(v)
if err != nil {
panic(err)
}
hashMap[storedCount] = hashes
})
var (
hashes = make([]util.Uint256, 0, len(hashMap))
sortedKeys = make([]uint32, 0, len(hashMap))
)
for k := range hashMap {
sortedKeys = append(sortedKeys, k)
}
sort.Sort(uint32Slice(sortedKeys))
for _, key := range sortedKeys {
hashes = append(hashes[:key], hashMap[key]...)
}
return hashes, nil
}
// read2000Uint256Hashes attempts to read 2000 Uint256 hashes from
// the given byte array.
func read2000Uint256Hashes(b []byte) ([]util.Uint256, error) {
r := bytes.NewReader(b)
br := util.BinReader{R: r}
lenHashes := br.ReadVarUint()
hashes := make([]util.Uint256, lenHashes)
br.ReadLE(hashes)
if br.Err != nil {
return nil, br.Err
}
return hashes, nil
}