Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 276
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
Uh oh!
There was an error while loading. Please reload this page.
Block store #42
Changes from all commits
c8230acd93c7ba2f19fd68fbbc8ba785428d89a2e7ea091fbFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Uh oh!
There was an error while loading. Please reload this page.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| 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.
Uh oh!There was an error while loading. Please reload this page. | ||
| "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.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| 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? ContributorAuthor 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.
Uh oh!
There was an error while loading. Please reload this page.