refactor coin records

This commit is contained in:
Yostra 2020-02-08 01:13:34 -08:00
parent 663045d501
commit 6b34bede0a
9 changed files with 88 additions and 88 deletions

View File

@ -22,7 +22,7 @@ from src.types.hashable.CoinRecord import CoinRecord
from src.types.header_block import HeaderBlock
from src.types.header import Header
from src.types.sized_bytes import bytes32
from src.unspent_store import UnspentStore
from src.coin_store import CoinStore
from src.util.ConsensusError import Err
from src.util.blockchain_check_conditions import blockchain_check_conditions_dict
from src.util.condition_tools import hash_key_pairs_for_conditions_dict
@ -66,7 +66,7 @@ class Blockchain:
# Genesis block
genesis: FullBlock
# Unspent Store
unspent_store: UnspentStore
unspent_store: CoinStore
# Store
store: FullNodeStore
# Coinbase freeze period
@ -74,7 +74,7 @@ class Blockchain:
@staticmethod
async def create(
unspent_store: UnspentStore,
unspent_store: CoinStore,
store: FullNodeStore,
override_constants: Dict = {},
):

View File

@ -22,19 +22,19 @@ class DiffStore:
return self
class UnspentStore:
class CoinStore:
"""
This object handles unspent coins in DB.
This object handles CoinRecords in DB.
Coins from genesis to LCA are stored on disk db, coins from lca to head are stored in DiffStore object for each tip.
When blockchain notifies UnspentStore of new LCA, LCA is added to the disk db,
DiffStores are updated/recreated. (managed by blockchain.py)
"""
unspent_db: aiosqlite.Connection
coin_record_db: aiosqlite.Connection
# Whether or not we are syncing
sync_mode: bool = False
lock: asyncio.Lock
lca_unspent_coins: Dict[str, CoinRecord]
lca_coin_records: Dict[str, CoinRecord]
head_diffs: Dict[bytes32, DiffStore]
cache_size: uint32
@ -44,51 +44,51 @@ class UnspentStore:
self.cache_size = cache_size
# All full blocks which have been added to the blockchain. Header_hash -> block
self.unspent_db = await aiosqlite.connect(db_path)
await self.unspent_db.execute(
self.coin_record_db = await aiosqlite.connect(db_path)
await self.coin_record_db.execute(
(
f"CREATE TABLE IF NOT EXISTS unspent("
f"CREATE TABLE IF NOT EXISTS coin_record("
f"coin_name text PRIMARY KEY,"
f" confirmed_index bigint,"
f" spent_index bigint,"
f" spent int,"
f" coinbase int,"
f" puzzle_hash text,"
f" unspent blob)"
f" coin_record blob)"
)
)
# Useful for reorg lookups
await self.unspent_db.execute(
"CREATE INDEX IF NOT EXISTS coin_confirmed_index on unspent(confirmed_index)"
await self.coin_record_db.execute(
"CREATE INDEX IF NOT EXISTS coin_confirmed_index on coin_record(confirmed_index)"
)
await self.unspent_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent_index on unspent(spent_index)"
await self.coin_record_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent_index on coin_record(spent_index)"
)
await self.unspent_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent on unspent(spent)"
await self.coin_record_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent on coin_record(spent)"
)
await self.unspent_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent on unspent(puzzle_hash)"
await self.coin_record_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent on coin_record(puzzle_hash)"
)
await self.unspent_db.commit()
await self.coin_record_db.commit()
# Lock
self.lock = asyncio.Lock() # external
self.lca_unspent_coins = dict()
self.lca_coin_records = dict()
self.head_diffs = dict()
return self
async def close(self):
await self.unspent_db.close()
await self.coin_record_db.close()
async def _clear_database(self):
cursor = await self.unspent_db.execute("DELETE FROM unspent")
cursor = await self.coin_record_db.execute("DELETE FROM coin_record")
await cursor.close()
await self.unspent_db.commit()
await self.coin_record_db.commit()
async def add_lcas(self, blocks: List[FullBlock]):
for block in blocks:
@ -101,13 +101,13 @@ class UnspentStore:
await self.set_spent(coin_name, block.height)
for coin in additions:
unspent: CoinRecord = CoinRecord(coin, block.height, 0, 0, 0) # type: ignore # noqa
await self.add_unspent(unspent)
record: CoinRecord = CoinRecord(coin, block.height, 0, 0, 0) # type: ignore # noqa
await self.add_coin_record(record)
coinbase: CoinRecord = CoinRecord(block.body.coinbase, block.height, 0, 0, 1) # type: ignore # noqa
fees_coin: CoinRecord = CoinRecord(block.body.fees_coin, block.height, 0, 0, 1) # type: ignore # noqa
await self.add_unspent(coinbase)
await self.add_unspent(fees_coin)
await self.add_coin_record(coinbase)
await self.add_coin_record(fees_coin)
def nuke_diffs(self):
self.head_diffs.clear()
@ -158,29 +158,29 @@ class UnspentStore:
fees_coin: CoinRecord = CoinRecord(block.body.fees_coin, block.height, 0, 0, 1) # type: ignore # noqa
diff_store.diffs[fees_coin.name.hex()] = fees_coin
# Store unspent in DB and ram cache
async def add_unspent(self, unspent: CoinRecord) -> None:
cursor = await self.unspent_db.execute(
"INSERT OR REPLACE INTO unspent VALUES(?, ?, ?, ?, ?, ?, ?)",
# Store CoinRecord in DB and ram cache
async def add_coin_record(self, record: CoinRecord) -> None:
cursor = await self.coin_record_db.execute(
"INSERT OR REPLACE INTO coin_record VALUES(?, ?, ?, ?, ?, ?, ?)",
(
unspent.coin.name().hex(),
unspent.confirmed_block_index,
unspent.spent_block_index,
int(unspent.spent),
int(unspent.coinbase),
str(unspent.coin.puzzle_hash.hex()),
bytes(unspent),
record.coin.name().hex(),
record.confirmed_block_index,
record.spent_block_index,
int(record.spent),
int(record.coinbase),
str(record.coin.puzzle_hash.hex()),
bytes(record),
),
)
await cursor.close()
await self.unspent_db.commit()
self.lca_unspent_coins[unspent.coin.name().hex()] = unspent
if len(self.lca_unspent_coins) > self.cache_size:
while len(self.lca_unspent_coins) > self.cache_size:
first_in = list(self.lca_unspent_coins.keys())[0]
del self.lca_unspent_coins[first_in]
await self.coin_record_db.commit()
self.lca_coin_records[record.coin.name().hex()] = record
if len(self.lca_coin_records) > self.cache_size:
while len(self.lca_coin_records) > self.cache_size:
first_in = list(self.lca_coin_records.keys())[0]
del self.lca_coin_records[first_in]
# Update unspent to be spent in DB
# Update coin_record to be spent in DB
async def set_spent(self, coin_name: bytes32, index: uint32):
current: Optional[CoinRecord] = await self.get_coin_record(coin_name)
if current is None:
@ -192,7 +192,7 @@ class UnspentStore:
uint8(1),
current.coinbase,
) # type: ignore # noqa
await self.add_unspent(spent)
await self.add_coin_record(spent)
# Checks DB and DiffStores for CoinRecord with coin_name and returns it
async def get_coin_record(
@ -202,10 +202,10 @@ class UnspentStore:
diff_store = self.head_diffs[header.header_hash]
if coin_name.hex() in diff_store.diffs:
return diff_store.diffs[coin_name.hex()]
if coin_name.hex() in self.lca_unspent_coins:
return self.lca_unspent_coins[coin_name.hex()]
cursor = await self.unspent_db.execute(
"SELECT * from unspent WHERE coin_name=?", (coin_name.hex(),)
if coin_name.hex() in self.lca_coin_records:
return self.lca_coin_records[coin_name.hex()]
cursor = await self.coin_record_db.execute(
"SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)
)
row = await cursor.fetchone()
await cursor.close()
@ -223,8 +223,8 @@ class UnspentStore:
for _, record in diff_store.diffs.items():
if record.coin.puzzle_hash == puzzle_hash:
coins.add(record)
cursor = await self.unspent_db.execute(
"SELECT * from unspent WHERE puzzle_hash=?", (puzzle_hash.hex(),)
cursor = await self.coin_record_db.execute(
"SELECT * from coin_record WHERE puzzle_hash=?", (puzzle_hash.hex(),)
)
rows = await cursor.fetchall()
await cursor.close()
@ -238,30 +238,30 @@ class UnspentStore:
async def rollback_lca_to_block(self, block_index):
# Update memory cache
delete_queue: bytes32 = []
for coin_name, coin_record in self.lca_unspent_coins.items():
for coin_name, coin_record in self.lca_coin_records.items():
if coin_record.spent_block_index > block_index:
new_unspent = CoinRecord(
new_record = CoinRecord(
coin_record.coin,
coin_record.confirmed_block_index,
coin_record.spent_block_index,
uint8(0),
coin_record.coinbase,
)
self.lca_unspent_coins[coin_record.coin.name().hex()] = new_unspent
self.lca_coin_records[coin_record.coin.name().hex()] = new_record
if coin_record.confirmed_block_index > block_index:
delete_queue.append(coin_name)
for coin_name in delete_queue:
del self.lca_unspent_coins[coin_name]
del self.lca_coin_records[coin_name]
# Delete from storage
c1 = await self.unspent_db.execute(
"DELETE FROM unspent WHERE confirmed_index>?", (block_index,)
c1 = await self.coin_record_db.execute(
"DELETE FROM coin_record WHERE confirmed_index>?", (block_index,)
)
await c1.close()
c2 = await self.unspent_db.execute(
"UPDATE unspent SET spent_index = 0, spent = 0 WHERE spent_index>?",
c2 = await self.coin_record_db.execute(
"UPDATE coin_record SET spent_index = 0, spent = 0 WHERE spent_index>?",
(block_index,),
)
await c2.close()
await self.unspent_db.commit()
await self.coin_record_db.commit()

View File

@ -31,7 +31,7 @@ from src.types.header_block import HeaderBlock
from src.types.peer_info import PeerInfo
from src.types.proof_of_space import ProofOfSpace
from src.types.sized_bytes import bytes32
from src.unspent_store import UnspentStore
from src.coin_store import CoinStore
from src.util import errors
from src.util.api_decorators import api_request
from src.util.errors import BlockNotInBlockchain, InvalidUnfinishedBlock
@ -47,7 +47,7 @@ class FullNode:
blockchain: Blockchain,
config: Dict,
mempool_manager: MempoolManager,
unspent_store: UnspentStore,
unspent_store: CoinStore,
name: str = None,
):
self.config: Dict = config
@ -56,7 +56,7 @@ class FullNode:
self.mempool_manager: MempoolManager = mempool_manager
self._shut_down = False # Set to true to close all infinite loops
self.server: Optional[ChiaServer] = None
self.unspent_store: UnspentStore = unspent_store
self.unspent_store: CoinStore = unspent_store
if name:
self.log = logging.getLogger(name)
else:

View File

@ -11,7 +11,7 @@ from src.types.header import Header
from src.types.mempool_item import MempoolItem
from src.mempool import Mempool
from src.types.sized_bytes import bytes32
from src.unspent_store import UnspentStore
from src.coin_store import CoinStore
from src.util.ConsensusError import Err
from src.util.mempool_check_conditions import (
get_name_puzzle_conditions,
@ -23,7 +23,7 @@ from sortedcontainers import SortedDict
class MempoolManager:
def __init__(self, unspent_store: UnspentStore, override_constants: Dict = {}):
def __init__(self, unspent_store: CoinStore, override_constants: Dict = {}):
# Allow passing in custom overrides
self.constants: Dict = consensus_constants
for key, value in override_constants.items():

View File

@ -21,7 +21,7 @@ from src.server.server import ChiaServer
from src.server.connection import NodeType
from src.types.full_block import FullBlock
from src.types.peer_info import PeerInfo
from src.unspent_store import UnspentStore
from src.coin_store import CoinStore
from src.util.logging import initialize_logging
from src.util.config import load_config_cli
from setproctitle import setproctitle
@ -42,7 +42,7 @@ async def main():
genesis: FullBlock = FullBlock.from_bytes(constants["GENESIS_BLOCK"])
await store.add_block(genesis)
unspent_store = await UnspentStore.create(db_path)
unspent_store = await CoinStore.create(db_path)
log.info("Initializing blockchain from disk")
blockchain = await Blockchain.create(unspent_store, store)

View File

@ -10,7 +10,7 @@ from src.store import FullNodeStore
from src.full_node import FullNode
from src.server.connection import NodeType
from src.server.server import ChiaServer
from src.unspent_store import UnspentStore
from src.coin_store import CoinStore
from tests.block_tools import BlockTools
from src.rpc.rpc_server import start_rpc_server
from src.rpc.rpc_client import RpcClient
@ -53,7 +53,7 @@ class TestRpc:
store = await FullNodeStore.create(db_filename)
await store._clear_database()
blocks = bt.get_consecutive_blocks(test_constants, 10, [], 10)
unspent_store = await UnspentStore.create("blockchain_test.db")
unspent_store = await CoinStore.create("blockchain_test.db")
mempool_manager = MempoolManager(unspent_store)
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)
@ -74,7 +74,7 @@ class TestRpc:
full_node_1._shutdown()
server_1.close_all()
unspent_store2 = await UnspentStore.create("blockchain_test_2.db")
unspent_store2 = await CoinStore.create("blockchain_test_2.db")
mempool_manager2 = MempoolManager(unspent_store2)
full_node_2 = FullNode(store, b, config, mempool_manager2, unspent_store2)
server_2 = ChiaServer(test_node_2_port, full_node_2, NodeType.FULL_NODE)

View File

@ -9,7 +9,7 @@ from src.full_node import FullNode
from src.server.connection import NodeType
from src.server.server import ChiaServer
from src.types.full_block import FullBlock
from src.unspent_store import UnspentStore
from src.coin_store import CoinStore
from tests.block_tools import BlockTools
from src.types.hashable.BLSSignature import BLSPublicKey
from src.util.config import load_config
@ -49,7 +49,7 @@ async def setup_full_node(db_name, port, introducer_port=None, dic={}):
store_1 = await FullNodeStore.create(Path(db_name))
await store_1._clear_database()
unspent_store_1 = await UnspentStore.create(Path(db_name))
unspent_store_1 = await CoinStore.create(Path(db_name))
await unspent_store_1._clear_database()
mempool_1 = MempoolManager(unspent_store_1, dic)

View File

@ -14,7 +14,7 @@ from src.types.full_block import FullBlock
from src.types.hashable.Coin import Coin
from src.types.header import Header, HeaderData
from src.types.proof_of_space import ProofOfSpace
from src.unspent_store import UnspentStore
from src.coin_store import CoinStore
from src.util.ints import uint8, uint32, uint64
from src.util.errors import BlockNotInBlockchain
from tests.block_tools import BlockTools
@ -45,7 +45,7 @@ def event_loop():
class TestGenesisBlock:
@pytest.mark.asyncio
async def test_basic_blockchain(self):
unspent_store = await UnspentStore.create(Path("blockchain_test"))
unspent_store = await CoinStore.create(Path("blockchain_test"))
store = await FullNodeStore.create(Path("blockchain_test"))
await store._clear_database()
bc1 = await Blockchain.create(unspent_store, store)
@ -73,7 +73,7 @@ class TestBlockValidation:
blocks = bt.get_consecutive_blocks(test_constants, 10, [], 10)
store = await FullNodeStore.create(Path("blockchain_test"))
await store._clear_database()
unspent_store = await UnspentStore.create(Path("blockchain_test"))
unspent_store = await CoinStore.create(Path("blockchain_test"))
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)
for i in range(1, 9):
result, removed = await b.receive_block(blocks[i])
@ -282,7 +282,7 @@ class TestBlockValidation:
# Make it 5x faster than target time
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 2)
unspent_store = await UnspentStore.create(Path("blockchain_test"))
unspent_store = await CoinStore.create(Path("blockchain_test"))
store = await FullNodeStore.create(Path("blockchain_test"))
await store._clear_database()
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)
@ -312,7 +312,7 @@ class TestReorgs:
@pytest.mark.asyncio
async def test_basic_reorg(self):
blocks = bt.get_consecutive_blocks(test_constants, 100, [], 9)
unspent_store = await UnspentStore.create(Path("blockchain_test"))
unspent_store = await CoinStore.create(Path("blockchain_test"))
store = await FullNodeStore.create(Path("blockchain_test"))
await store._clear_database()
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)
@ -341,7 +341,7 @@ class TestReorgs:
@pytest.mark.asyncio
async def test_reorg_from_genesis(self):
blocks = bt.get_consecutive_blocks(test_constants, 20, [], 9, b"0")
unspent_store = await UnspentStore.create(Path("blockchain_test"))
unspent_store = await CoinStore.create(Path("blockchain_test"))
store = await FullNodeStore.create(Path("blockchain_test"))
await store._clear_database()
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)
@ -383,7 +383,7 @@ class TestReorgs:
@pytest.mark.asyncio
async def test_lca(self):
blocks = bt.get_consecutive_blocks(test_constants, 5, [], 9, b"0")
unspent_store = await UnspentStore.create(Path("blockchain_test"))
unspent_store = await CoinStore.create(Path("blockchain_test"))
store = await FullNodeStore.create(Path("blockchain_test"))
await store._clear_database()
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)
@ -410,7 +410,7 @@ class TestReorgs:
@pytest.mark.asyncio
async def test_get_header_hashes(self):
blocks = bt.get_consecutive_blocks(test_constants, 5, [], 9, b"0")
unspent_store = await UnspentStore.create(Path("blockchain_test"))
unspent_store = await CoinStore.create(Path("blockchain_test"))
store = await FullNodeStore.create(Path("blockchain_test"))
await store._clear_database()
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)

View File

@ -6,7 +6,7 @@ import pytest
from src.blockchain import Blockchain, ReceiveBlockResult
from src.store import FullNodeStore
from src.unspent_store import UnspentStore
from src.coin_store import CoinStore
from tests.block_tools import BlockTools
bt = BlockTools()
@ -37,7 +37,7 @@ class TestUnspent:
async def test_basic_unspent_store(self):
blocks = bt.get_consecutive_blocks(test_constants, 9, [], 9, b"0")
db = await UnspentStore.create(Path("fndb_test.db"))
db = await CoinStore.create(Path("fndb_test.db"))
await db._clear_database()
# Save/get block
@ -55,7 +55,7 @@ class TestUnspent:
async def test_set_spent(self):
blocks = bt.get_consecutive_blocks(test_constants, 9, [], 9, b"0")
db = await UnspentStore.create(Path("fndb_test.db"))
db = await CoinStore.create(Path("fndb_test.db"))
await db._clear_database()
# Save/get block
@ -80,7 +80,7 @@ class TestUnspent:
async def test_rollback(self):
blocks = bt.get_consecutive_blocks(test_constants, 9, [], 9, b"0")
db = await UnspentStore.create(Path("fndb_test.db"))
db = await CoinStore.create(Path("fndb_test.db"))
await db._clear_database()
# Save/get block
@ -117,7 +117,7 @@ class TestUnspent:
@pytest.mark.asyncio
async def test_basic_reorg(self):
blocks = bt.get_consecutive_blocks(test_constants, 100, [], 9)
unspent_store = await UnspentStore.create(Path("blockchain_test.db"))
unspent_store = await CoinStore.create(Path("blockchain_test.db"))
store = await FullNodeStore.create(Path("blockchain_test.db"))
await store._clear_database()
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)
@ -176,7 +176,7 @@ class TestUnspent:
async def test_get_puzzle_hash(self):
num_blocks = 20
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 9)
unspent_store = await UnspentStore.create(Path("blockchain_test.db"))
unspent_store = await CoinStore.create(Path("blockchain_test.db"))
store = await FullNodeStore.create(Path("blockchain_test.db"))
await store._clear_database()
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)