mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-21 08:31:52 +03:00
generate and transfer ported
This commit is contained in:
parent
0bb21abe38
commit
ccf16cb223
@ -95,7 +95,7 @@ jobs:
|
|||||||
- name: Test wallet-nft_wallet code with pytest
|
- name: Test wallet-nft_wallet code with pytest
|
||||||
run: |
|
run: |
|
||||||
. ./activate
|
. ./activate
|
||||||
venv/bin/coverage run --rcfile=.coveragerc --module pytest --durations=10 -n 0 -m "not benchmark" tests/wallet/nft_wallet/test_nft_clvm.py tests/wallet/nft_wallet/test_nft_offers.py tests/wallet/nft_wallet/test_nft_puzzles.py tests/wallet/nft_wallet/test_nft_wallet.py
|
venv/bin/coverage run --rcfile=.coveragerc --module pytest --durations=10 -n 0 -m "not benchmark" tests/wallet/nft_wallet/test_nft_clvm.py tests/wallet/nft_wallet/test_nft_lifecycle.py tests/wallet/nft_wallet/test_nft_offers.py tests/wallet/nft_wallet/test_nft_puzzles.py tests/wallet/nft_wallet/test_nft_wallet.py
|
||||||
|
|
||||||
- name: Process coverage data
|
- name: Process coverage data
|
||||||
run: |
|
run: |
|
||||||
|
@ -94,7 +94,7 @@ jobs:
|
|||||||
- name: Test wallet-nft_wallet code with pytest
|
- name: Test wallet-nft_wallet code with pytest
|
||||||
run: |
|
run: |
|
||||||
. ./activate
|
. ./activate
|
||||||
venv/bin/coverage run --rcfile=.coveragerc --module pytest --durations=10 -n 0 -m "not benchmark" tests/wallet/nft_wallet/test_nft_clvm.py tests/wallet/nft_wallet/test_nft_offers.py tests/wallet/nft_wallet/test_nft_puzzles.py tests/wallet/nft_wallet/test_nft_wallet.py
|
venv/bin/coverage run --rcfile=.coveragerc --module pytest --durations=10 -n 0 -m "not benchmark" tests/wallet/nft_wallet/test_nft_clvm.py tests/wallet/nft_wallet/test_nft_lifecycle.py tests/wallet/nft_wallet/test_nft_offers.py tests/wallet/nft_wallet/test_nft_puzzles.py tests/wallet/nft_wallet/test_nft_wallet.py
|
||||||
|
|
||||||
- name: Process coverage data
|
- name: Process coverage data
|
||||||
run: |
|
run: |
|
||||||
|
@ -26,9 +26,6 @@ class NFTInfo(Streamable):
|
|||||||
owner_did: Optional[bytes32]
|
owner_did: Optional[bytes32]
|
||||||
"""Owner DID"""
|
"""Owner DID"""
|
||||||
|
|
||||||
owner_pubkey: Optional[bytes]
|
|
||||||
"""Pubkey of the NFT owner"""
|
|
||||||
|
|
||||||
royalty_percentage: Optional[uint16]
|
royalty_percentage: Optional[uint16]
|
||||||
"""Percentage of the transaction fee paid to the author, e.g. 1000 = 1%"""
|
"""Percentage of the transaction fee paid to the author, e.g. 1000 = 1%"""
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from blspy import G1Element
|
|
||||||
from clvm.casts import int_from_bytes
|
from clvm.casts import int_from_bytes
|
||||||
from clvm_tools.binutils import disassemble
|
from clvm_tools.binutils import disassemble
|
||||||
|
|
||||||
@ -107,7 +106,6 @@ def get_nft_info_from_puzzle(nft_coin_info: NFTCoinInfo) -> NFTInfo:
|
|||||||
uncurried_nft.singleton_launcher_id,
|
uncurried_nft.singleton_launcher_id,
|
||||||
nft_coin_info.coin.name(),
|
nft_coin_info.coin.name(),
|
||||||
uncurried_nft.owner_did,
|
uncurried_nft.owner_did,
|
||||||
uncurried_nft.owner_pubkey,
|
|
||||||
uncurried_nft.trade_price_percentage,
|
uncurried_nft.trade_price_percentage,
|
||||||
uncurried_nft.royalty_address,
|
uncurried_nft.royalty_address,
|
||||||
data_uris,
|
data_uris,
|
||||||
@ -204,7 +202,7 @@ def create_ownership_layer_puzzle(
|
|||||||
log.debug(
|
log.debug(
|
||||||
"Creating ownership layer puzzle with NFT_ID: %s DID_ID: %s Royalty_Percentage: %d P2_puzzle: %s",
|
"Creating ownership layer puzzle with NFT_ID: %s DID_ID: %s Royalty_Percentage: %d P2_puzzle: %s",
|
||||||
nft_id.hex(),
|
nft_id.hex(),
|
||||||
did_id.hex(),
|
did_id,
|
||||||
percentage,
|
percentage,
|
||||||
p2_puzzle,
|
p2_puzzle,
|
||||||
)
|
)
|
||||||
@ -212,7 +210,6 @@ def create_ownership_layer_puzzle(
|
|||||||
if not royalty_puzzle_hash:
|
if not royalty_puzzle_hash:
|
||||||
royalty_puzzle_hash = p2_puzzle.get_tree_hash()
|
royalty_puzzle_hash = p2_puzzle.get_tree_hash()
|
||||||
transfer_program = NFT_TRANSFER_PROGRAM_DEFAULT.curry(
|
transfer_program = NFT_TRANSFER_PROGRAM_DEFAULT.curry(
|
||||||
STANDARD_PUZZLE_MOD.get_tree_hash(),
|
|
||||||
singleton_struct,
|
singleton_struct,
|
||||||
royalty_puzzle_hash,
|
royalty_puzzle_hash,
|
||||||
percentage,
|
percentage,
|
||||||
@ -221,9 +218,7 @@ def create_ownership_layer_puzzle(
|
|||||||
)
|
)
|
||||||
nft_inner_puzzle = p2_puzzle
|
nft_inner_puzzle = p2_puzzle
|
||||||
|
|
||||||
nft_ownership_layer_puzzle = NFT_OWNERSHIP_LAYER.curry(
|
nft_ownership_layer_puzzle = construct_ownership_layer(bytes32(did_id), transfer_program, nft_inner_puzzle)
|
||||||
NFT_OWNERSHIP_LAYER.get_tree_hash(), did_id, transfer_program, nft_inner_puzzle
|
|
||||||
)
|
|
||||||
return nft_ownership_layer_puzzle
|
return nft_ownership_layer_puzzle
|
||||||
|
|
||||||
|
|
||||||
@ -231,30 +226,28 @@ def create_ownership_layer_transfer_solution(
|
|||||||
new_did: bytes,
|
new_did: bytes,
|
||||||
new_did_inner_hash: bytes32,
|
new_did_inner_hash: bytes32,
|
||||||
trade_prices_list: List[List[int]],
|
trade_prices_list: List[List[int]],
|
||||||
new_pubkey: G1Element,
|
new_puzhash: bytes32,
|
||||||
) -> Program:
|
) -> Program:
|
||||||
log.debug(
|
log.debug(
|
||||||
"Creating a transfer solution with: DID:%s Inner_puzhash:%s trade_price:%s pubkey:%s",
|
"Creating a transfer solution with: DID:%s Inner_puzhash:%s trade_price:%s puzhash:%s",
|
||||||
new_did.hex(),
|
new_did.hex(),
|
||||||
new_did_inner_hash.hex(),
|
new_did_inner_hash.hex(),
|
||||||
str(trade_prices_list),
|
str(trade_prices_list),
|
||||||
new_pubkey,
|
new_puzhash.hex(),
|
||||||
)
|
)
|
||||||
puzhash = STANDARD_PUZZLE_MOD.curry(new_pubkey).get_tree_hash()
|
|
||||||
condition_list = [
|
condition_list = [
|
||||||
[
|
[
|
||||||
51,
|
51,
|
||||||
puzhash,
|
new_puzhash,
|
||||||
1,
|
1,
|
||||||
[puzhash],
|
[new_puzhash],
|
||||||
],
|
],
|
||||||
[-10, new_did, trade_prices_list, new_pubkey, [new_did_inner_hash]],
|
[-10, new_did, trade_prices_list, new_did_inner_hash],
|
||||||
]
|
]
|
||||||
log.debug("Condition list raw: %r", condition_list)
|
log.debug("Condition list raw: %r", condition_list)
|
||||||
solution = Program.to(
|
solution = Program.to(
|
||||||
[
|
[
|
||||||
[solution_for_conditions(condition_list)],
|
[solution_for_conditions(condition_list)],
|
||||||
1,
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
log.debug("Generated transfer solution: %s", solution)
|
log.debug("Generated transfer solution: %s", solution)
|
||||||
@ -285,35 +278,26 @@ def get_metadata_and_phs(unft: UncurriedNFT, puzzle: Program, solution: Serializ
|
|||||||
if puzhash_for_derivation is not None:
|
if puzhash_for_derivation is not None:
|
||||||
# ignore duplicated create coin conditions
|
# ignore duplicated create coin conditions
|
||||||
continue
|
continue
|
||||||
puzhash = bytes32(condition.rest().first().atom)
|
|
||||||
memo = bytes32(condition.as_python()[-1][0])
|
memo = bytes32(condition.as_python()[-1][0])
|
||||||
if memo != puzhash:
|
puzhash_for_derivation = memo
|
||||||
puzhash_for_derivation = memo
|
|
||||||
else:
|
|
||||||
puzhash_for_derivation = puzhash
|
|
||||||
log.debug("Got back puzhash from solution: %s", puzhash_for_derivation)
|
log.debug("Got back puzhash from solution: %s", puzhash_for_derivation)
|
||||||
assert puzhash_for_derivation
|
assert puzhash_for_derivation
|
||||||
return metadata, puzhash_for_derivation
|
return metadata, puzhash_for_derivation
|
||||||
|
|
||||||
|
|
||||||
def recurry_nft_puzzle(unft: UncurriedNFT, solution: Program) -> Program:
|
def recurry_nft_puzzle(unft: UncurriedNFT, solution: Program, sp2_puzzle: Program) -> Program:
|
||||||
log.debug("Generating NFT puzzle with ownership support: %s", solution)
|
log.debug("Generating NFT puzzle with ownership support: %s", disassemble(solution))
|
||||||
conditions = solution.at("frfr").as_iter()
|
conditions = solution.at("frfr").as_iter()
|
||||||
for change_did_condition in conditions:
|
new_did_id = None
|
||||||
if change_did_condition.first().as_int() == -10:
|
new_puzhash = None
|
||||||
|
for condition in conditions:
|
||||||
|
if condition.first().as_int() == -10:
|
||||||
# this is the change owner magic condition
|
# this is the change owner magic condition
|
||||||
break
|
new_did_id = condition.at("rf").atom
|
||||||
else:
|
elif condition.first().as_int() == 51:
|
||||||
raise ValueError("Not a valid puzzle")
|
new_puzhash = condition.at("rf").atom
|
||||||
new_did_id = change_did_condition.at("rf").atom
|
# assert new_puzhash and new_did_id
|
||||||
# trade_list_price = change_did_condition.at("rrf").as_python()
|
log.debug(f"Found NFT puzzle details: {new_did_id} {new_puzhash}")
|
||||||
# new_did_inner_hash = change_did_condition.at("rrrrf").atom
|
assert unft.transfer_program
|
||||||
new_pub_key = G1Element.from_bytes(change_did_condition.at("rrrf").atom)
|
inner_puzzle = construct_ownership_layer(new_did_id, unft.transfer_program, sp2_puzzle)
|
||||||
log.debug(f"Found NFT puzzle details: {new_did_id.hex()} ")
|
|
||||||
inner_puzzle = NFT_OWNERSHIP_LAYER.curry(
|
|
||||||
NFT_OWNERSHIP_LAYER.get_tree_hash(),
|
|
||||||
new_did_id,
|
|
||||||
unft.transfer_program,
|
|
||||||
STANDARD_PUZZLE_MOD.curry(new_pub_key),
|
|
||||||
)
|
|
||||||
return inner_puzzle
|
return inner_puzzle
|
||||||
|
@ -207,27 +207,21 @@ class NFTWallet:
|
|||||||
)
|
)
|
||||||
singleton_id = uncurried_nft.singleton_launcher_id
|
singleton_id = uncurried_nft.singleton_launcher_id
|
||||||
parent_inner_puzhash = uncurried_nft.nft_state_layer.get_tree_hash()
|
parent_inner_puzhash = uncurried_nft.nft_state_layer.get_tree_hash()
|
||||||
metadata, p2_puzzle_hash = get_metadata_and_phs(
|
metadata, p2_puzzle_hash = get_metadata_and_phs(uncurried_nft, puzzle, coin_spend.solution.to_program())
|
||||||
uncurried_nft, puzzle, Program.from_bytes(bytes(coin_spend.solution))
|
|
||||||
)
|
|
||||||
self.log.debug("Got back puzhash from solution: %s", p2_puzzle_hash)
|
self.log.debug("Got back puzhash from solution: %s", p2_puzzle_hash)
|
||||||
|
self.log.debug("Got back updated metadata: %s", metadata)
|
||||||
derivation_record: Optional[
|
derivation_record: Optional[
|
||||||
DerivationRecord
|
DerivationRecord
|
||||||
] = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(p2_puzzle_hash)
|
] = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(p2_puzzle_hash)
|
||||||
if derivation_record:
|
self.log.debug("Record for %s is: %s", p2_puzzle_hash, derivation_record)
|
||||||
p2_puzzle = puzzle_for_pk(derivation_record.pubkey)
|
if derivation_record is None:
|
||||||
else:
|
|
||||||
# we don't have this puzhash in puzzle store
|
|
||||||
# either it's not our coin or it's a NFT with a DID
|
|
||||||
p2_puzzle = None
|
|
||||||
self.log.debug("Got back updated metadata: %s", metadata)
|
|
||||||
if p2_puzzle is None and uncurried_nft.owner_pubkey is None:
|
|
||||||
self.log.info("Received a puzzle hash that is not ours, returning")
|
self.log.info("Received a puzzle hash that is not ours, returning")
|
||||||
# we transferred it to another wallet, remove the coin from our wallet
|
# we transferred it to another wallet, remove the coin from our wallet
|
||||||
await self.remove_coin(coin_spend.coin, in_transaction=in_transaction)
|
await self.remove_coin(coin_spend.coin, in_transaction=in_transaction)
|
||||||
return
|
return
|
||||||
if p2_puzzle is None:
|
p2_puzzle = puzzle_for_pk(derivation_record.pubkey)
|
||||||
inner_puzzle = nft_puzzles.recurry_nft_puzzle(uncurried_nft, delegated_puz_solution)
|
if uncurried_nft.supports_did:
|
||||||
|
inner_puzzle = nft_puzzles.recurry_nft_puzzle(uncurried_nft, delegated_puz_solution, p2_puzzle)
|
||||||
else:
|
else:
|
||||||
inner_puzzle = p2_puzzle
|
inner_puzzle = p2_puzzle
|
||||||
child_puzzle: Program = nft_puzzles.create_full_puzzle(
|
child_puzzle: Program = nft_puzzles.create_full_puzzle(
|
||||||
@ -318,9 +312,6 @@ class NFTWallet:
|
|||||||
def puzzle_for_pk(self, pk: G1Element) -> Program:
|
def puzzle_for_pk(self, pk: G1Element) -> Program:
|
||||||
inner_puzzle = self.standard_wallet.puzzle_for_pk(bytes(pk))
|
inner_puzzle = self.standard_wallet.puzzle_for_pk(bytes(pk))
|
||||||
provenance_puzzle = Program.to([NFT_STATE_LAYER_MOD_HASH, inner_puzzle])
|
provenance_puzzle = Program.to([NFT_STATE_LAYER_MOD_HASH, inner_puzzle])
|
||||||
self.log.debug(
|
|
||||||
"Wallet name %s generated a puzzle: %s", self.wallet_info.name, provenance_puzzle.get_tree_hash()
|
|
||||||
)
|
|
||||||
return provenance_puzzle
|
return provenance_puzzle
|
||||||
|
|
||||||
async def get_did_approval_info(
|
async def get_did_approval_info(
|
||||||
@ -426,17 +417,8 @@ class NFTWallet:
|
|||||||
record: Optional[DerivationRecord] = None
|
record: Optional[DerivationRecord] = None
|
||||||
# Create inner solution for eve spend
|
# Create inner solution for eve spend
|
||||||
if did_id is not None:
|
if did_id is not None:
|
||||||
record = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
|
|
||||||
p2_inner_puzzle.get_tree_hash()
|
|
||||||
)
|
|
||||||
self.log.debug("Got back a pubkey record: %s", record)
|
|
||||||
if not record:
|
|
||||||
record = await self.wallet_state_manager.get_unused_derivation_record(self.id(), False)
|
|
||||||
assert record
|
|
||||||
did_inner_hash, did_bundle = await self.get_did_approval_info(launcher_coin.name())
|
did_inner_hash, did_bundle = await self.get_did_approval_info(launcher_coin.name())
|
||||||
pubkey = record.pubkey
|
innersol = create_ownership_layer_transfer_solution(did_id, did_inner_hash, [], target_puzzle_hash)
|
||||||
self.log.debug("Going to use this pubkey for NFT mint: %s", pubkey)
|
|
||||||
innersol = create_ownership_layer_transfer_solution(did_id, did_inner_hash, [], pubkey)
|
|
||||||
bundles_to_agg.append(did_bundle)
|
bundles_to_agg.append(did_bundle)
|
||||||
|
|
||||||
self.log.debug("Created an inner DID NFT solution: %s", disassemble(innersol))
|
self.log.debug("Created an inner DID NFT solution: %s", disassemble(innersol))
|
||||||
@ -593,7 +575,7 @@ class NFTWallet:
|
|||||||
self.log.info(
|
self.log.info(
|
||||||
"Attempting to add urls to NFT coin %s in the metadata: %s", nft_coin_info, uncurried_nft.metadata
|
"Attempting to add urls to NFT coin %s in the metadata: %s", nft_coin_info, uncurried_nft.metadata
|
||||||
)
|
)
|
||||||
inner_solution = Program.to([solution_for_conditions(condition_list), 1])
|
inner_solution = Program.to([solution_for_conditions(condition_list)])
|
||||||
nft_tx_record = await self._make_nft_transaction(nft_coin_info, inner_solution, [puzzle_hash], fee)
|
nft_tx_record = await self._make_nft_transaction(nft_coin_info, inner_solution, [puzzle_hash], fee)
|
||||||
await self.standard_wallet.push_transaction(nft_tx_record)
|
await self.standard_wallet.push_transaction(nft_tx_record)
|
||||||
self.wallet_state_manager.state_changed("nft_coin_updated", self.wallet_info.id)
|
self.wallet_state_manager.state_changed("nft_coin_updated", self.wallet_info.id)
|
||||||
@ -605,16 +587,20 @@ class NFTWallet:
|
|||||||
puzzle_hash: bytes32,
|
puzzle_hash: bytes32,
|
||||||
fee: uint64 = uint64(0),
|
fee: uint64 = uint64(0),
|
||||||
) -> Optional[SpendBundle]:
|
) -> Optional[SpendBundle]:
|
||||||
self.log.debug("Attempt to transfer a new NFT")
|
self.log.info("Attempt to transfer a new NFT")
|
||||||
coin = nft_coin_info.coin
|
coin = nft_coin_info.coin
|
||||||
self.log.debug("Transferring NFT coin %r to puzhash: %s", nft_coin_info, puzzle_hash)
|
self.log.error("Transferring NFT coin %r to puzhash: %s", nft_coin_info.coin, puzzle_hash)
|
||||||
|
|
||||||
amount = coin.amount
|
amount = coin.amount
|
||||||
unft = UncurriedNFT.uncurry(nft_coin_info.full_puzzle)
|
unft = UncurriedNFT.uncurry(nft_coin_info.full_puzzle)
|
||||||
puzzle_hash_to_sign = unft.inner_puzzle.get_tree_hash()
|
puzzle_hash_to_sign = unft.p2_puzzle.get_tree_hash()
|
||||||
condition_list = [make_create_coin_condition(puzzle_hash, amount, [puzzle_hash])]
|
if unft.supports_did:
|
||||||
inner_solution = Program.to([solution_for_conditions(condition_list), amount])
|
self.log.error("Transferring NFT with ownership layer")
|
||||||
self.log.debug("Solution for new coin: %r", disassemble(inner_solution))
|
inner_solution = create_ownership_layer_transfer_solution(int_to_bytes(0), int_to_bytes(0), [], puzzle_hash)
|
||||||
|
else:
|
||||||
|
condition_list = [make_create_coin_condition(puzzle_hash, amount, [puzzle_hash])]
|
||||||
|
inner_solution = Program.to([solution_for_conditions(condition_list), amount])
|
||||||
|
self.log.error("Solution for new coin: %r", disassemble(inner_solution))
|
||||||
nft_tx_record = await self._make_nft_transaction(
|
nft_tx_record = await self._make_nft_transaction(
|
||||||
nft_coin_info,
|
nft_coin_info,
|
||||||
inner_solution,
|
inner_solution,
|
||||||
|
@ -4,8 +4,6 @@ import logging
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional, Type, TypeVar
|
from typing import Optional, Type, TypeVar
|
||||||
|
|
||||||
from blspy import G1Element
|
|
||||||
|
|
||||||
from chia.types.blockchain_format.program import Program
|
from chia.types.blockchain_format.program import Program
|
||||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||||
from chia.util.ints import uint16
|
from chia.util.ints import uint16
|
||||||
@ -72,9 +70,6 @@ class UncurriedNFT:
|
|||||||
supports_did: bool
|
supports_did: bool
|
||||||
"""If the inner puzzle support the DID"""
|
"""If the inner puzzle support the DID"""
|
||||||
|
|
||||||
owner_pubkey: Optional[G1Element]
|
|
||||||
"""Owner's Pubkey in the P2 puzzle"""
|
|
||||||
|
|
||||||
nft_inner_puzzle_hash: Optional[bytes32]
|
nft_inner_puzzle_hash: Optional[bytes32]
|
||||||
"""Puzzle hash of the ownership layer inner puzzle """
|
"""Puzzle hash of the ownership layer inner puzzle """
|
||||||
|
|
||||||
@ -141,7 +136,6 @@ class UncurriedNFT:
|
|||||||
if kv_pair.first().as_atom() == b"st":
|
if kv_pair.first().as_atom() == b"st":
|
||||||
series_total = kv_pair.rest()
|
series_total = kv_pair.rest()
|
||||||
current_did = None
|
current_did = None
|
||||||
pubkey = None
|
|
||||||
transfer_program = None
|
transfer_program = None
|
||||||
transfer_program_args = None
|
transfer_program_args = None
|
||||||
royalty_address = None
|
royalty_address = None
|
||||||
@ -153,17 +147,14 @@ class UncurriedNFT:
|
|||||||
supports_did = True
|
supports_did = True
|
||||||
log.debug("Parsing ownership layer")
|
log.debug("Parsing ownership layer")
|
||||||
_, current_did, transfer_program, p2_puzzle = ol_args.as_iter()
|
_, current_did, transfer_program, p2_puzzle = ol_args.as_iter()
|
||||||
_, p2_args = p2_puzzle.uncurry()
|
|
||||||
(pubkey_sexp,) = p2_args.as_iter()
|
|
||||||
transfer_program_mod, transfer_program_args = transfer_program.uncurry()
|
transfer_program_mod, transfer_program_args = transfer_program.uncurry()
|
||||||
_, _, royalty_address_p, royalty_percentage, _, _ = transfer_program_args.as_iter()
|
_, royalty_address_p, royalty_percentage, _, _ = transfer_program_args.as_iter()
|
||||||
royalty_percentage = uint16(royalty_percentage.as_int())
|
royalty_percentage = uint16(royalty_percentage.as_int())
|
||||||
royalty_address = royalty_address_p.atom
|
royalty_address = royalty_address_p.atom
|
||||||
current_did = current_did.atom
|
current_did = current_did.atom
|
||||||
if current_did == b"":
|
if current_did == b"":
|
||||||
# For unassigned NFT, set owner DID to None
|
# For unassigned NFT, set owner DID to None
|
||||||
current_did = None
|
current_did = None
|
||||||
pubkey = pubkey_sexp.atom
|
|
||||||
else:
|
else:
|
||||||
log.debug("Creating a standard NFT puzzle")
|
log.debug("Creating a standard NFT puzzle")
|
||||||
p2_puzzle = inner_puzzle
|
p2_puzzle = inner_puzzle
|
||||||
@ -190,7 +181,6 @@ class UncurriedNFT:
|
|||||||
inner_puzzle=inner_puzzle,
|
inner_puzzle=inner_puzzle,
|
||||||
owner_did=current_did,
|
owner_did=current_did,
|
||||||
supports_did=supports_did,
|
supports_did=supports_did,
|
||||||
owner_pubkey=pubkey,
|
|
||||||
transfer_program=transfer_program,
|
transfer_program=transfer_program,
|
||||||
transfer_program_curry_params=transfer_program_args,
|
transfer_program_curry_params=transfer_program_args,
|
||||||
royalty_address=royalty_address,
|
royalty_address=royalty_address,
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
(c
|
(c
|
||||||
CREATE_COIN
|
CREATE_COIN
|
||||||
(c
|
(c
|
||||||
(nft_ownership_layer_puzzle_hash NFT_OWNERSHIP_LAYER_MOD_HASH (f tp_output) (f (r tp_output)) (f odd_args))
|
(nft_ownership_layer_puzzle_hash NFT_OWNERSHIP_LAYER_MOD_HASH (f tp_output) (if (f (r tp_output)) (f (r tp_output)) TRANSFER_PROGRAM) (f odd_args))
|
||||||
(r odd_args)
|
(r odd_args)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1 +1 @@
|
|||||||
ff02ffff01ff02ff26ffff04ff02ffff04ff05ffff04ff17ffff04ff0bffff04ffff02ff2fff5f80ff80808080808080ffff04ffff01ffffff88ad4cd55cf7ad6414ff0233ffff3e04ff81f601ffff01ff02ff02ffff03ff05ffff01ff02ff3affff04ff02ffff04ff0dffff04ffff0bff2affff0bff3cff3480ffff0bff2affff0bff2affff0bff3cff1280ff0980ffff0bff2aff0bffff0bff3cff8080808080ff8080808080ffff010b80ff0180ffffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff2fffff01ff80ff80ff80ff80ff808080808080808080ff0bff2affff0bff3cff2880ffff0bff2affff0bff2affff0bff3cff1280ff0580ffff0bff2affff02ff3affff04ff02ffff04ff07ffff04ffff0bff3cff3c80ff8080808080ffff0bff3cff8080808080ffff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff2effff04ff02ffff04ff09ff80808080ffff02ff2effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff02ffff03ff5fffff01ff02ffff03ffff09ff82011fff3880ffff01ff02ffff03ffff09ffff18ff82059f80ff3c80ffff01ff02ffff03ffff20ff81bf80ffff01ff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ffff0101ffff04ff82017fffff04ff82019fffff04ff8205ffff808080808080808080808080ffff01ff088080ff0180ffff01ff04ff819fffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ff81bfffff04ff82017fffff04ff8202ffffff04ff8205ffff8080808080808080808080808080ff0180ffff01ff02ffff03ffff09ff82011fff2c80ffff01ff02ffff03ffff20ff82017f80ffff01ff04ffff04ff24ffff04ffff0eff10ffff02ff2effff04ff02ffff04ff82019fff8080808080ff808080ffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ff81bfffff04ffff0101ffff04ff8202ffffff04ffff02ff0bffff04ff17ffff04ff2fffff04ff82019fff8080808080ff80808080808080808080808080ffff01ff088080ff0180ffff01ff02ffff03ffff09ff82011fff2480ffff01ff02ffff03ffff22ffff20ffff09ffff0cff82029fff80ffff010880ff108080ffff09ffff0128ffff0dff82029f808080ffff01ff04ff819fffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ff81bfffff04ff82017fffff04ff8202ffffff04ff8205ffff80808080808080808080808080ffff01ff088080ff0180ffff01ff04ff819fffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ff81bfffff04ff82017fffff04ff8202ffffff04ff8205ffff8080808080808080808080808080ff018080ff018080ff0180ffff01ff02ffff03ff81bfffff01ff02ffff03ff82017fffff01ff04ffff04ff38ffff04ffff02ff36ffff04ff02ffff04ff05ffff04ff8204ffffff04ffff02ff2effff04ff02ffff04ff8215ffff80808080ffff04ffff0bff3cff8209ff80ffff04ffff0bff3cff0580ff8080808080808080ff8206ff8080ff822dff80ffff01ff088080ff0180ffff01ff088080ff018080ff0180ff018080
|
ff02ffff01ff02ff26ffff04ff02ffff04ff05ffff04ff17ffff04ff0bffff04ffff02ff2fff5f80ff80808080808080ffff04ffff01ffffff88ad4cd55cf7ad6414ff0233ffff3e04ff81f601ffff01ff02ff02ffff03ff05ffff01ff02ff3affff04ff02ffff04ff0dffff04ffff0bff2affff0bff3cff3480ffff0bff2affff0bff2affff0bff3cff1280ff0980ffff0bff2aff0bffff0bff3cff8080808080ff8080808080ffff010b80ff0180ffffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff2fffff01ff80ff80ff80ff80ff808080808080808080ff0bff2affff0bff3cff2880ffff0bff2affff0bff2affff0bff3cff1280ff0580ffff0bff2affff02ff3affff04ff02ffff04ff07ffff04ffff0bff3cff3c80ff8080808080ffff0bff3cff8080808080ffff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff2effff04ff02ffff04ff09ff80808080ffff02ff2effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff02ffff03ff5fffff01ff02ffff03ffff09ff82011fff3880ffff01ff02ffff03ffff09ffff18ff82059f80ff3c80ffff01ff02ffff03ffff20ff81bf80ffff01ff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ffff0101ffff04ff82017fffff04ff82019fffff04ff8205ffff808080808080808080808080ffff01ff088080ff0180ffff01ff04ff819fffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ff81bfffff04ff82017fffff04ff8202ffffff04ff8205ffff8080808080808080808080808080ff0180ffff01ff02ffff03ffff09ff82011fff2c80ffff01ff02ffff03ffff20ff82017f80ffff01ff04ffff04ff24ffff04ffff0eff10ffff02ff2effff04ff02ffff04ff82019fff8080808080ff808080ffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ff81bfffff04ffff0101ffff04ff8202ffffff04ffff02ff0bffff04ff17ffff04ff2fffff04ff82019fff8080808080ff80808080808080808080808080ffff01ff088080ff0180ffff01ff02ffff03ffff09ff82011fff2480ffff01ff02ffff03ffff22ffff20ffff09ffff0cff82029fff80ffff010880ff108080ffff09ffff0128ffff0dff82029f808080ffff01ff04ff819fffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ff81bfffff04ff82017fffff04ff8202ffffff04ff8205ffff80808080808080808080808080ffff01ff088080ff0180ffff01ff04ff819fffff02ff3effff04ff02ffff04ff05ffff04ff0bffff04ff17ffff04ff2fffff04ff81dfffff04ff81bfffff04ff82017fffff04ff8202ffffff04ff8205ffff8080808080808080808080808080ff018080ff018080ff0180ffff01ff02ffff03ff81bfffff01ff02ffff03ff82017fffff01ff04ffff04ff38ffff04ffff02ff36ffff04ff02ffff04ff05ffff04ff8204ffffff04ffff02ff2effff04ff02ffff04ffff02ffff03ff8215ffffff018215ffffff010b80ff0180ff80808080ffff04ffff0bff3cff8209ff80ffff04ffff0bff3cff0580ff8080808080808080ff8206ff8080ff822dff80ffff01ff088080ff0180ffff01ff088080ff018080ff0180ff018080
|
@ -1,10 +1,10 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import pytest
|
|
||||||
|
|
||||||
from blspy import G2Element
|
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
from chia.clvm.spend_sim import SpendSim, SimClient
|
import pytest
|
||||||
|
from blspy import G2Element
|
||||||
|
|
||||||
|
from chia.clvm.spend_sim import SimClient, SpendSim
|
||||||
from chia.types.announcement import Announcement
|
from chia.types.announcement import Announcement
|
||||||
from chia.types.blockchain_format.program import Program
|
from chia.types.blockchain_format.program import Program
|
||||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||||
@ -13,11 +13,11 @@ from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
|||||||
from chia.types.spend_bundle import SpendBundle
|
from chia.types.spend_bundle import SpendBundle
|
||||||
from chia.util.errors import Err
|
from chia.util.errors import Err
|
||||||
from chia.wallet.nft_wallet.nft_puzzles import (
|
from chia.wallet.nft_wallet.nft_puzzles import (
|
||||||
create_nft_layer_puzzle_with_curry_params,
|
|
||||||
metadata_to_program,
|
|
||||||
NFT_METADATA_UPDATER,
|
NFT_METADATA_UPDATER,
|
||||||
NFT_TRANSFER_PROGRAM_DEFAULT,
|
NFT_TRANSFER_PROGRAM_DEFAULT,
|
||||||
construct_ownership_layer,
|
construct_ownership_layer,
|
||||||
|
create_nft_layer_puzzle_with_curry_params,
|
||||||
|
metadata_to_program,
|
||||||
)
|
)
|
||||||
|
|
||||||
ACS = Program.to(1)
|
ACS = Program.to(1)
|
||||||
|
@ -7,6 +7,7 @@ from chia.types.blockchain_format.program import Program
|
|||||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||||
from chia.wallet.nft_wallet import uncurry_nft
|
from chia.wallet.nft_wallet import uncurry_nft
|
||||||
from chia.wallet.nft_wallet.nft_puzzles import (
|
from chia.wallet.nft_wallet.nft_puzzles import (
|
||||||
|
construct_ownership_layer,
|
||||||
create_full_puzzle,
|
create_full_puzzle,
|
||||||
create_nft_layer_puzzle_with_curry_params,
|
create_nft_layer_puzzle_with_curry_params,
|
||||||
recurry_nft_puzzle,
|
recurry_nft_puzzle,
|
||||||
@ -22,7 +23,6 @@ DID_MOD = load_clvm("did_innerpuz.clvm")
|
|||||||
NFT_STATE_LAYER_MOD = load_clvm("nft_state_layer.clvm")
|
NFT_STATE_LAYER_MOD = load_clvm("nft_state_layer.clvm")
|
||||||
NFT_OWNERSHIP_LAYER = load_clvm("nft_ownership_layer.clvm")
|
NFT_OWNERSHIP_LAYER = load_clvm("nft_ownership_layer.clvm")
|
||||||
NFT_TRANSFER_PROGRAM_DEFAULT = load_clvm("nft_ownership_transfer_program_one_way_claim_with_royalties.clvm")
|
NFT_TRANSFER_PROGRAM_DEFAULT = load_clvm("nft_ownership_transfer_program_one_way_claim_with_royalties.clvm")
|
||||||
STANDARD_PUZZLE_MOD = load_clvm("p2_delegated_puzzle_or_hidden_puzzle.clvm")
|
|
||||||
LAUNCHER_PUZZLE_HASH = LAUNCHER_PUZZLE.get_tree_hash()
|
LAUNCHER_PUZZLE_HASH = LAUNCHER_PUZZLE.get_tree_hash()
|
||||||
NFT_STATE_LAYER_MOD_HASH = NFT_STATE_LAYER_MOD.get_tree_hash()
|
NFT_STATE_LAYER_MOD_HASH = NFT_STATE_LAYER_MOD.get_tree_hash()
|
||||||
SINGLETON_MOD_HASH = SINGLETON_MOD.get_tree_hash()
|
SINGLETON_MOD_HASH = SINGLETON_MOD.get_tree_hash()
|
||||||
@ -32,30 +32,30 @@ LAUNCHER_ID = Program.to(b"launcher-id").get_tree_hash()
|
|||||||
NFT_METADATA_UPDATER_DEFAULT = load_clvm("nft_metadata_updater_default.clvm")
|
NFT_METADATA_UPDATER_DEFAULT = load_clvm("nft_metadata_updater_default.clvm")
|
||||||
|
|
||||||
|
|
||||||
def make_a_new_solution() -> Tuple[bytes, Program]:
|
def make_a_new_solution() -> Tuple[Program, Program]:
|
||||||
destination = int_to_public_key(2)
|
destination = int_to_public_key(2)
|
||||||
|
p2_puzzle = puzzle_for_pk(destination)
|
||||||
|
puzhash = p2_puzzle.get_tree_hash()
|
||||||
new_did = Program.to("test").get_tree_hash()
|
new_did = Program.to("test").get_tree_hash()
|
||||||
|
print(f"NEW DID: {new_did.hex()} {puzhash.hex()}")
|
||||||
new_did_inner_hash = Program.to("fake").get_tree_hash()
|
new_did_inner_hash = Program.to("fake").get_tree_hash()
|
||||||
trade_prices_list = [[200]]
|
trade_prices_list = [[200]]
|
||||||
my_amount = 1
|
|
||||||
|
|
||||||
condition_list = [
|
condition_list = [
|
||||||
[
|
[
|
||||||
51,
|
51,
|
||||||
STANDARD_PUZZLE_MOD.curry(destination).get_tree_hash(),
|
puzhash,
|
||||||
1,
|
1,
|
||||||
[STANDARD_PUZZLE_MOD.curry(destination).get_tree_hash()],
|
[puzhash],
|
||||||
],
|
],
|
||||||
[-10, new_did, trade_prices_list, destination, [new_did_inner_hash]],
|
[-10, new_did, trade_prices_list, new_did_inner_hash],
|
||||||
]
|
]
|
||||||
solution = Program.to(
|
solution = Program.to(
|
||||||
[
|
[
|
||||||
[solution_for_conditions(condition_list)],
|
[solution_for_conditions(condition_list)],
|
||||||
my_amount,
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
print(disassemble(solution))
|
return p2_puzzle, solution
|
||||||
return destination, solution
|
|
||||||
|
|
||||||
|
|
||||||
def make_a_new_ownership_layer_puzzle() -> Tuple[Program, Program]:
|
def make_a_new_ownership_layer_puzzle() -> Tuple[Program, Program]:
|
||||||
@ -65,20 +65,14 @@ def make_a_new_ownership_layer_puzzle() -> Tuple[Program, Program]:
|
|||||||
nft_id = Program.to("nft_id")
|
nft_id = Program.to("nft_id")
|
||||||
SINGLETON_STRUCT = Program.to((SINGLETON_MOD_HASH, (nft_id, LAUNCHER_PUZZLE_HASH)))
|
SINGLETON_STRUCT = Program.to((SINGLETON_MOD_HASH, (nft_id, LAUNCHER_PUZZLE_HASH)))
|
||||||
curried_tp = NFT_TRANSFER_PROGRAM_DEFAULT.curry(
|
curried_tp = NFT_TRANSFER_PROGRAM_DEFAULT.curry(
|
||||||
STANDARD_PUZZLE_MOD.get_tree_hash(),
|
|
||||||
SINGLETON_STRUCT,
|
SINGLETON_STRUCT,
|
||||||
innerpuz.get_tree_hash(),
|
innerpuz.get_tree_hash(),
|
||||||
2000,
|
2000,
|
||||||
OFFER_MOD.get_tree_hash(),
|
OFFER_MOD.get_tree_hash(),
|
||||||
CAT_MOD.get_tree_hash(),
|
CAT_MOD.get_tree_hash(),
|
||||||
)
|
)
|
||||||
curried_inner = STANDARD_PUZZLE_MOD.curry(pubkey)
|
curried_inner = innerpuz
|
||||||
curried_ownership_layer = NFT_OWNERSHIP_LAYER.curry(
|
curried_ownership_layer = construct_ownership_layer(old_did, curried_tp, curried_inner)
|
||||||
NFT_OWNERSHIP_LAYER.get_tree_hash(),
|
|
||||||
old_did,
|
|
||||||
curried_tp,
|
|
||||||
curried_inner,
|
|
||||||
)
|
|
||||||
return innerpuz, curried_ownership_layer
|
return innerpuz, curried_ownership_layer
|
||||||
|
|
||||||
|
|
||||||
@ -107,10 +101,11 @@ def test_transfer_puzzle_builder() -> None:
|
|||||||
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
|
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
|
||||||
("h", 0xD4584AD463139FA8C0D9F68F4B59F185),
|
("h", 0xD4584AD463139FA8C0D9F68F4B59F185),
|
||||||
]
|
]
|
||||||
destination, solution = make_a_new_solution()
|
sp2_puzzle, solution = make_a_new_solution()
|
||||||
p2_puzzle, ownership_puzzle = make_a_new_ownership_layer_puzzle()
|
p2_puzzle, ownership_puzzle = make_a_new_ownership_layer_puzzle()
|
||||||
clvm_nft_puzzle = make_a_new_nft_puzzle(ownership_puzzle, Program.to(metadata))
|
clvm_nft_puzzle = create_nft_layer_puzzle_with_curry_params(
|
||||||
print("NFT state layer: %r" % clvm_nft_puzzle.get_tree_hash())
|
Program.to(metadata), NFT_METADATA_UPDATER_DEFAULT.get_tree_hash(), ownership_puzzle
|
||||||
|
)
|
||||||
puzzle = create_full_puzzle(
|
puzzle = create_full_puzzle(
|
||||||
Program.to(["singleton_id"]).get_tree_hash(),
|
Program.to(["singleton_id"]).get_tree_hash(),
|
||||||
Program.to(metadata),
|
Program.to(metadata),
|
||||||
@ -119,10 +114,11 @@ def test_transfer_puzzle_builder() -> None:
|
|||||||
)
|
)
|
||||||
clvm_puzzle_hash = get_updated_nft_puzzle(clvm_nft_puzzle, solution)
|
clvm_puzzle_hash = get_updated_nft_puzzle(clvm_nft_puzzle, solution)
|
||||||
unft = uncurry_nft.UncurriedNFT.uncurry(puzzle)
|
unft = uncurry_nft.UncurriedNFT.uncurry(puzzle)
|
||||||
|
assert unft.nft_state_layer == clvm_nft_puzzle
|
||||||
assert unft.inner_puzzle == ownership_puzzle
|
assert unft.inner_puzzle == ownership_puzzle
|
||||||
|
assert unft.p2_puzzle == p2_puzzle
|
||||||
ol_puzzle = recurry_nft_puzzle(unft, solution.first())
|
ol_puzzle = recurry_nft_puzzle(unft, solution.first(), sp2_puzzle)
|
||||||
py_puzzle = create_nft_layer_puzzle_with_curry_params(
|
nft_puzzle = create_nft_layer_puzzle_with_curry_params(
|
||||||
Program.to(metadata), NFT_METADATA_UPDATER_DEFAULT.get_tree_hash(), ol_puzzle
|
Program.to(metadata), NFT_METADATA_UPDATER_DEFAULT.get_tree_hash(), ol_puzzle
|
||||||
)
|
)
|
||||||
assert clvm_puzzle_hash == py_puzzle.get_tree_hash()
|
assert clvm_puzzle_hash == nft_puzzle.get_tree_hash()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -12,6 +13,7 @@ from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
|||||||
from chia.types.blockchain_format.program import Program
|
from chia.types.blockchain_format.program import Program
|
||||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||||
from chia.types.peer_info import PeerInfo
|
from chia.types.peer_info import PeerInfo
|
||||||
|
from chia.util.bech32m import encode_puzzle_hash
|
||||||
from chia.util.byte_types import hexstr_to_bytes
|
from chia.util.byte_types import hexstr_to_bytes
|
||||||
from chia.util.ints import uint16, uint32, uint64
|
from chia.util.ints import uint16, uint32, uint64
|
||||||
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
||||||
@ -20,6 +22,8 @@ from chia.wallet.util.compute_memos import compute_memos
|
|||||||
from chia.wallet.util.wallet_types import WalletType
|
from chia.wallet.util.wallet_types import WalletType
|
||||||
from tests.time_out_assert import time_out_assert, time_out_assert_not_none
|
from tests.time_out_assert import time_out_assert, time_out_assert_not_none
|
||||||
|
|
||||||
|
logging.getLogger("aiosqlite").setLevel(logging.INFO) # Too much logging on debug level
|
||||||
|
|
||||||
|
|
||||||
async def tx_in_pool(mempool: MempoolManager, tx_id: bytes32) -> bool:
|
async def tx_in_pool(mempool: MempoolManager, tx_id: bytes32) -> bool:
|
||||||
tx = mempool.get_spendbundle(tx_id)
|
tx = mempool.get_spendbundle(tx_id)
|
||||||
@ -654,7 +658,6 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
|
|||||||
assert did_nft["data_uris"][0] == "https://www.chia.net/img/branding/chia-logo.svg"
|
assert did_nft["data_uris"][0] == "https://www.chia.net/img/branding/chia-logo.svg"
|
||||||
assert did_nft["data_hash"] == "0xD4584AD463139FA8C0D9F68F4B59F185".lower()
|
assert did_nft["data_hash"] == "0xD4584AD463139FA8C0D9F68F4B59F185".lower()
|
||||||
assert did_nft["owner_did"][2:] == hex_did_id
|
assert did_nft["owner_did"][2:] == hex_did_id
|
||||||
assert did_nft["owner_pubkey"] is not None
|
|
||||||
# Check unassigned NFT
|
# Check unassigned NFT
|
||||||
nft_wallets = await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
|
nft_wallets = await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
|
||||||
assert len(nft_wallets) == 2
|
assert len(nft_wallets) == 2
|
||||||
@ -669,7 +672,6 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
|
|||||||
assert non_did_nft["data_uris"][0] == "https://url1"
|
assert non_did_nft["data_uris"][0] == "https://url1"
|
||||||
assert non_did_nft["data_hash"] == "0xD4584AD463139FA8C0D9F68F4B59F181".lower()
|
assert non_did_nft["data_hash"] == "0xD4584AD463139FA8C0D9F68F4B59F181".lower()
|
||||||
assert non_did_nft["owner_did"] is None
|
assert non_did_nft["owner_did"] is None
|
||||||
assert non_did_nft["owner_pubkey"] is not None
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -792,3 +794,128 @@ async def test_nft_rpc_mint(two_wallet_nodes: Any, trusted: Any) -> None:
|
|||||||
assert did_nft.series_total == st
|
assert did_nft.series_total == st
|
||||||
assert did_nft.series_number == sn
|
assert did_nft.series_number == sn
|
||||||
assert did_nft.royalty_percentage == royalty_percentage
|
assert did_nft.royalty_percentage == royalty_percentage
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"trusted",
|
||||||
|
[True, False],
|
||||||
|
)
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_nft_transfer_nft_with_did(two_wallet_nodes: Any, trusted: Any) -> None:
|
||||||
|
num_blocks = 3
|
||||||
|
full_nodes, wallets = two_wallet_nodes
|
||||||
|
full_node_api: FullNodeSimulator = full_nodes[0]
|
||||||
|
full_node_server = full_node_api.server
|
||||||
|
wallet_node_0, server_0 = wallets[0]
|
||||||
|
wallet_node_1, server_1 = wallets[1]
|
||||||
|
wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
|
||||||
|
wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
|
||||||
|
api_0 = WalletRpcApi(wallet_node_0)
|
||||||
|
ph = await wallet_0.get_new_puzzlehash()
|
||||||
|
ph1 = await wallet_1.get_new_puzzlehash()
|
||||||
|
|
||||||
|
if trusted:
|
||||||
|
wallet_node_0.config["trusted_peers"] = {
|
||||||
|
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
|
||||||
|
}
|
||||||
|
wallet_node_1.config["trusted_peers"] = {
|
||||||
|
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
wallet_node_0.config["trusted_peers"] = {}
|
||||||
|
wallet_node_1.config["trusted_peers"] = {}
|
||||||
|
|
||||||
|
await server_0.start_client(PeerInfo("localhost", uint16(full_node_server._port)), None)
|
||||||
|
await server_1.start_client(PeerInfo("localhost", uint16(full_node_server._port)), None)
|
||||||
|
|
||||||
|
for _ in range(1, num_blocks):
|
||||||
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||||
|
|
||||||
|
funds = sum(
|
||||||
|
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)]
|
||||||
|
)
|
||||||
|
|
||||||
|
await time_out_assert(10, wallet_0.get_unconfirmed_balance, funds)
|
||||||
|
await time_out_assert(10, wallet_0.get_confirmed_balance, funds)
|
||||||
|
did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
|
||||||
|
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
||||||
|
)
|
||||||
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(wallet_0.id())
|
||||||
|
|
||||||
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
||||||
|
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
||||||
|
|
||||||
|
for _ in range(1, num_blocks):
|
||||||
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||||
|
await time_out_assert(15, wallet_0.get_pending_change_balance, 0)
|
||||||
|
hex_did_id = did_wallet.get_my_DID()
|
||||||
|
|
||||||
|
res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hex_did_id))
|
||||||
|
assert isinstance(res, dict)
|
||||||
|
assert res.get("success")
|
||||||
|
nft_wallet_0_id = res["wallet_id"]
|
||||||
|
|
||||||
|
# Create a NFT with DID
|
||||||
|
resp = await api_0.nft_mint_nft(
|
||||||
|
{
|
||||||
|
"wallet_id": nft_wallet_0_id,
|
||||||
|
"hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
|
||||||
|
"uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert resp.get("success")
|
||||||
|
sb = resp["spend_bundle"]
|
||||||
|
|
||||||
|
# ensure hints are generated
|
||||||
|
assert compute_memos(sb)
|
||||||
|
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
||||||
|
|
||||||
|
for i in range(1, num_blocks):
|
||||||
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||||
|
|
||||||
|
# Check DID NFT
|
||||||
|
time_left = 5.0
|
||||||
|
coins_response = {}
|
||||||
|
while time_left > 0:
|
||||||
|
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
|
||||||
|
if coins_response.get("nft_list"):
|
||||||
|
break
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
time_left -= 0.5
|
||||||
|
assert coins_response["nft_list"], isinstance(coins_response, dict)
|
||||||
|
assert coins_response.get("success")
|
||||||
|
coins = coins_response["nft_list"]
|
||||||
|
assert len(coins) == 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
wallet_1.wallet_state_manager.wallets[2]
|
||||||
|
raise AssertionError("NFT wallet shouldn't exist yet")
|
||||||
|
except KeyError:
|
||||||
|
# there shouldn't be a nft wallet yet
|
||||||
|
pass
|
||||||
|
resp = await api_0.nft_transfer_nft(
|
||||||
|
dict(
|
||||||
|
wallet_id=nft_wallet_0_id,
|
||||||
|
target_address=encode_puzzle_hash(ph1, "xch"),
|
||||||
|
nft_coin_id=coins[0].nft_coin_id.hex(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert resp.get("success")
|
||||||
|
sb = resp["spend_bundle"]
|
||||||
|
assert compute_memos(sb)
|
||||||
|
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
||||||
|
|
||||||
|
for i in range(1, num_blocks):
|
||||||
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
||||||
|
time_left = 5.0
|
||||||
|
while time_left > 0:
|
||||||
|
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
|
||||||
|
if len(coins_response["nft_list"]) == 0:
|
||||||
|
break
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
time_left -= 0.5
|
||||||
|
else:
|
||||||
|
raise AssertionError("NFT not transferred")
|
||||||
|
|
||||||
|
nft_wallet_1 = wallet_1.wallet_state_manager.wallets[2]
|
||||||
|
await time_out_assert(15, len, 1, nft_wallet_1.nft_wallet_info.my_nft_coins)
|
||||||
|
Loading…
Reference in New Issue
Block a user