-
Notifications
You must be signed in to change notification settings - Fork 253
Block store #42
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
Block store #42
Changes from all commits
c8230ac
d93c7ba
2f19fd6
8fbbc8b
a785428
d89a2e7
ea091fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package store | ||
|
|
||
| import "github.com/dgraph-io/badger/v3" | ||
|
|
||
| var _ KVStore = &BadgerKV{} | ||
|
|
||
| type BadgerKV struct { | ||
| db *badger.DB | ||
| } | ||
|
|
||
| func (b *BadgerKV) Get(key []byte) ([]byte, error) { | ||
| txn := b.db.NewTransaction(false) | ||
| defer txn.Discard() | ||
| item, err := txn.Get(key) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return item.ValueCopy(nil) | ||
| } | ||
|
|
||
| func (b *BadgerKV) Set(key []byte, value []byte) error { | ||
| txn := b.db.NewTransaction(true) | ||
| err := txn.Set(key, value) | ||
| if err != nil { | ||
| txn.Discard() | ||
| return err | ||
| } | ||
| return txn.Commit() | ||
| } | ||
|
|
||
| func (b *BadgerKV) Delete(key []byte) error { | ||
| txn := b.db.NewTransaction(true) | ||
| err := txn.Delete(key) | ||
| if err != nil { | ||
| txn.Discard() | ||
| return err | ||
| } | ||
| return txn.Commit() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| package store | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "encoding/binary" | ||
| "encoding/gob" | ||
liamsi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "sync" | ||
|
|
||
| "github.com/lazyledger/optimint/types" | ||
| "github.com/minio/sha256-simd" | ||
| "go.uber.org/multierr" | ||
| ) | ||
|
|
||
| var ( | ||
| blockPrefix = [1]byte{1} | ||
| indexPrefix = [1]byte{2} | ||
| ) | ||
|
|
||
| type DefaultBlockStore struct { | ||
| db KVStore | ||
|
|
||
| height uint64 | ||
|
|
||
| // mtx protects height | ||
| mtx sync.RWMutex | ||
liamsi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| var _ BlockStore = &DefaultBlockStore{} | ||
|
|
||
| func NewBlockStore() BlockStore { | ||
| return &DefaultBlockStore{db: NewInMemoryKVStore()} | ||
| } | ||
|
|
||
| func (bs *DefaultBlockStore) Height() uint64 { | ||
| bs.mtx.RLock() | ||
| defer bs.mtx.RUnlock() | ||
| return bs.height | ||
| } | ||
|
|
||
| func (bs *DefaultBlockStore) SaveBlock(block *types.Block) error { | ||
| // TODO(tzdybal): proper serialization & hashing | ||
|
Contributor
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. Why not make it proper already?
Contributor
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. Goal of this PR is to introduce block store that works. Using sha256-simd actually makes sense as it's drop-in replacement. By this comment I mostly mean that the serialization and hashing code will definitely be modified in the future. |
||
| hash, err := getHash(block) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| key := append(blockPrefix[:], hash[:]...) | ||
|
|
||
| height := make([]byte, 8) | ||
| binary.LittleEndian.PutUint64(height, block.Header.Height) | ||
| ikey := append(indexPrefix[:], height[:]...) | ||
|
|
||
| var value bytes.Buffer | ||
| enc := gob.NewEncoder(&value) | ||
| err = enc.Encode(block) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| bs.mtx.Lock() | ||
| defer bs.mtx.Unlock() | ||
| // TODO(tzdybal): use transaction for consistency of DB | ||
| err = multierr.Append(err, bs.db.Set(key, value.Bytes())) | ||
| err = multierr.Append(err, bs.db.Set(ikey, hash[:])) | ||
|
|
||
| if block.Header.Height > bs.height { | ||
| bs.height = block.Header.Height | ||
| } | ||
|
|
||
| return err | ||
| } | ||
|
|
||
| // TODO(tzdybal): what is more common access pattern? by height or by hash? | ||
| // currently, we're indexing height->hash, and store blocks by hash, but we might as well store by height | ||
| // and index hash->height | ||
|
Comment on lines
+72
to
+74
Contributor
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 we need both. In optimint you can dictate the access pattern, on the app-side (e.g. via RPC), you can't really know (but you could look into some typical cosmos "apps"). |
||
| func (bs *DefaultBlockStore) LoadBlock(height uint64) (*types.Block, error) { | ||
| buf := make([]byte, 8) | ||
| binary.LittleEndian.PutUint64(buf, height) | ||
| ikey := append(indexPrefix[:], buf[:]...) | ||
|
|
||
| hash, err := bs.db.Get(ikey) | ||
|
|
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // TODO(tzdybal): any better way to convert slice to array? | ||
| var h [32]byte | ||
| copy(h[:], hash) | ||
|
Comment on lines
+86
to
+88
Contributor
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 that is the most common way. You could add a helper method (or methods) if you have to convert back and forth. |
||
| return bs.LoadBlockByHash(h) | ||
| } | ||
|
|
||
| func (bs *DefaultBlockStore) LoadBlockByHash(hash [32]byte) (*types.Block, error) { | ||
| key := append(blockPrefix[:], hash[:]...) | ||
|
|
||
| blockData, err := bs.db.Get(key) | ||
|
|
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| dec := gob.NewDecoder(bytes.NewReader(blockData)) | ||
| var block types.Block | ||
| err = dec.Decode(&block) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return &block, nil | ||
| } | ||
|
|
||
| // TODO(tzdybal): replace with proper hashing mechanism | ||
|
Contributor
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 would be good to decide on all cryptographic primitives in ADR I think. |
||
| func getHash(block *types.Block) ([32]byte, error) { | ||
| var header bytes.Buffer | ||
| enc := gob.NewEncoder(&header) | ||
| err := enc.Encode(block.Header) | ||
| if err != nil { | ||
| return [32]byte{}, err | ||
| } | ||
|
|
||
| return sha256.Sum256(header.Bytes()), nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.