async def add_coin_record(self, record: WalletCoinRecord) -> None: # update wallet cache name = record.name() self.coin_record_cache[name] = record if record.wallet_id in self.unspent_coin_wallet_cache: if record.spent and name in self.unspent_coin_wallet_cache[ record.wallet_id]: self.unspent_coin_wallet_cache[record.wallet_id].pop(name) if not record.spent: self.unspent_coin_wallet_cache[record.wallet_id][name] = record else: if not record.spent: self.unspent_coin_wallet_cache[record.wallet_id] = {} self.unspent_coin_wallet_cache[record.wallet_id][name] = record cursor = await self.db_connection.execute( "INSERT OR REPLACE INTO coin_record VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ( name.hex(), record.confirmed_block_height, record.spent_block_height, int(record.spent), int(record.coinbase), str(record.coin.puzzle_hash.hex()), str(record.coin.parent_coin_info.hex()), bytes(record.coin.amount), record.wallet_type, record.wallet_id, ), ) await cursor.close()
async def get_unspent_coins_for_wallet( self, wallet_id: int) -> Set[WalletCoinRecord]: """ Returns set of CoinRecords that have not been spent yet for a wallet. """ async with self.wallet_cache_lock: if wallet_id in self.coin_wallet_record_cache: wallet_coins: Dict[ bytes32, WalletCoinRecord] = self.coin_wallet_record_cache[ wallet_id] return set(wallet_coins.values()) coin_set = set() cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=0 and wallet_id=?", (wallet_id, ), ) rows = await cursor.fetchall() await cursor.close() cache_dict = {} for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coin_record = WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) coin_set.add(coin_record) cache_dict[coin.name()] = coin_record self.coin_wallet_record_cache[wallet_id] = cache_dict return coin_set
async def get_coin_records_by_puzzle_hash(self, puzzle_hash: bytes32) -> List[WalletCoinRecord]: """Returns a list of all coin records with the given puzzle hash""" coins = set() cursor = await self.db_connection.execute("SELECT * from coin_record WHERE puzzle_hash=?", (puzzle_hash.hex(),)) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return list(coins)
async def get_coin_record_by_coin_id(self, coin_id: bytes32) -> Optional[WalletCoinRecord]: """Returns a coin records with the given name, if it exists""" cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_id.hex(),)) row = await cursor.fetchone() await cursor.close() if row is None: return None coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coin_record = WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) return coin_record
async def get_all_coins(self) -> Set[WalletCoinRecord]: """ Returns set of all CoinRecords.""" coins = set() cursor = await self.db_connection.execute("SELECT * from coin_record") rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return coins
async def get_coin_record(self, coin_name: bytes32) -> Optional[WalletCoinRecord]: """ Returns CoinRecord with specified coin id. """ if coin_name in self.coin_record_cache: return self.coin_record_cache[coin_name] cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) return WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) return None
async def set_spent(self, coin_name: bytes32, height: uint32): current: Optional[WalletCoinRecord] = await self.get_coin_record(coin_name) if current is None: return spent: WalletCoinRecord = WalletCoinRecord( current.coin, current.confirmed_block_height, height, True, current.coinbase, current.wallet_type, current.wallet_id, ) await self.add_coin_record(spent)
async def rollback_to_block(self, height: int): """ Rolls back the blockchain to block_index. All blocks confirmed after this point are removed from the LCA. All coins confirmed after this point are removed. All coins spent after this point are set to unspent. Can be -1 (rollback all) """ # Update memory cache delete_queue: List[WalletCoinRecord] = [] for coin_name, coin_record in self.coin_record_cache.items(): if coin_record.spent_block_height > height: new_record = WalletCoinRecord( coin_record.coin, coin_record.confirmed_block_height, coin_record.spent_block_height, False, coin_record.coinbase, coin_record.wallet_type, coin_record.wallet_id, ) self.coin_record_cache[coin_record.coin.name()] = new_record if coin_record.confirmed_block_height > height: delete_queue.append(coin_record) for coin_record in delete_queue: self.coin_record_cache.pop(coin_record.coin.name()) if coin_record.wallet_id in self.coin_wallet_record_cache: coin_cache = self.coin_wallet_record_cache[ coin_record.wallet_id] if coin_record.coin.name() in coin_cache: coin_cache.pop(coin_record.coin.name()) # Delete from storage c1 = await self.db_connection.execute( "DELETE FROM coin_record WHERE confirmed_height>?", (height, )) await c1.close() c2 = await self.db_connection.execute( "UPDATE coin_record SET spent_height = 0, spent = 0 WHERE spent_height>?", (height, ), ) c3 = await self.db_connection.execute( "UPDATE coin_record SET spent_height = 0, spent = 0 WHERE spent_height>?", (height, ), ) await c3.close() await c2.close() await self.db_connection.commit()
async def coin_added( self, coin: Coin, coinbase: bool, fee_reward: bool, wallet_id: uint32, wallet_type: WalletType, height: uint32, ): """ Adding coin to DB """ self.log.info(f"Adding coin: {coin} at {height}") farm_reward = False if coinbase or fee_reward: farm_reward = True now = uint64(int(time.time())) if coinbase: tx_type: int = TransactionType.COINBASE_REWARD.value else: tx_type = TransactionType.FEE_REWARD.value tx_record = TransactionRecord( confirmed_at_height=uint32(height), created_at_time=now, to_puzzle_hash=coin.puzzle_hash, amount=coin.amount, fee_amount=uint64(0), confirmed=True, sent=uint32(0), spend_bundle=None, additions=[coin], removals=[], wallet_id=wallet_id, sent_to=[], trade_id=None, type=uint32(tx_type), name=coin.name(), ) await self.tx_store.add_transaction_record(tx_record, True) else: records = await self.tx_store.tx_with_addition_coin( coin.name(), wallet_id) if len(records) > 0: # This is the change from this transaction for record in records: if record.confirmed is False: await self.tx_store.set_confirmed(record.name, height) else: now = uint64(int(time.time())) tx_record = TransactionRecord( confirmed_at_height=uint32(height), created_at_time=now, to_puzzle_hash=coin.puzzle_hash, amount=coin.amount, fee_amount=uint64(0), confirmed=True, sent=uint32(0), spend_bundle=None, additions=[coin], removals=[], wallet_id=wallet_id, sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=coin.name(), ) if coin.amount > 0: await self.tx_store.add_transaction_record(tx_record, True) coin_record: WalletCoinRecord = WalletCoinRecord( coin, height, uint32(0), False, farm_reward, wallet_type, wallet_id) await self.coin_store.add_coin_record(coin_record) if wallet_type == WalletType.COLOURED_COIN or wallet_type == WalletType.DISTRIBUTED_ID: wallet = self.wallets[wallet_id] header_hash: bytes32 = self.blockchain.height_to_hash(height) block: Optional[ HeaderBlockRecord] = await self.block_store.get_header_block_record( header_hash) assert block is not None assert block.removals is not None await wallet.coin_added(coin, header_hash, block.removals, height) self.state_changed("coin_added", wallet_id)
def coin_record_from_row(self, row: sqlite3.Row) -> WalletCoinRecord: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) return WalletCoinRecord(coin, uint32(row[1]), uint32(row[2]), bool(row[3]), bool(row[4]), WalletType(row[8]), row[9])