mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-20 16:08:51 +03:00
Mirror creation working
This commit is contained in:
parent
54b8e5ea06
commit
fcb359938c
@ -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 #
|
||||
##########
|
||||
|
@ -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
|
||||
|
@ -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")
|
1
chia/wallet/puzzles/p2_parent.clvm.hex
Normal file
1
chia/wallet/puzzles/p2_parent.clvm.hex
Normal file
@ -0,0 +1 @@
|
||||
ff02ffff01ff04ffff04ffff01934153534552545f4d595f504152454e545f4944ffff04ffff02ff0affff04ff02ffff04ff0bffff04ffff02ff0effff04ff02ffff04ffff02ff05ff1780ff80808080ffff04ff2fff808080808080ff808080ff8b03840d9b5c9f9cdc1e609f80ffff04ffff01ff20ffff02ffff03ffff22ffff09ffff0dff0580ff0480ffff09ffff0dff0b80ff0480ffff15ff17ffff0181ff8080ffff01ff0bff05ff0bff1780ffff01ff088080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ffff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080
|
@ -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)
|
Loading…
Reference in New Issue
Block a user