mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-16 16:38:07 +00:00
trie -> kv store
* simplify data storage to key-value, tries are not relevant for NBC * locked-down version of lmdb dependency * easier to build / maintain on various platforms
This commit is contained in:
parent
3dcdce137a
commit
2a67ac3c05
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -136,3 +136,6 @@
|
|||||||
url = https://github.com/status-im/nim-bearssl.git
|
url = https://github.com/status-im/nim-bearssl.git
|
||||||
ignore = dirty
|
ignore = dirty
|
||||||
branch = master
|
branch = master
|
||||||
|
[submodule "vendor/lmdb"]
|
||||||
|
path = vendor/lmdb
|
||||||
|
url = https://github.com/status-im/lmdb.git
|
||||||
|
@ -7,7 +7,6 @@ cache:
|
|||||||
directories:
|
directories:
|
||||||
- vendor/nimbus-build-system/vendor/Nim/bin
|
- vendor/nimbus-build-system/vendor/Nim/bin
|
||||||
- vendor/go/bin
|
- vendor/go/bin
|
||||||
- rocksdbCache
|
|
||||||
- jsonTestsCache
|
- jsonTestsCache
|
||||||
|
|
||||||
git:
|
git:
|
||||||
@ -27,7 +26,6 @@ matrix:
|
|||||||
before_install:
|
before_install:
|
||||||
- export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
|
- export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
|
||||||
- sudo apt-get -q update
|
- sudo apt-get -q update
|
||||||
- sudo apt-get install -y librocksdb-dev
|
|
||||||
- os: linux
|
- os: linux
|
||||||
arch: arm64
|
arch: arm64
|
||||||
sudo: required
|
sudo: required
|
||||||
@ -37,14 +35,10 @@ matrix:
|
|||||||
before_install:
|
before_install:
|
||||||
- export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
|
- export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
|
||||||
- sudo apt-get -q update
|
- sudo apt-get -q update
|
||||||
- sudo apt-get install -y libpcre3-dev librocksdb-dev
|
- sudo apt-get install -y libpcre3-dev
|
||||||
- os: osx
|
- os: osx
|
||||||
env:
|
env:
|
||||||
- NPROC=2
|
- NPROC=2
|
||||||
before_install:
|
|
||||||
- launchctl setenv LIBRARY_PATH /usr/local/lib # for RocksDB
|
|
||||||
# build our own rocksdb to test with a fixed version that we think works
|
|
||||||
- vendor/nimbus-build-system/scripts/build_rocksdb.sh rocksdbCache
|
|
||||||
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
26
README.md
26
README.md
@ -52,7 +52,6 @@ Nimbus has 4 external dependencies:
|
|||||||
|
|
||||||
* Go 1.12 (for compiling libp2p daemon - being phased out)
|
* Go 1.12 (for compiling libp2p daemon - being phased out)
|
||||||
* Developer tools (C compiler, Make, Bash, Git)
|
* Developer tools (C compiler, Make, Bash, Git)
|
||||||
* [RocksDB](https://github.com/facebook/rocksdb/)
|
|
||||||
* PCRE
|
* PCRE
|
||||||
|
|
||||||
Nim is not an external dependency, Nimbus will build its own local copy.
|
Nim is not an external dependency, Nimbus will build its own local copy.
|
||||||
@ -62,13 +61,13 @@ Nim is not an external dependency, Nimbus will build its own local copy.
|
|||||||
On common Linux distributions the dependencies can be installed with:
|
On common Linux distributions the dependencies can be installed with:
|
||||||
```sh
|
```sh
|
||||||
# Debian and Ubuntu
|
# Debian and Ubuntu
|
||||||
sudo apt-get install build-essential git golang-go librocksdb-dev libpcre3-dev
|
sudo apt-get install build-essential git golang-go libpcre3-dev
|
||||||
|
|
||||||
# Fedora
|
# Fedora
|
||||||
dnf install @development-tools go rocksdb-devel pcre
|
dnf install @development-tools go pcre
|
||||||
|
|
||||||
# Archlinux, using an AUR manager for pcre-static
|
# Archlinux, using an AUR manager for pcre-static
|
||||||
yourAURmanager -S base-devel go rocksdb pcre-static
|
yourAURmanager -S base-devel go pcre-static
|
||||||
```
|
```
|
||||||
|
|
||||||
### MacOS
|
### MacOS
|
||||||
@ -76,14 +75,14 @@ yourAURmanager -S base-devel go rocksdb pcre-static
|
|||||||
Assuming you use [Homebrew](https://brew.sh/) to manage packages
|
Assuming you use [Homebrew](https://brew.sh/) to manage packages
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
brew install go rocksdb pcre
|
brew install go pcre
|
||||||
```
|
```
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
* install [Go](https://golang.org/doc/install#windows)
|
* install [Go](https://golang.org/doc/install#windows)
|
||||||
You can install the developer tools by following the instruction in our [Windows dev environment section](#windows-dev-environment).
|
You can install the developer tools by following the instruction in our [Windows dev environment section](#windows-dev-environment).
|
||||||
It also provides a downloading script for prebuilt PCRE and RocksDB.
|
It also provides a downloading script for prebuilt PCRE.
|
||||||
|
|
||||||
If you choose to install Go from source, both Go and Nimbus requires the same initial steps of installing Mingw.
|
If you choose to install Go from source, both Go and Nimbus requires the same initial steps of installing Mingw.
|
||||||
|
|
||||||
@ -220,7 +219,7 @@ Variables -> Path -> Edit -> New -> C:\mingw-w64\mingw64\bin (it's "C:\mingw-w64
|
|||||||
|
|
||||||
Install [Git for Windows](https://gitforwindows.org/) and use a "Git Bash" shell to clone and build nim-beacon-chain.
|
Install [Git for Windows](https://gitforwindows.org/) and use a "Git Bash" shell to clone and build nim-beacon-chain.
|
||||||
|
|
||||||
If you don't want to compile RocksDB and SQLite separately, you can fetch pre-compiled DLLs with:
|
If you don't want to compile PCRE separately, you can fetch pre-compiled DLLs with:
|
||||||
```bash
|
```bash
|
||||||
mingw32-make # this first invocation will update the Git submodules
|
mingw32-make # this first invocation will update the Git submodules
|
||||||
mingw32-make fetch-dlls # this will place the right DLLs for your architecture in the "build/" directory
|
mingw32-make fetch-dlls # this will place the right DLLs for your architecture in the "build/" directory
|
||||||
@ -286,19 +285,6 @@ sudo apt-get install git libgflags-dev libsnappy-dev libpcre3-dev
|
|||||||
mkdir status
|
mkdir status
|
||||||
cd status
|
cd status
|
||||||
|
|
||||||
# Install rocksdb
|
|
||||||
git clone https://github.com/facebook/rocksdb.git
|
|
||||||
cd rocksdb
|
|
||||||
make shared_lib
|
|
||||||
sudo make install-shared
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# Raspberry pi doesn't include /usr/local/lib in library search path
|
|
||||||
# Add it to your profile
|
|
||||||
echo '# Local compiles (nimbus - rocksdb)' >> ~/.profile
|
|
||||||
echo 'export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH' >> ~/.profile
|
|
||||||
echo '' >> ~/.profile
|
|
||||||
|
|
||||||
# Install Go at least 1.12 (Buster only includes up to 1.11)
|
# Install Go at least 1.12 (Buster only includes up to 1.11)
|
||||||
# Raspbian is 32-bit, so the package is go1.XX.X.linux-armv6l.tar.gz (and not arm64)
|
# Raspbian is 32-bit, so the package is go1.XX.X.linux-armv6l.tar.gz (and not arm64)
|
||||||
curl -O https://storage.googleapis.com/golang/go1.13.3.linux-armv6l.tar.gz
|
curl -O https://storage.googleapis.com/golang/go1.13.3.linux-armv6l.tar.gz
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import
|
import
|
||||||
json, tables, options,
|
options,
|
||||||
chronicles, serialization, json_serialization, eth/common/eth_types_json_serialization,
|
serialization,
|
||||||
spec/[datatypes, digest, crypto],
|
spec/[datatypes, digest, crypto],
|
||||||
eth/trie/db, ssz
|
kvstore, ssz
|
||||||
|
|
||||||
type
|
type
|
||||||
BeaconChainDB* = ref object
|
BeaconChainDB* = ref object
|
||||||
## Database storing resolved blocks and states - resolved blocks are such
|
## Database storing resolved blocks and states - resolved blocks are such
|
||||||
## blocks that form a chain back to the tail block.
|
## blocks that form a chain back to the tail block.
|
||||||
backend: TrieDatabaseRef
|
backend: KVStoreRef
|
||||||
|
|
||||||
DbKeyKind = enum
|
DbKeyKind = enum
|
||||||
kHashToState
|
kHashToState
|
||||||
@ -61,7 +61,7 @@ func subkey(root: Eth2Digest, slot: Slot): auto =
|
|||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
proc init*(T: type BeaconChainDB, backend: TrieDatabaseRef): BeaconChainDB =
|
proc init*(T: type BeaconChainDB, backend: KVStoreRef): BeaconChainDB =
|
||||||
T(backend: backend)
|
T(backend: backend)
|
||||||
|
|
||||||
proc putBlock*(db: BeaconChainDB, key: Eth2Digest, value: SignedBeaconBlock) =
|
proc putBlock*(db: BeaconChainDB, key: Eth2Digest, value: SignedBeaconBlock) =
|
||||||
@ -99,22 +99,18 @@ proc putTailBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
|||||||
db.backend.put(subkey(kTailBlock), key.data)
|
db.backend.put(subkey(kTailBlock), key.data)
|
||||||
|
|
||||||
proc get(db: BeaconChainDB, key: auto, T: typedesc): Option[T] =
|
proc get(db: BeaconChainDB, key: auto, T: typedesc): Option[T] =
|
||||||
let res = db.backend.get(key)
|
var res: Option[T]
|
||||||
if res.len != 0:
|
discard db.backend.get(key, proc (data: openArray[byte]) =
|
||||||
try:
|
try:
|
||||||
some(SSZ.decode(res, T))
|
res = some(SSZ.decode(data, T))
|
||||||
except SerializationError:
|
except SerializationError:
|
||||||
none(T)
|
discard
|
||||||
else:
|
)
|
||||||
none(T)
|
res
|
||||||
|
|
||||||
proc getBlock*(db: BeaconChainDB, key: Eth2Digest): Option[SignedBeaconBlock] =
|
proc getBlock*(db: BeaconChainDB, key: Eth2Digest): Option[SignedBeaconBlock] =
|
||||||
db.get(subkey(SignedBeaconBlock, key), SignedBeaconBlock)
|
db.get(subkey(SignedBeaconBlock, key), SignedBeaconBlock)
|
||||||
|
|
||||||
proc getBlock*(db: BeaconChainDB, slot: Slot): Option[SignedBeaconBlock] =
|
|
||||||
# TODO implement this
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getState*(db: BeaconChainDB, key: Eth2Digest): Option[BeaconState] =
|
proc getState*(db: BeaconChainDB, key: Eth2Digest): Option[BeaconState] =
|
||||||
db.get(subkey(BeaconState, key), BeaconState)
|
db.get(subkey(BeaconState, key), BeaconState)
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@ import
|
|||||||
os, net, tables, random, strutils, times, sequtils,
|
os, net, tables, random, strutils, times, sequtils,
|
||||||
|
|
||||||
# Nimble packages
|
# Nimble packages
|
||||||
stew/[objects, bitseqs, byteutils], stew/ranges/ptr_arith,
|
stew/[objects, bitseqs, byteutils],
|
||||||
chronos, chronicles, confutils, metrics,
|
chronos, chronicles, confutils, metrics,
|
||||||
json_serialization/std/[options, sets], serialization/errors,
|
json_serialization/std/[options, sets], serialization/errors,
|
||||||
eth/trie/db, eth/trie/backends/rocksdb_backend, eth/async_utils,
|
kvstore, kvstore_lmdb, eth/async_utils,
|
||||||
|
|
||||||
# Local modules
|
# Local modules
|
||||||
spec/[datatypes, digest, crypto, beaconstate, helpers, validator, network],
|
spec/[datatypes, digest, crypto, beaconstate, helpers, validator, network],
|
||||||
@ -135,7 +135,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
|||||||
networkId = getPersistentNetIdentity(conf)
|
networkId = getPersistentNetIdentity(conf)
|
||||||
nickname = if conf.nodeName == "auto": shortForm(networkId)
|
nickname = if conf.nodeName == "auto": shortForm(networkId)
|
||||||
else: conf.nodeName
|
else: conf.nodeName
|
||||||
db = BeaconChainDB.init(trieDB newChainDb(conf.databaseDir))
|
db = BeaconChainDB.init(kvStore LmdbStoreRef.init(conf.databaseDir))
|
||||||
|
|
||||||
var mainchainMonitor: MainchainMonitor
|
var mainchainMonitor: MainchainMonitor
|
||||||
|
|
||||||
|
94
beacon_chain/kvstore.nim
Normal file
94
beacon_chain/kvstore.nim
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Simple Key-Value store database interface
|
||||||
|
|
||||||
|
import
|
||||||
|
tables, hashes, sets
|
||||||
|
|
||||||
|
type
|
||||||
|
MemoryStoreRef* = ref object of RootObj
|
||||||
|
records: Table[seq[byte], seq[byte]]
|
||||||
|
|
||||||
|
DataProc* = proc(val: openArray[byte])
|
||||||
|
PutProc = proc (db: RootRef, key, val: openArray[byte]) {.gcsafe.}
|
||||||
|
GetProc = proc (db: RootRef, key: openArray[byte], onData: DataProc): bool {.gcsafe.}
|
||||||
|
DelProc = proc (db: RootRef, key: openArray[byte]) {.gcsafe.}
|
||||||
|
ContainsProc = proc (db: RootRef, key: openArray[byte]): bool {.gcsafe.}
|
||||||
|
|
||||||
|
KVStoreRef* = ref object
|
||||||
|
## Key-Value store virtual interface
|
||||||
|
obj: RootRef
|
||||||
|
putProc: PutProc
|
||||||
|
getProc: GetProc
|
||||||
|
delProc: DelProc
|
||||||
|
containsProc: ContainsProc
|
||||||
|
|
||||||
|
template put*(db: KVStoreRef, key, val: openArray[byte]) =
|
||||||
|
## Store ``value`` at ``key`` - overwrites existing value if already present
|
||||||
|
db.putProc(db.obj, key, val)
|
||||||
|
|
||||||
|
template get*(db: KVStoreRef, key: openArray[byte], onData: untyped): bool =
|
||||||
|
## Retrive value at ``key`` and call ``onData`` with the value. The data is
|
||||||
|
## valid for the duration of the callback.
|
||||||
|
## ``onData``: ``proc(data: openArray[byte])``
|
||||||
|
## returns true if found and false otherwise.
|
||||||
|
db.getProc(db.obj, key, onData)
|
||||||
|
|
||||||
|
template del*(db: KVStoreRef, key: openArray[byte]) =
|
||||||
|
## Remove value at ``key`` from store - do nothing if the value is not present
|
||||||
|
db.delProc(db.obj, key)
|
||||||
|
|
||||||
|
template contains*(db: KVStoreRef, key: openArray[byte]): bool =
|
||||||
|
## Return true iff ``key`` has a value in store
|
||||||
|
db.containsProc(db.obj, key)
|
||||||
|
|
||||||
|
proc get*(db: MemoryStoreRef, key: openArray[byte], onData: DataProc): bool =
|
||||||
|
let key = @key
|
||||||
|
db.records.withValue(key, v):
|
||||||
|
onData(v[])
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc del*(db: MemoryStoreRef, key: openArray[byte]) =
|
||||||
|
# TODO: This is quite inefficient and it won't be necessary once
|
||||||
|
# https://github.com/nim-lang/Nim/issues/7457 is developed.
|
||||||
|
let key = @key
|
||||||
|
db.records.del(key)
|
||||||
|
|
||||||
|
proc contains*(db: MemoryStoreRef, key: openArray[byte]): bool =
|
||||||
|
db.records.contains(@key)
|
||||||
|
|
||||||
|
proc put*(db: MemoryStoreRef, key, val: openArray[byte]) =
|
||||||
|
# TODO: This is quite inefficient and it won't be necessary once
|
||||||
|
# https://github.com/nim-lang/Nim/issues/7457 is developed.
|
||||||
|
let key = @key
|
||||||
|
db.records[key] = @val
|
||||||
|
|
||||||
|
proc init*(T: type MemoryStoreRef): T =
|
||||||
|
T(
|
||||||
|
records: initTable[seq[byte], seq[byte]]()
|
||||||
|
)
|
||||||
|
|
||||||
|
proc putImpl[T](db: RootRef, key, val: openArray[byte]) =
|
||||||
|
mixin put
|
||||||
|
put(T(db), key, val)
|
||||||
|
|
||||||
|
proc getImpl[T](db: RootRef, key: openArray[byte], onData: DataProc): bool =
|
||||||
|
mixin get
|
||||||
|
get(T(db), key, onData)
|
||||||
|
|
||||||
|
proc delImpl[T](db: RootRef, key: openArray[byte]) =
|
||||||
|
mixin del
|
||||||
|
del(T(db), key)
|
||||||
|
|
||||||
|
proc containsImpl[T](db: RootRef, key: openArray[byte]): bool =
|
||||||
|
mixin contains
|
||||||
|
contains(T(db), key)
|
||||||
|
|
||||||
|
func kvStore*[T: RootRef](x: T): KVStoreRef =
|
||||||
|
mixin del, get, put, contains
|
||||||
|
|
||||||
|
KVStoreRef(
|
||||||
|
obj: x,
|
||||||
|
putProc: putImpl[T],
|
||||||
|
getProc: getImpl[T],
|
||||||
|
delProc: delImpl[T],
|
||||||
|
containsProc: containsImpl[T]
|
||||||
|
)
|
159
beacon_chain/kvstore_lmdb.nim
Normal file
159
beacon_chain/kvstore_lmdb.nim
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
## Implementation of KVStore based on LMDB
|
||||||
|
## TODO: crashes on win32, investigate
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import ./kvstore
|
||||||
|
|
||||||
|
{.compile: "../vendor/lmdb/libraries/liblmdb/mdb.c".}
|
||||||
|
{.compile: "../vendor/lmdb/libraries/liblmdb/midl.c".}
|
||||||
|
|
||||||
|
const
|
||||||
|
MDB_NOSUBDIR = 0x4000
|
||||||
|
MDB_RDONLY = 0x20000
|
||||||
|
MDB_NOTFOUND = -30798
|
||||||
|
|
||||||
|
when defined(cpu64):
|
||||||
|
const LMDB_MAP_SIZE = 1024'u64 * 1024'u64 * 1024'u64 * 10'u64 # 10TB enough?
|
||||||
|
else:
|
||||||
|
const LMDB_MAP_SIZE = 1024'u64 * 1024'u64 * 1024'u64 # 32bit limitation
|
||||||
|
|
||||||
|
type
|
||||||
|
MDB_Env = distinct pointer
|
||||||
|
MDB_Txn = distinct pointer
|
||||||
|
MDB_Dbi = distinct cuint
|
||||||
|
|
||||||
|
MDB_val = object
|
||||||
|
mv_size: csize
|
||||||
|
mv_data: pointer
|
||||||
|
|
||||||
|
LmdbError* = object of CatchableError
|
||||||
|
|
||||||
|
# Used subset of the full LMDB API
|
||||||
|
proc mdb_env_create(env: var MDB_Env): cint {.importc.}
|
||||||
|
proc mdb_env_open(env: MDB_Env, path: cstring, flags: cuint, mode: cint): cint {.importc.}
|
||||||
|
proc mdb_txn_begin(env: MDB_Env, parent: MDB_Txn, flags: cuint, txn: var MDB_Txn): cint {.importc.}
|
||||||
|
proc mdb_txn_commit(txn: MDB_Txn): cint {.importc.}
|
||||||
|
proc mdb_txn_abort(txn: MDB_Txn) {.importc.}
|
||||||
|
proc mdb_dbi_open(txn: MDB_Txn, name: cstring, flags: cuint, dbi: var MDB_Dbi): cint {.importc.}
|
||||||
|
proc mdb_env_close(env: MDB_Env) {.importc.}
|
||||||
|
proc mdb_strerror(err: cint): cstring {.importc.}
|
||||||
|
|
||||||
|
proc mdb_get(txn: MDB_Txn, dbi: MDB_Dbi, key: var MDB_val, data: var MDB_val): cint {.importc.}
|
||||||
|
proc mdb_del(txn: MDB_Txn, dbi: MDB_Dbi, key: var MDB_val, data: ptr MDB_val): cint {.importc.}
|
||||||
|
proc mdb_put(txn: MDB_Txn, dbi: MDB_Dbi, key: var MDB_val, data: var MDB_val, flags: cuint): cint {.importc.}
|
||||||
|
|
||||||
|
proc mdb_env_set_mapsize(env: MDB_Env, size: uint64): cint {.importc.}
|
||||||
|
|
||||||
|
func raiseLmdbError(err: cint) {.noreturn.} =
|
||||||
|
let tmp = mdb_strerror(err)
|
||||||
|
raise (ref LmdbError)(msg: $tmp)
|
||||||
|
|
||||||
|
type
|
||||||
|
LmdbStoreRef* = ref object of RootObj
|
||||||
|
env: MDB_Env
|
||||||
|
|
||||||
|
template init(T: type MDB_Val, val: openArray[byte]): T =
|
||||||
|
T(
|
||||||
|
mv_size: val.len,
|
||||||
|
mv_data: unsafeAddr val[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
proc begin(db: LmdbStoreRef, flags: cuint): tuple[txn: MDB_Txn, dbi: MDB_Dbi] =
|
||||||
|
var
|
||||||
|
txn: MDB_Txn
|
||||||
|
dbi: MDB_Dbi
|
||||||
|
|
||||||
|
if (let x = mdb_txn_begin(db.env, nil, flags, txn); x != 0):
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
if (let x = mdb_dbi_open(txn, nil, 0, dbi); x != 0):
|
||||||
|
mdb_txn_abort(txn)
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
(txn, dbi)
|
||||||
|
|
||||||
|
proc get*(db: LmdbStoreRef, key: openarray[byte], onData: DataProc): bool =
|
||||||
|
if key.len == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
var
|
||||||
|
(txn, dbi) = db.begin(MDB_RDONLY)
|
||||||
|
dbKey = MDB_Val.init(key)
|
||||||
|
dbVal: MDB_val
|
||||||
|
|
||||||
|
# abort ok for read-only and easier for exception safety
|
||||||
|
defer: mdb_txn_abort(txn)
|
||||||
|
|
||||||
|
if (let x = mdb_get(txn, dbi, dbKey, dbVal); x != 0):
|
||||||
|
if x == MDB_NOTFOUND:
|
||||||
|
return false
|
||||||
|
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
if not onData.isNil:
|
||||||
|
onData(toOpenArrayByte(cast[cstring](dbVal.mv_data), 0, dbVal.mv_size.int - 1))
|
||||||
|
|
||||||
|
true
|
||||||
|
|
||||||
|
proc put*(db: LmdbStoreRef, key, value: openarray[byte]) =
|
||||||
|
if key.len == 0: return
|
||||||
|
|
||||||
|
var
|
||||||
|
(txn, dbi) = db.begin(0)
|
||||||
|
dbKey = MDB_Val.init(key)
|
||||||
|
dbVal = MDB_Val.init(value)
|
||||||
|
|
||||||
|
if (let x = mdb_put(txn, dbi, dbKey, dbVal, 0); x != 0):
|
||||||
|
mdb_txn_abort(txn)
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
if (let x = mdb_txn_commit(txn); x != 0):
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
proc contains*(db: LmdbStoreRef, key: openarray[byte]): bool =
|
||||||
|
db.get(key, nil)
|
||||||
|
|
||||||
|
proc del*(db: LmdbStoreRef, key: openarray[byte]) =
|
||||||
|
if key.len == 0: return
|
||||||
|
|
||||||
|
var
|
||||||
|
(txn, dbi) = db.begin(0)
|
||||||
|
dbKey = MDB_Val.init(key)
|
||||||
|
|
||||||
|
if (let x = mdb_del(txn, dbi, dbKey, nil); x != 0):
|
||||||
|
mdb_txn_abort(txn)
|
||||||
|
|
||||||
|
if x != MDB_NOTFOUND:
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
if (let x = mdb_txn_commit(txn); x != 0):
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
proc close*(db: LmdbStoreRef) =
|
||||||
|
mdb_env_close(db.env)
|
||||||
|
|
||||||
|
proc init*(T: type LmdbStoreRef, basePath: string, readOnly = false): T =
|
||||||
|
var
|
||||||
|
env: MDB_Env
|
||||||
|
|
||||||
|
if (let x = mdb_env_create(env); x != 0):
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
let dataDir = basePath / "nimbus.lmdb"
|
||||||
|
|
||||||
|
if (let x = mdb_env_set_mapsize(env, LMDB_MAP_SIZE); x != 0):
|
||||||
|
mdb_env_close(env)
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
var openFlags = MDB_NOSUBDIR
|
||||||
|
if readOnly: openFlags = openFlags or MDB_RDONLY
|
||||||
|
|
||||||
|
# file mode ignored on windows
|
||||||
|
if (let x = mdb_env_open(env, dataDir, openFlags.cuint, 0o664.cint); x != 0):
|
||||||
|
mdb_env_close(env)
|
||||||
|
raiseLmdbError(x)
|
||||||
|
|
||||||
|
T(env: env)
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
macros, hashes, json, strutils, tables,
|
macros, hashes, json, strutils, tables,
|
||||||
stew/[byteutils, bitseqs], chronicles, eth/common,
|
stew/[byteutils, bitseqs], chronicles,
|
||||||
../version, ../ssz/types, ./crypto, ./digest
|
../version, ../ssz/types, ./crypto, ./digest
|
||||||
|
|
||||||
# TODO Data types:
|
# TODO Data types:
|
||||||
|
@ -21,11 +21,11 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
chronicles,
|
chronicles,
|
||||||
nimcrypto/[sha2, hash, utils], eth/common/eth_types_json_serialization,
|
nimcrypto/[sha2, hash, utils],
|
||||||
hashes
|
hashes
|
||||||
|
|
||||||
export
|
export
|
||||||
eth_types_json_serialization, hash.`$`
|
hash.`$`
|
||||||
|
|
||||||
type
|
type
|
||||||
Eth2Digest* = MDigest[32 * 8] ## `hash32` from spec
|
Eth2Digest* = MDigest[32 * 8] ## `hash32` from spec
|
||||||
|
@ -12,7 +12,7 @@ import
|
|||||||
endians, stew/shims/macros, options, algorithm, options,
|
endians, stew/shims/macros, options, algorithm, options,
|
||||||
stew/[bitops2, bitseqs, objects, varints, ptrops, ranges/ptr_arith], stint,
|
stew/[bitops2, bitseqs, objects, varints, ptrops, ranges/ptr_arith], stint,
|
||||||
faststreams/input_stream, serialization, serialization/testing/tracing,
|
faststreams/input_stream, serialization, serialization/testing/tracing,
|
||||||
nimcrypto/sha2, blscurve, eth/common,
|
nimcrypto/sha2, blscurve,
|
||||||
./spec/[crypto, datatypes, digest],
|
./spec/[crypto, datatypes, digest],
|
||||||
./ssz/[types, bytes_reader]
|
./ssz/[types, bytes_reader]
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ FROM debian:bullseye-slim AS build
|
|||||||
SHELL ["/bin/bash", "-c"]
|
SHELL ["/bin/bash", "-c"]
|
||||||
|
|
||||||
RUN apt-get -qq update \
|
RUN apt-get -qq update \
|
||||||
&& apt-get -qq -y install build-essential make wget librocksdb-dev libpcre3-dev golang-go git &>/dev/null \
|
&& apt-get -qq -y install build-essential make wget libpcre3-dev golang-go git &>/dev/null \
|
||||||
&& apt-get -qq clean
|
&& apt-get -qq clean
|
||||||
|
|
||||||
# let Docker cache this between Git revision and testnet version changes
|
# let Docker cache this between Git revision and testnet version changes
|
||||||
@ -36,7 +36,7 @@ FROM debian:bullseye-slim
|
|||||||
SHELL ["/bin/bash", "-c"]
|
SHELL ["/bin/bash", "-c"]
|
||||||
|
|
||||||
RUN apt-get -qq update \
|
RUN apt-get -qq update \
|
||||||
&& apt-get -qq -y install librocksdb-dev libpcre3 psmisc &>/dev/null \
|
&& apt-get -qq -y install libpcre3 psmisc &>/dev/null \
|
||||||
&& apt-get -qq clean \
|
&& apt-get -qq clean \
|
||||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ import # Unit test
|
|||||||
./test_block_pool,
|
./test_block_pool,
|
||||||
./test_helpers,
|
./test_helpers,
|
||||||
./test_interop,
|
./test_interop,
|
||||||
|
./test_kvstore,
|
||||||
|
./test_kvstore_lmdb,
|
||||||
./test_ssz,
|
./test_ssz,
|
||||||
./test_state_transition,
|
./test_state_transition,
|
||||||
./test_sync_protocol,
|
./test_sync_protocol,
|
||||||
|
@ -7,17 +7,16 @@
|
|||||||
|
|
||||||
{.used.}
|
{.used.}
|
||||||
|
|
||||||
import options, unittest, sequtils, eth/trie/[db],
|
import options, unittest, sequtils,
|
||||||
../beacon_chain/[beacon_chain_db, extras, interop, ssz],
|
../beacon_chain/[beacon_chain_db, extras, interop, ssz, kvstore],
|
||||||
../beacon_chain/spec/[beaconstate, datatypes, digest, crypto],
|
../beacon_chain/spec/[beaconstate, datatypes, digest, crypto],
|
||||||
# test utilies
|
# test utilies
|
||||||
./testutil, ./testblockutil
|
./testutil, ./testblockutil
|
||||||
|
|
||||||
suite "Beacon chain DB" & preset():
|
suite "Beacon chain DB" & preset():
|
||||||
|
|
||||||
timedTest "empty database" & preset():
|
timedTest "empty database" & preset():
|
||||||
var
|
var
|
||||||
db = init(BeaconChainDB, newMemoryDB())
|
db = init(BeaconChainDB, kvStore MemoryStoreRef.init())
|
||||||
|
|
||||||
check:
|
check:
|
||||||
when const_preset=="minimal":
|
when const_preset=="minimal":
|
||||||
@ -28,7 +27,7 @@ suite "Beacon chain DB" & preset():
|
|||||||
|
|
||||||
timedTest "sanity check blocks" & preset():
|
timedTest "sanity check blocks" & preset():
|
||||||
var
|
var
|
||||||
db = init(BeaconChainDB, newMemoryDB())
|
db = init(BeaconChainDB, kvStore MemoryStoreRef.init())
|
||||||
|
|
||||||
let
|
let
|
||||||
blck = SignedBeaconBlock()
|
blck = SignedBeaconBlock()
|
||||||
@ -46,7 +45,7 @@ suite "Beacon chain DB" & preset():
|
|||||||
|
|
||||||
timedTest "sanity check states" & preset():
|
timedTest "sanity check states" & preset():
|
||||||
var
|
var
|
||||||
db = init(BeaconChainDB, newMemoryDB())
|
db = init(BeaconChainDB, kvStore MemoryStoreRef.init())
|
||||||
|
|
||||||
let
|
let
|
||||||
state = BeaconState()
|
state = BeaconState()
|
||||||
@ -60,7 +59,7 @@ suite "Beacon chain DB" & preset():
|
|||||||
|
|
||||||
timedTest "find ancestors" & preset():
|
timedTest "find ancestors" & preset():
|
||||||
var
|
var
|
||||||
db = init(BeaconChainDB, newMemoryDB())
|
db = init(BeaconChainDB, kvStore MemoryStoreRef.init())
|
||||||
x: ValidatorSig
|
x: ValidatorSig
|
||||||
y = init(ValidatorSig, x.getBytes())
|
y = init(ValidatorSig, x.getBytes())
|
||||||
|
|
||||||
@ -101,7 +100,7 @@ suite "Beacon chain DB" & preset():
|
|||||||
# serialization where an all-zero default-initialized bls signature could
|
# serialization where an all-zero default-initialized bls signature could
|
||||||
# not be deserialized because the deserialization was too strict.
|
# not be deserialized because the deserialization was too strict.
|
||||||
var
|
var
|
||||||
db = init(BeaconChainDB, newMemoryDB())
|
db = init(BeaconChainDB, kvStore MemoryStoreRef.init())
|
||||||
|
|
||||||
let
|
let
|
||||||
state = initialize_beacon_state_from_eth1(
|
state = initialize_beacon_state_from_eth1(
|
||||||
|
45
tests/test_kvstore.nim
Normal file
45
tests/test_kvstore.nim
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{.used.}
|
||||||
|
|
||||||
|
import
|
||||||
|
unittest,
|
||||||
|
../beacon_chain/kvstore
|
||||||
|
|
||||||
|
proc testKVStore*(db: KVStoreRef) =
|
||||||
|
let
|
||||||
|
key = [0'u8, 1, 2, 3]
|
||||||
|
value = [3'u8, 2, 1, 0]
|
||||||
|
value2 = [5'u8, 2, 1, 0]
|
||||||
|
|
||||||
|
check:
|
||||||
|
db != nil
|
||||||
|
|
||||||
|
not db.get(key, proc(data: openArray[byte]) = discard)
|
||||||
|
not db.contains(key)
|
||||||
|
|
||||||
|
db.del(key) # does nothing
|
||||||
|
|
||||||
|
db.put(key, value)
|
||||||
|
|
||||||
|
check:
|
||||||
|
db.contains(key)
|
||||||
|
db.get(key, proc(data: openArray[byte]) =
|
||||||
|
check data == value
|
||||||
|
)
|
||||||
|
|
||||||
|
db.put(key, value2) # overwrite old value
|
||||||
|
check:
|
||||||
|
db.contains(key)
|
||||||
|
db.get(key, proc(data: openArray[byte]) =
|
||||||
|
check data == value2
|
||||||
|
)
|
||||||
|
|
||||||
|
db.del(key)
|
||||||
|
check:
|
||||||
|
not db.get(key, proc(data: openArray[byte]) = discard)
|
||||||
|
not db.contains(key)
|
||||||
|
|
||||||
|
db.del(key) # does nothing
|
||||||
|
|
||||||
|
suite "MemoryStoreRef":
|
||||||
|
test "KVStore interface":
|
||||||
|
testKVStore(kvStore MemoryStoreRef.init())
|
24
tests/test_kvstore_lmdb.nim
Normal file
24
tests/test_kvstore_lmdb.nim
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{.used.}
|
||||||
|
|
||||||
|
import
|
||||||
|
os,
|
||||||
|
unittest,
|
||||||
|
../beacon_chain/[kvstore, kvstore_lmdb],
|
||||||
|
./test_kvstore
|
||||||
|
|
||||||
|
suite "LMDB":
|
||||||
|
setup:
|
||||||
|
let
|
||||||
|
path = os.getTempDir() / "test_kvstore_lmdb"
|
||||||
|
|
||||||
|
os.removeDir(path)
|
||||||
|
os.createDir(path)
|
||||||
|
|
||||||
|
teardown:
|
||||||
|
os.removeDir(path)
|
||||||
|
|
||||||
|
test "KVStore interface":
|
||||||
|
let db = LmdbStoreRef.init(path)
|
||||||
|
defer: db.close()
|
||||||
|
|
||||||
|
testKVStore(kvStore db)
|
@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
algorithm, strformat, stats, times, std/monotimes, stew/endians2,
|
algorithm, strformat, stats, times, std/monotimes, stew/endians2,
|
||||||
chronicles, eth/trie/[db],
|
chronicles,
|
||||||
../beacon_chain/[beacon_chain_db, block_pool, extras, ssz, beacon_node_types],
|
../beacon_chain/[beacon_chain_db, block_pool, extras, ssz, kvstore, beacon_node_types],
|
||||||
../beacon_chain/spec/[digest, beaconstate, datatypes],
|
../beacon_chain/spec/[digest, beaconstate, datatypes],
|
||||||
testblockutil
|
testblockutil
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ template timedTest*(name, body) =
|
|||||||
testTimes.add (f, name)
|
testTimes.add (f, name)
|
||||||
|
|
||||||
proc makeTestDB*(tailState: BeaconState, tailBlock: SignedBeaconBlock): BeaconChainDB =
|
proc makeTestDB*(tailState: BeaconState, tailBlock: SignedBeaconBlock): BeaconChainDB =
|
||||||
result = init(BeaconChainDB, newMemoryDB())
|
result = init(BeaconChainDB, kvStore MemoryStoreRef.init())
|
||||||
BlockPool.preInit(result, tailState, tailBlock)
|
BlockPool.preInit(result, tailState, tailBlock)
|
||||||
|
|
||||||
proc makeTestDB*(validators: int): BeaconChainDB =
|
proc makeTestDB*(validators: int): BeaconChainDB =
|
||||||
|
1
vendor/lmdb
vendored
Submodule
1
vendor/lmdb
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit c8ecc17b38e164e6a728d66a9b1d05bc18dd3ace
|
Loading…
x
Reference in New Issue
Block a user