2021-12-04 16:42:11 +00:00

206 lines
5.2 KiB
Go

package graviton
import "io"
import "bytes"
import "fmt"
import "encoding/binary"
import "golang.org/x/xerrors"
var gET_CHECKED bool = true // all gets go through value checks
type leaf struct {
findex, fpos uint32
key []byte
keybuf [MINBLOCK]byte
keyhash [HASHSIZE]byte
hash_check [HASHSIZE]byte // used to verify check
hash [HASHSIZE]byte
leaf_init bool
value []byte
dirty bool
//dirtyhash bool
loaded_partial bool
}
func newLeaf(keyhash [HASHSIZE]byte, key, value []byte) *leaf {
key_copy := make([]byte, len(key))
copy(key_copy, key[:])
value_copy := make([]byte, len(value))
copy(value_copy, value[:])
l := &leaf{
dirty: true, // new leaf is by default dirty
keyhash: keyhash,
value: value_copy,
}
l.key = append(l.keybuf[:0], key...)
rst := sum(l.value)
copy(l.hash_check[:], leafHash(l.keyhash[:], rst[:]))
copy(l.hash[:], l.hash_check[:])
l.leaf_init = true
//fmt.Printf("leafhash new %x key '%s' value '%s'\n", l.hash_check, string(key), string(value))
return l
}
func leafHash(hkey, hvalue []byte) []byte {
rst := make([]byte, 0, HASHSIZE)
h := hasher()
h.Write([]byte{leafNODE})
h.Write(hkey)
h.Write(hvalue)
rst = h.Sum(rst)
return rst
}
func (l *leaf) Hash(store *Store) ([]byte, error) {
if l.loaded_partial { // if leaf is loaded partially, load it fully now
if err := l.loadfullleaffromstore(store); err != nil {
return nil, err
}
}
return l.hash[:], nil
}
func (l *leaf) isDirty() bool {
return l.dirty
}
func (l *leaf) Position() (uint32, uint32) {
return l.findex, l.fpos
}
// this always assummes that keyhash already matches to new keyhash
// this function is only used once , in node_inner.go insert
func (l *leaf) Put(store *Store, keyhash [HASHSIZE]byte, value []byte) error {
if l.loaded_partial { // if leaf is loaded partially, load it fully now
if err := l.loadfullleaffromstore(store); err != nil {
return err
}
}
// overwrite created new branch. Old versions are all accessible using previous root
l.value = value
rst := sum(l.value)
copy(l.hash[:], leafHash(l.keyhash[:], rst[:])) // use hash of key and hash of value
copy(l.hash_check[:], l.hash[:])
l.dirty = true
l.findex, l.fpos = 0, 0
return nil
}
// should we return a copy
func (l *leaf) Get(store *Store, keyhash [HASHSIZE]byte) ([]byte, error) {
if l.loaded_partial { // if leaf is loaded partially, load it fully now
if err := l.loadfullleaffromstore(store); err != nil {
return nil, err
}
}
if l.keyhash == keyhash {
return l.value, nil
}
return nil, xerrors.Errorf("%w: collision, keyhash %x not found, keyhash in ram %x", ErrNotFound, keyhash,l.keyhash)
}
func (l *leaf) Delete(store *Store, keyhash [HASHSIZE]byte) (bool, bool, error) {
if l.loaded_partial { // if leaf is loaded partially, load it fully now
if err := l.loadfullleaffromstore(store); err != nil {
return false, false, err
}
}
match := l.keyhash == keyhash
return match, match, nil
}
func (l *leaf) load_partial(store *Store) error {
if l.loaded_partial { // if leaf is loaded partially, load it fully now
return l.loadfullleaffromstore(store)
}
return nil
}
func (l *leaf) loadfullleaffromstore(store *Store) error { // loading leaf from store
//fmt.Printf("loading leaf findex %d fpos %d\n", l.findex, l.fpos)
if l.findex <= 0 && l.fpos <= 0 {
return xerrors.Errorf("Invalid findex %d fpos %d", l.findex, l.fpos)
}
var buf_array [4 * MINBLOCK]byte
buf := buf_array[:]
read_again:
_, err := store.read(l.findex, l.fpos, buf[:]) // atleast keylen, key, valuelen will be available in this read, if value is small,it's also available
if err != nil && err != io.EOF {
return err
}
var done int
l.key = l.keybuf[:0]
l.value = l.value[:0]
if value, bytecount := binary.Uvarint(buf[:]); bytecount > 0 {
l.key = append(l.keybuf[:0], buf[bytecount:uint64(bytecount)+value]...)
done += bytecount + int(value)
} else {
return xerrors.Errorf("invalid key size")
}
if value, bytecount := binary.Uvarint(buf[done:]); bytecount > 0 {
if done+bytecount+int(value) > len(buf) {
buf = make([]byte, done+bytecount+int(value), done+bytecount+int(value))
goto read_again
}
//fmt.Printf("value %d bytecount %d done %d len(buf) %d\n", value,bytecount, done, len(buf))
l.value = append(l.value, buf[done+bytecount:done+bytecount+int(value)]...)
done += bytecount + int(value)
} else {
return xerrors.Errorf("invalid value size")
}
// time for data integrity
l.keyhash = sum(l.key)
// we also need to calculate hash, see whether it matches with what is stored
rst := sum(l.value)
copy(l.hash[:], leafHash(l.keyhash[:], rst[:])) // use hash of key and hash of value
if gET_CHECKED {
if bytes.Compare(l.hash_check[:], l.hash[:]) != 0 {
//fmt.Printf("hash_check %x hash %x keyhash %x\n", l.hash_check, l.hash, l.keyhash)
return fmt.Errorf("Key/Value data Corruption, key '%x'", l.key)
}
}
l.loaded_partial = false
return nil
}
func (l *leaf) Prove(store *Store, keyhash [HASHSIZE]byte, proof *Proof) error {
if l.loaded_partial { // if leaf is loaded partially, load it fully now
if err := l.loadfullleaffromstore(store); err != nil {
return err
}
}
if l.keyhash == keyhash {
proof.addValue(l.value)
return nil
}
rst := sum(l.value)
proof.addCollision(l.keyhash[:], rst[:])
return nil
}