forked from LBRYCommunity/lbry-sdk
125 lines
2.9 KiB
Python
125 lines
2.9 KiB
Python
|
import os
|
||
|
import asyncio
|
||
|
from collections import namedtuple
|
||
|
|
||
|
import apsw
|
||
|
|
||
|
|
||
|
DDL = """
|
||
|
pragma journal_mode=WAL;
|
||
|
|
||
|
create table block (
|
||
|
block_hash bytes not null,
|
||
|
previous_hash bytes not null,
|
||
|
height int
|
||
|
);
|
||
|
create table tx (
|
||
|
block_hash integer not null,
|
||
|
position integer not null,
|
||
|
tx_hash bytes not null
|
||
|
);
|
||
|
create table txi (
|
||
|
block_hash bytes not null,
|
||
|
tx_hash bytes not null,
|
||
|
txo_hash bytes not null
|
||
|
);
|
||
|
create table claim (
|
||
|
txo_hash bytes not null,
|
||
|
claim_hash bytes not null,
|
||
|
claim_name text not null,
|
||
|
amount integer not null,
|
||
|
height integer
|
||
|
);
|
||
|
create table claim_history (
|
||
|
block_hash bytes not null,
|
||
|
tx_hash bytes not null,
|
||
|
tx_position integer not null,
|
||
|
txo_hash bytes not null,
|
||
|
claim_hash bytes not null,
|
||
|
claim_name text not null,
|
||
|
action integer not null,
|
||
|
amount integer not null,
|
||
|
height integer,
|
||
|
is_spent bool
|
||
|
);
|
||
|
create table support (
|
||
|
block_hash bytes not null,
|
||
|
tx_hash bytes not null,
|
||
|
txo_hash bytes not null,
|
||
|
claim_hash bytes not null,
|
||
|
amount integer not null
|
||
|
);
|
||
|
"""
|
||
|
|
||
|
|
||
|
class BlockchainDB:
|
||
|
|
||
|
__slots__ = 'db', 'directory'
|
||
|
|
||
|
def __init__(self, path: str):
|
||
|
self.db = None
|
||
|
self.directory = path
|
||
|
|
||
|
def open(self):
|
||
|
self.db = apsw.Connection(
|
||
|
os.path.join(self.directory, 'blockchain.db'),
|
||
|
flags=(
|
||
|
apsw.SQLITE_OPEN_READWRITE |
|
||
|
apsw.SQLITE_OPEN_CREATE |
|
||
|
apsw.SQLITE_OPEN_URI
|
||
|
)
|
||
|
)
|
||
|
def exec_factory(cursor, statement, bindings):
|
||
|
tpl = namedtuple('row', (d[0] for d in cursor.getdescription()))
|
||
|
cursor.setrowtrace(lambda cursor, row: tpl(*row))
|
||
|
return True
|
||
|
self.db.setexectrace(exec_factory)
|
||
|
self.execute(DDL)
|
||
|
self.execute(f"ATTACH {os.path.join(self._db_path, 'block_index.sqlite')} AS block_index")
|
||
|
|
||
|
def close(self):
|
||
|
if self.db is not None:
|
||
|
self.db.close()
|
||
|
|
||
|
def execute(self, *args):
|
||
|
return self.db.cursor().execute(*args)
|
||
|
|
||
|
def executemany(self, *args):
|
||
|
return self.db.cursor().executemany(*args)
|
||
|
|
||
|
def begin(self):
|
||
|
self.execute('begin;')
|
||
|
|
||
|
def commit(self):
|
||
|
self.execute('commit;')
|
||
|
|
||
|
def get_blocks(self):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class AsyncBlockchainDB:
|
||
|
|
||
|
__slots__ = 'db',
|
||
|
|
||
|
def __init__(self, db: BlockchainDB):
|
||
|
self.db = db
|
||
|
|
||
|
@classmethod
|
||
|
def from_path(cls, path: str):
|
||
|
return cls(BlockchainDB(path))
|
||
|
|
||
|
@staticmethod
|
||
|
async def run_in_executor(func, *args):
|
||
|
return await asyncio.get_running_loop().run_in_executor(
|
||
|
None, func, *args
|
||
|
)
|
||
|
|
||
|
async def open(self):
|
||
|
return await self.run_in_executor(self.db.open)
|
||
|
|
||
|
async def close(self):
|
||
|
return await self.run_in_executor(self.db.close)
|
||
|
|
||
|
async def get_blocks(self):
|
||
|
return await self.run_in_executor(self.db.get_blocks)
|