Mirror creation working

This commit is contained in:
Matt Hauff 2022-07-29 10:32:57 -05:00
parent 54b8e5ea06
commit fcb359938c
No known key found for this signature in database
GPG Key ID: 3CBA6CFC81A00E46
5 changed files with 145 additions and 36 deletions

View File

@ -12,6 +12,7 @@ from clvm.EvalError import EvalError
from chia.consensus.block_record import BlockRecord
from chia.protocols.wallet_protocol import PuzzleSolutionResponse, CoinState
from chia.server.outbound_message import NodeType
from chia.wallet.db_wallet.db_wallet_puzzles import (
ACS_MU,
ACS_MU_PH,
@ -69,6 +70,14 @@ class SingletonRecord(Streamable):
generation: uint32
timestamp: uint64
@dataclasses.dataclass(frozen=True)
class Mirror:
coin_id: bytes32
launcher_id: bytes32
amount: uint64
urls: List[bytes]
ours: bool
_T_DataLayerWallet = TypeVar("_T_DataLayerWallet", bound="DataLayerWallet")
@ -288,6 +297,7 @@ class DataLayerWallet:
await self.wallet_state_manager.dl_store.add_launcher(launcher_spend.coin, in_transaction)
await self.wallet_state_manager.add_interested_puzzle_hashes([launcher_id], [self.id()], in_transaction)
await self.wallet_state_manager.add_interested_puzzle_hashes([create_mirror_puzzle().get_tree_hash()], [self.id()], in_transaction)
await self.wallet_state_manager.add_interested_coin_ids([new_singleton.name()], in_transaction)
await self.wallet_state_manager.coin_store.add_coin_record(
WalletCoinRecord(
@ -735,13 +745,13 @@ class DataLayerWallet:
return collected
async def create_new_mirror(self, launcher_id: bytes32, urls: List[str]) -> List[TransactionRecord]:
async def create_new_mirror(self, launcher_id: bytes32, amount: uint64, urls: List[bytes], fee: uint64 = uint64(0)) -> List[TransactionRecord]:
create_mirror_tx_record: Optional[TransactionRecord] = await self.standard_wallet.generate_signed_transaction(
amount=uint64(0),
puzzle_hash=P2_PARENT.get_tree_hash(),
amount=amount,
puzzle_hash=create_mirror_puzzle().get_tree_hash(),
fee=fee,
primaries=[],
memos=[launcher_id, *(bytes(url) for url in urls)]
memos=[launcher_id, *(url for url in urls)],
ignore_max_send_amount=False,
)
assert create_mirror_tx_record is not None and create_mirror_tx_record.spend_bundle is not None
@ -758,20 +768,30 @@ class DataLayerWallet:
if len(all_nodes.keys()) == 0:
raise ValueError("Not connected to the full node")
synced_peers = [node for node in all_nodes.values() if node.peer_node_id in self.wallet_state_manager.wallet_node.synced_peers]
peer = None
for node in synced_peers:
if self.wallet_state_manager.wallet_node.is_trusted(node):
peer = node
break
if peer is None:
for node in synced_peers:
if self.is_trusted(node):
peer = node
break
if peer is None:
if len(synced_peers) > 0:
peer = synced_peers[0]
else:
peer = list(all_nodes.values())[0]
parent_spend: Optional[CoinSpend] = await self.wallet_node.fetch_puzzle_solution(peer, height, coin)
if len(synced_peers) > 0:
peer = synced_peers[0]
else:
peer = list(all_nodes.values())[0]
parent_state: List[CoinState] = (await self.wallet_state_manager.wallet_node.get_coin_state([coin.parent_coin_info]))[0]
parent_spend: Optional[CoinSpend] = await self.wallet_state_manager.wallet_node.fetch_puzzle_solution(peer, height, parent_state.coin)
launcher_id, urls = get_mirror_info(parent_spend.puzzle_reveal.to_program(), parent_spend.solution.to_program())
ours: bool = await wallet.get_wallet_for_coin(coin.name()) is not None
await self.wallet_state_manager.dl_store.add_mirror(coin.name(), launcher_id, urls, ours, True)
ours: bool = await self.wallet_state_manager.get_wallet_for_coin(coin.parent_coin_info) is not None
await self.wallet_state_manager.dl_store.add_mirror(
Mirror(
coin.name(),
launcher_id,
uint64(coin.amount),
urls,
ours,
),
True,
)
async def singleton_removed(self, parent_spend: CoinSpend, height: uint32, in_transaction: bool = False) -> None:
@ -980,6 +1000,10 @@ class DataLayerWallet:
)
return singletons
async def get_mirrors_for_launcher(self, launcher_id: bytes32) -> List[Mirror]:
return await self.wallet_state_manager.dl_store.get_mirrors(launcher_id)
##########
# WALLET #
##########

View File

@ -4,11 +4,11 @@ from typing import List, Optional, Type, TypeVar, Union
import aiosqlite
import dataclasses
from chia.data_layer.data_layer_wallet import SingletonRecord
from chia.data_layer.data_layer_wallet import Mirror, SingletonRecord
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.db_wrapper import DBWrapper
from chia.util.ints import uint32, uint64
from chia.util.ints import uint16, uint32, uint64
from chia.wallet.lineage_proof import LineageProof
_T_DataLayerStore = TypeVar("_T_DataLayerStore", bound="DataLayerStore")
@ -27,6 +27,22 @@ def _row_to_singleton_record(row: Row) -> SingletonRecord:
uint64(row[8]),
)
def _row_to_mirror(row: Row) -> Mirror:
urls: List[bytes] = []
byte_list: bytes = row[3]
while byte_list != b'':
length = uint16.from_bytes(byte_list[0:2])
url = byte_list[2:length+2]
byte_list = byte_list[length+2:]
urls.append(url)
return Mirror(
bytes32(row[0]),
bytes32(row[1]),
uint64.from_bytes(row[2]),
urls,
bool(row[4])
)
class DataLayerStore:
"""
@ -62,6 +78,7 @@ class DataLayerStore:
"CREATE TABLE IF NOT EXISTS mirrors("
"coin_id blob PRIMARY KEY,"
"launcher_id blob,"
"amount blob,"
"urls blob,"
"ours tinyint)"
)
@ -286,7 +303,7 @@ class DataLayerStore:
c = await self.db_connection.execute("DELETE FROM launchers WHERE id=?", (launcher_id,))
await c.close()
async def add_mirror(self, coin_name: bytes32, launcher_id: bytes32, urls: List[bytes], ours: bool, in_transaction: bool) -> None:
async def add_mirror(self, mirror: Mirror, in_transaction: bool) -> None:
"""
Add a mirror coin to the DB
"""
@ -295,12 +312,13 @@ class DataLayerStore:
await self.db_wrapper.lock.acquire()
try:
cursor = await self.db_connection.execute(
"INSERT OR REPLACE INTO mirrors VALUES (?, ?, ?, ?)",
"INSERT OR REPLACE INTO mirrors VALUES (?, ?, ?, ?, ?)",
(
coin_name,
launcher_id,
b''.join([bytes(uint16(len(url)))+url for url in urls]), # prefix each item with a length
1 if ours else 0,
mirror.coin_id,
mirror.launcher_id,
bytes(mirror.amount),
b''.join([bytes(uint16(len(url)))+url for url in mirror.urls]), # prefix each item with a length
1 if mirror.ours else 0,
),
)
await cursor.close()
@ -315,21 +333,16 @@ class DataLayerStore:
if not in_transaction:
self.db_wrapper.lock.release()
async def get_mirrors(self, launcher_id: bytes32) -> List[bytes]:
async def get_mirrors(self, launcher_id: bytes32) -> List[Mirror]:
cursor = await self.db_connection.execute(
"SELECT * from mirrors WHERE launcher_id=?",
(launcher_id,),
)
rows = await cursor.fetchall()
await cursor.close()
urls = []
mirrors: List[Mirror] = []
for row in rows:
byte_list: bytes = row[2]
while byte_list != b'':
length = uint16.from_bytes(byte_list[0])
url = byte_list[1:length+1]
byte_list = byte_list[length+1:]
urls.append(url)
mirrors.append(_row_to_mirror(row))
return urls
return mirrors

View File

@ -2,6 +2,7 @@ from typing import Union, Tuple, Iterator, List
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.blockchain_format.program import Program
from chia.types.condition_opcodes import ConditionOpcode
from chia.util.ints import uint64
from chia.wallet.nft_wallet.nft_puzzles import create_nft_layer_puzzle_with_curry_params, NFT_STATE_LAYER_MOD
from chia.wallet.puzzles.load_clvm import load_clvm
@ -87,7 +88,7 @@ def get_mirror_info(parent_puzzle: Program, parent_solution: Program) -> Tuple[b
conditions = parent_puzzle.run(parent_solution)
for condition in conditions.as_iter():
if condition.first().as_python() == ConditionOpcode.CREATE_COIN and condition.at("rf").as_python() == create_mirror_puzzle().get_tree_hash():
memos: List[bytes] = conditions.at("rrrf").as_python()
memos: List[bytes] = condition.at("rrrf").as_python()
launcher_id = bytes32(memos[0])
return launcher_id, [url for url in memos[1:]]
raise ValueError("The provided puzzle and solution do not create a mirror coin")

View File

@ -0,0 +1 @@
ff02ffff01ff04ffff04ffff01934153534552545f4d595f504152454e545f4944ffff04ffff02ff0affff04ff02ffff04ff0bffff04ffff02ff0effff04ff02ffff04ffff02ff05ff1780ff80808080ffff04ff2fff808080808080ff808080ff8b03840d9b5c9f9cdc1e609f80ffff04ffff01ff20ffff02ffff03ffff22ffff09ffff0dff0580ff0480ffff09ffff0dff0b80ff0480ffff15ff17ffff0181ff8080ffff01ff0bff05ff0bff1780ffff01ff088080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ffff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080

View File

@ -1,18 +1,21 @@
import asyncio
import dataclasses
import pytest
import pytest_asyncio
from typing import AsyncIterator, Iterator
from typing import Any, AsyncIterator, Iterator
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.peer_info import PeerInfo
from chia.util.ints import uint16, uint32, uint64
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
from tests.setup_nodes import setup_simulators_and_wallets
from chia.data_layer.data_layer_wallet import DataLayerWallet
from chia.data_layer.data_layer_wallet import DataLayerWallet, Mirror
from chia.types.blockchain_format.program import Program
from tests.time_out_assert import time_out_assert
from chia.wallet.util.merkle_tree import MerkleTree
from chia.wallet.db_wallet.db_wallet_puzzles import create_mirror_puzzle
from tests.setup_nodes import SimulatorsAndWallets
@ -504,3 +507,70 @@ class TestDLWallet:
for tx in update_txs_0:
assert await wallet_node_0.wallet_state_manager.tx_store.get_transaction_record(tx.name) is None
assert await dl_wallet_0.get_singleton_record(record_0.coin_id) is None
async def is_singleton_confirmed_and_root(dl_wallet: DataLayerWallet, lid: bytes32, root: bytes32) -> bool:
rec = await dl_wallet.get_latest_singleton(lid)
if rec is None:
return False
if rec.confirmed is True:
assert rec.confirmed_at_height > 0
assert rec.timestamp > 0
return rec.confirmed and rec.root == root
@pytest.mark.parametrize(
"trusted",
[True, False],
)
@pytest.mark.asyncio
async def test_mirrors(wallets_prefarm: Any, trusted: bool) -> None:
wallet_node_1, wallet_node_2, full_node_api = wallets_prefarm
assert wallet_node_1.wallet_state_manager is not None
assert wallet_node_2.wallet_state_manager is not None
wsm_1 = wallet_node_1.wallet_state_manager
wsm_2 = wallet_node_2.wallet_state_manager
wallet_1 = wsm_1.main_wallet
wallet_2 = wsm_2.main_wallet
funds = 20000000000000
await time_out_assert(10, wallet_1.get_unconfirmed_balance, funds)
await time_out_assert(10, wallet_2.get_confirmed_balance, funds)
async with wsm_1.lock:
dl_wallet_1 = await DataLayerWallet.create_new_dl_wallet(wsm_1, wallet_1)
async with wsm_2.lock:
dl_wallet_2 = await DataLayerWallet.create_new_dl_wallet(wsm_2, wallet_2)
dl_record, std_record, launcher_id_1 = await dl_wallet_1.generate_new_reporter(bytes32([0]*32))
assert await dl_wallet_1.get_latest_singleton(launcher_id_1) is not None
await wsm_1.add_pending_transaction(dl_record)
await wsm_1.add_pending_transaction(std_record)
await full_node_api.process_transaction_records(records=[dl_record, std_record])
await time_out_assert(15, is_singleton_confirmed_and_root, True, dl_wallet_1, launcher_id_1, bytes32([0]*32))
dl_record, std_record, launcher_id_2 = await dl_wallet_2.generate_new_reporter(bytes32([0]*32))
assert await dl_wallet_2.get_latest_singleton(launcher_id_2) is not None
await wsm_2.add_pending_transaction(dl_record)
await wsm_2.add_pending_transaction(std_record)
await full_node_api.process_transaction_records(records=[dl_record, std_record])
await time_out_assert(15, is_singleton_confirmed_and_root, True, dl_wallet_2, launcher_id_2, bytes32([0]*32))
await dl_wallet_1.track_new_launcher_id(launcher_id_2)
await dl_wallet_2.track_new_launcher_id(launcher_id_1)
await time_out_assert(15, is_singleton_confirmed_and_root, True, dl_wallet_1, launcher_id_2, bytes32([0]*32))
await time_out_assert(15, is_singleton_confirmed_and_root, True, dl_wallet_2, launcher_id_1, bytes32([0]*32))
txs = await dl_wallet_1.create_new_mirror(launcher_id_2, uint64(1), [b"foo", b"bar"], fee=uint64(1999999999999))
additions: List[Coin] = []
for tx in txs:
if tx.spend_bundle is not None:
additions.extend(tx.spend_bundle.additions())
await wsm_1.add_pending_transaction(tx)
await full_node_api.process_transaction_records(records=txs)
mirror_coin: Coin = [c for c in additions if c.puzzle_hash == create_mirror_puzzle().get_tree_hash()][0]
mirror = Mirror(bytes32(mirror_coin.name()), bytes32(launcher_id_2), mirror_coin.amount, [b"foo", b"bar"], True)
await time_out_assert(15, dl_wallet_1.get_mirrors_for_launcher, [mirror], launcher_id_2)
await time_out_assert(15, dl_wallet_2.get_mirrors_for_launcher, [dataclasses.replace(mirror, ours=False)], launcher_id_2)