Skip to content
This repository has been archived by the owner on Jun 13, 2019. It is now read-only.

Commit

Permalink
leveldb DAO implement
Browse files Browse the repository at this point in the history
  • Loading branch information
boolafish committed Jun 14, 2018
1 parent 5dab4b0 commit 1a25d17
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ contract_data/
**/__pycache__/
*.pyc
.pytest_cache/
.db/
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# plasma-cash

### Dependency Prerequisite

- [LevelDB](https://github.com/google/leveldb)

Mac:
```
$ brew install leveldb
```

Linux:

LevelDB should be installed along with `plyvel` once you make the project later on.

Windows:

First, install [vcpkg](https://github.com/Microsoft/vcpkg). Then,

```
> vcpkg install leveldb
```

### Develop

Install requirements:
```
pip install -r requirements.txt
Expand Down
4 changes: 2 additions & 2 deletions plasma_cash/child_chain/child_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(self, authority, root_chain, db):
self.authority = authority
self.db = db
self.current_block = Block()
self.current_block_number = 1
self.current_block_number = self.db.get_current_block_num()

# Register for deposit event listener
deposit_filter = self.root_chain.on('Deposit')
Expand All @@ -44,7 +44,7 @@ def submit_block(self, sig):
).submitBlock(merkle_hash, self.current_block_number)

self.db.save_block(self.current_block, self.current_block_number)
self.current_block_number += 1
self.current_block_number = self.db.increment_current_block_num()
self.current_block = Block()

return merkle_hash
Expand Down
14 changes: 14 additions & 0 deletions plasma_cash/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import os

PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

plasma_config = dict(
ROOT_CHAIN_CONTRACT_ADDRESS='0xda52B0A0a040BFeAb711065cB69321ebAE9bB96f',
AUTHORITY=b';\x08\x84\xf4\xe5\x0e\x9b\xc2\xce\x9b"J\xb7/\xea\x89\xa8\x1c\xdf|',
AUTHORITY_KEY=b'\xa1\x89i\x81|,\xef\xad\xf5+\x93\xeb \xf9\x17\xdc\xe7`\xce\x13\xb2\xac\x90%\xe06\x1a\xd1\xe7\xa1\xd4H',
)

"""
db_config = {
'type': 'leveldb' | 'memory' (required)
'path': '' (optional, if nor specific set, would have default path)
}
"""
db_config = {
'type': 'memory'
}
19 changes: 16 additions & 3 deletions plasma_cash/dependency_config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import os

from plasma_cash.child_chain.child_chain import ChildChain
from plasma_cash.client.child_chain_client import ChildChainClient
from plasma_cash.client.client import Client
from plasma_cash.config import plasma_config
from plasma_cash.config import PROJECT_DIR, db_config, plasma_config
from plasma_cash.root_chain.deployer import Deployer
from plasma_cash.utils.db.leveldb import LevelDb
from plasma_cash.utils.db.memory_db import MemoryDb


Expand All @@ -15,9 +18,19 @@ def __init__(self):
self._db = None

def get_db(self):
# TODO: enable real_db type & memory_db chosen by config
if self._db is None:
self._db = MemoryDb()
db_type = db_config['type']
if db_type == 'memory':
self._db = MemoryDb()
elif db_type == 'leveldb':
if db_config.get('path'):
path = db_config['path']
else:
path = os.path.join(PROJECT_DIR, '.db')
self._db = LevelDb(path)
else:
raise ValueError('type of {} is not supported'.format(db_type))

return self._db

def get_root_chain(self):
Expand Down
8 changes: 8 additions & 0 deletions plasma_cash/utils/db/db_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ def get_block(self, block_number):
@abc.abstractmethod
def save_block(self, block, block_number):
return NotImplemented

@abc.abstractmethod
def get_current_block_num(self):
return NotImplemented

@abc.abstractmethod
def increment_current_block_num(self):
return NotImplemented
42 changes: 42 additions & 0 deletions plasma_cash/utils/db/leveldb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import plyvel
import rlp
from ethereum import utils

from plasma_cash.child_chain.block import Block

from .db_interface import DbInterface
from .exceptions import BlockAlreadyExistsException


class LevelDb(DbInterface):
CURRENT_BLOCK_NUM_KEY = b'current_block_num'

def __init__(self, path):
self.db = plyvel.DB(path, create_if_missing=True)

def get_block(self, block_number):
key = str.encode(str(block_number))
encoded_block = self.db.get(key)
if encoded_block:
return rlp.decode(utils.decode_hex(encoded_block.decode()), Block)
else:
return None

def save_block(self, block, block_number):
key = str.encode(str(block_number))
if self.db.get(key):
raise BlockAlreadyExistsException('should not save block with same blknum again.')
self.db.put(key, str.encode(rlp.encode(block).hex()))

def get_current_block_num(self):
current_block_num = self.db.get(self.CURRENT_BLOCK_NUM_KEY)
if not current_block_num:
self.db.put(self.CURRENT_BLOCK_NUM_KEY, '1'.encode())
return 1
return int(current_block_num.decode())

def increment_current_block_num(self):
current_block_num = self.get_current_block_num()
incr_block_num = current_block_num + 1
self.db.put(self.CURRENT_BLOCK_NUM_KEY, str(incr_block_num).encode())
return incr_block_num
10 changes: 9 additions & 1 deletion plasma_cash/utils/db/memory_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ class MemoryDb(DbInterface):

def __init__(self):
self.blocks = {}
self.block_num = 1

def get_block(self, block_number):
return self.blocks.get(block_number)

def save_block(self, block, block_number):
if block_number in self.blocks:
raise BlockAlreadyExistsException('should not save block with same id again.')
raise BlockAlreadyExistsException('should not save block with same blknum again.')
self.blocks[block_number] = block

def get_current_block_num(self):
return self.block_num

def increment_current_block_num(self):
self.block_num += 1
return self.block_num
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Flask==1.0.2
requests==2.18.4
rlp==0.6.0
ethereum==2.3.1
plyvel==1.0.4

# test
pytest==3.5.0
Expand Down
1 change: 1 addition & 0 deletions unit_tests/child_chain/test_child_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def child_chain(self, root_chain, db):
# create a block with the dummy transaction
db.save_block(Block([tx]), 1)
child_chain.current_block_number = 2
db.increment_current_block_num()
return child_chain

def test_constructor(self, root_chain, db):
Expand Down
6 changes: 3 additions & 3 deletions unit_tests/client/test_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from mockito import any, mock, verify, when
from mockito import ANY, mock, verify, when

from plasma_cash.child_chain.block import Block
from plasma_cash.client.client import Client
Expand Down Expand Up @@ -33,7 +33,7 @@ def test_deposit(self, client, root_chain):
DUMMY_DEPOSITOR = 'dummy depositor'
DUMMY_CURRENCY = 'dummy currency'

when(root_chain).transact({'from': DUMMY_DEPOSITOR}).thenReturn(MOCK_TRANSACT)
when(root_chain).transact(ANY).thenReturn(MOCK_TRANSACT)

client.deposit(DUMMY_AMOUNT, DUMMY_DEPOSITOR, DUMMY_CURRENCY)

Expand Down Expand Up @@ -77,7 +77,7 @@ def test_send_transaction(self, client, child_chain):

# `Transaction` is mocked previously, so use `any` here as a work around
(when('plasma_cash.client.client.rlp')
.encode(MOCK_TX, any)
.encode(MOCK_TX, ANY)
.thenReturn(MOCK_ENCODED_TX))

client.send_transaction(
Expand Down
66 changes: 66 additions & 0 deletions unit_tests/utils/db/test_leveldb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import pytest
from mockito import ANY, when

from plasma_cash.child_chain.block import Block
from plasma_cash.utils.db.exceptions import BlockAlreadyExistsException
from plasma_cash.utils.db.leveldb import LevelDb
from unit_tests.unstub_mixin import UnstubMixin


def is_byte(data):
try:
data = data.decode()
except Exception:
return False
return True


class FakeLevelDb(object):
def __init__(self):
self.data = {}

def put(self, k, v):
if not is_byte(k) or not is_byte(v):
raise Exception('leveldb could only take byte type as input')
self.data[k] = v

def get(self, k):
if not is_byte(k):
raise Exception('leveldb could only take byte type as input')
return self.data.get(k)


class TestLevelDb(UnstubMixin):

@pytest.fixture(scope='function')
def db(self):
db = FakeLevelDb()
(when('plasma_cash.utils.db.leveldb.plyvel')
.DB(ANY, create_if_missing=True).thenReturn(db))
return LevelDb('test_db')

def test_block_normal_case(self, db):
DUMMY_BLOCK = Block()
DUMMY_BLK_NUM = 1
db.save_block(DUMMY_BLOCK, DUMMY_BLK_NUM)
assert db.get_block(DUMMY_BLK_NUM) == DUMMY_BLOCK

def test_get_block_none(self, db):
NON_EXIST_BLK_NUM = -1
assert db.get_block(NON_EXIST_BLK_NUM) is None

def test_save_block_already_exists(self, db):
DUMMY_BLOCK = Block()
DUMMY_BLK_NUM = 1
db.save_block(DUMMY_BLOCK, DUMMY_BLK_NUM)
with pytest.raises(BlockAlreadyExistsException):
db.save_block('second block should fail', DUMMY_BLK_NUM)

def test_get_current_block_num_first_time_return_1(self, db):
assert db.get_current_block_num() == 1

def test_increment_current_block_num(self, db):
block_num = db.get_current_block_num()
db.increment_current_block_num()
block_num_incr = db.get_current_block_num()
assert block_num_incr == block_num + 1
9 changes: 9 additions & 0 deletions unit_tests/utils/db/test_memory_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ def test_save_block_already_exists(self, db):
db.save_block('first block', DUMMY_BLK_NUM)
with pytest.raises(BlockAlreadyExistsException):
db.save_block('second block should fail', DUMMY_BLK_NUM)

def test_get_current_block_num_first_time_return_1(self, db):
assert db.get_current_block_num() == 1

def test_increment_current_block_num(self, db):
block_num = db.get_current_block_num()
db.increment_current_block_num()
block_num_incr = db.get_current_block_num()
assert block_num_incr == block_num + 1

0 comments on commit 1a25d17

Please sign in to comment.