create offer and zero coin automatically

This commit is contained in:
Yostra 2020-04-23 19:54:10 -07:00
parent 8c3cf2617d
commit 3752d466e8
4 changed files with 153 additions and 32 deletions

View File

@ -734,13 +734,17 @@ class CCWallet:
full_spend = SpendBundle.aggregate([spend_bundle, eve_spend])
return full_spend
async def create_spend_bundle_relative_amount(self, cc_amount):
async def create_spend_bundle_relative_amount(self, cc_amount, zero_coin: Coin = None):
# If we're losing value then get coloured coins with at least that much value
# If we're gaining value then our amount doesn't matter
if cc_amount < 0:
cc_spends = await self.select_coins(abs(cc_amount))
else:
cc_spends = await self.select_coins(0)
if zero_coin is None:
return None
cc_spends = set()
cc_spends.add(zero_coin)
if cc_spends is None:
return None

View File

@ -5,6 +5,7 @@ import logging
import clvm
from src.types.BLSSignature import BLSSignature
from src.types.coin import Coin
from src.types.coin_solution import CoinSolution
from src.types.program import Program
from src.types.sized_bytes import bytes32
@ -52,13 +53,44 @@ class TradeManager:
wallet_id = uint32(int(id))
wallet = self.wallet_state_manager.wallets[wallet_id]
if isinstance(wallet, CCWallet):
new_spend_bundle = await wallet.create_spend_bundle_relative_amount(
amount
)
balance = await wallet.get_confirmed_balance()
if balance == 0:
if spend_bundle is None:
to_exclude = []
else:
to_exclude = spend_bundle.removals()
zero_spend_bundle: SpendBundle = await wallet.generate_zero_val_coin(False, to_exclude)
if zero_spend_bundle is None:
raise ValueError("Failed to generate offer. Zero value coin not created.")
if spend_bundle is None:
spend_bundle = zero_spend_bundle
else:
spend_bundle = SpendBundle.aggregate([spend_bundle, zero_spend_bundle])
additions = zero_spend_bundle.additions()
removals = zero_spend_bundle.removals()
zero_val_coin: Optional[Coin] = None
for add in additions:
if add not in removals and add.amount == 0:
zero_val_coin = add
new_spend_bundle = await wallet.create_spend_bundle_relative_amount(
amount, zero_val_coin
)
else:
new_spend_bundle = await wallet.create_spend_bundle_relative_amount(
amount
)
elif isinstance(wallet, Wallet):
if spend_bundle is None:
to_exclude = []
else:
to_exclude = spend_bundle.removals()
new_spend_bundle = await wallet.create_spend_bundle_relative_chia(
amount
amount, to_exclude
)
self.log.info("1")
else:
return False, None
if new_spend_bundle.removals() == [] or new_spend_bundle is None:
@ -83,7 +115,6 @@ class TradeManager:
try:
self.log.info(f"trade offer: {file_path}")
cc_discrepancies: Dict[bytes32, int] = dict()
wallets: Dict[bytes32, Any] = dict()
trade_offer_hex = file_path.read_text()
trade_offer = SpendBundle.from_bytes(bytes.fromhex(trade_offer_hex))
for coinsol in trade_offer.coin_solutions:
@ -92,15 +123,6 @@ class TradeManager:
# work out the deficits between coin amount and expected output for each
if cc_wallet_puzzles.check_is_cc_puzzle(puzzle):
colour = cc_wallet_puzzles.get_genesis_from_puzzle(
binutils.disassemble(puzzle)
)
if colour not in wallets:
wallets[
colour
] = await self.wallet_state_manager.get_wallet_for_colour(
colour
)
parent_info = binutils.disassemble(solution.rest().first()).split(
" "
)
@ -121,20 +143,16 @@ class TradeManager:
else:
cc_discrepancies[colour] = coinsol.coin.amount - out_amount
else: # standard chia coin
if None in cc_discrepancies:
cc_discrepancies["chia"] += (
coinsol.coin.amount
- cc_wallet_puzzles.get_output_amount_for_puzzle_and_solution(
puzzle, solution
)
coin_amount = coinsol.coin.amount
out_amount = cc_wallet_puzzles.get_output_amount_for_puzzle_and_solution(
puzzle, solution
)
diff = coin_amount - out_amount
if "chia" in cc_discrepancies:
cc_discrepancies["chia"] = cc_discrepancies["chia"] + diff
else:
cc_discrepancies["chia"] = (
coinsol.coin.amount
- cc_wallet_puzzles.get_output_amount_for_puzzle_and_solution(
puzzle, solution
)
)
cc_discrepancies["chia"] = diff
return True, cc_discrepancies, None
except Exception as e:
return False, None, e
@ -167,6 +185,7 @@ class TradeManager:
# work out the deficits between coin amount and expected output for each
if cc_wallet_puzzles.check_is_cc_puzzle(puzzle):
parent_info = binutils.disassemble(solution.rest().first()).split(" ")
if len(parent_info) > 1:
# Calculate output amounts
colour = cc_wallet_puzzles.get_genesis_from_puzzle(
@ -183,6 +202,7 @@ class TradeManager:
out_amount = cc_wallet_puzzles.get_output_amount_for_puzzle_and_solution(
innerpuzzlereveal, innersol
)
if colour in cc_discrepancies:
cc_discrepancies[colour] += coinsol.coin.amount - out_amount
else:
@ -212,6 +232,8 @@ class TradeManager:
out_amount,
)
]
else:
coinsols.append(coinsol)
else:
# standard chia coin
if chia_discrepancy is None:
@ -227,7 +249,7 @@ class TradeManager:
chia_spend_bundle: Optional[SpendBundle] = None
if chia_discrepancy is not None:
chia_spend_bundle = await self.wallet_state_manager.main_wallet.create_spend_bundle_relative_chia(
chia_discrepancy
chia_discrepancy, []
)
zero_spend_list: List[SpendBundle] = []

View File

@ -362,16 +362,16 @@ class Wallet:
# Create an offer spend bundle for chia given an amount of relative change (i.e -400 or 1000)
# This is to be aggregated together with a coloured coin offer to ensure that the trade happens
async def create_spend_bundle_relative_chia(self, chia_amount: int):
async def create_spend_bundle_relative_chia(self, chia_amount: int, exclude: List[Coin]):
list_of_solutions = []
utxos = None
# If we're losing value then get coins with at least that much value
# If we're gaining value then our amount doesn't matter
if chia_amount < 0:
utxos = await self.select_coins(abs(chia_amount))
utxos = await self.select_coins(abs(chia_amount), exclude)
else:
utxos = await self.select_coins(0)
utxos = await self.select_coins(0, exclude)
if utxos is None:
return None

View File

@ -582,3 +582,98 @@ class TestWalletSimulator:
cc_2_unconfirmed_balance = await red_wallet.get_confirmed_balance()
assert cc_2_confirmed_balance == 70
assert cc_2_unconfirmed_balance == 70
@pytest.mark.asyncio
async def test_create_offer_with_zero_val(self, two_wallet_nodes):
num_blocks = 10
full_nodes, wallets = two_wallet_nodes
full_node_1, server_1 = full_nodes[0]
wallet_node, server_2 = wallets[0]
wallet_node_2, server_3 = wallets[1]
wallet = wallet_node.wallet_state_manager.main_wallet
wallet2 = wallet_node_2.wallet_state_manager.main_wallet
ph = await wallet.get_new_puzzlehash()
ph2 = await wallet2.get_new_puzzlehash()
await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None)
await server_3.start_client(PeerInfo("localhost", uint16(server_1._port)), None)
for i in range(1, num_blocks):
await full_node_1.farm_new_block(FarmNewBlockProtocol(ph))
await asyncio.sleep(5)
funds = sum(
[
calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i))
for i in range(1, num_blocks - 2)
]
)
assert await wallet.get_confirmed_balance() == funds
cc_wallet: CCWallet = await CCWallet.create_new_cc(
wallet_node.wallet_state_manager, wallet, uint64(100)
)
for i in range(1, num_blocks):
await full_node_1.farm_new_block(FarmNewBlockProtocol(ph))
await asyncio.sleep(1)
confirmed_balance = await cc_wallet.get_confirmed_balance()
unconfirmed_balance = await cc_wallet.get_unconfirmed_balance()
assert confirmed_balance == 100
assert unconfirmed_balance == 100
colour = cc_wallet_puzzles.get_genesis_from_core(cc_wallet.cc_info.my_core)
cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc(
wallet_node_2.wallet_state_manager, wallet2, colour
)
assert cc_wallet.cc_info.my_core == cc_wallet_2.cc_info.my_core
await full_node_1.farm_new_block(FarmNewBlockProtocol(ph2))
for i in range(1, num_blocks):
await full_node_1.farm_new_block(FarmNewBlockProtocol(ph))
trade_manager_1 = await TradeManager.create(wallet_node.wallet_state_manager)
trade_manager_2 = await TradeManager.create(wallet_node_2.wallet_state_manager)
file = "test_offer_file.offer"
file_path = Path(file)
if file_path.exists():
file_path.unlink()
offer_dict = {1: -10, 2: 30}
success, spend_bundle = await trade_manager_2.create_offer_for_ids(offer_dict)
assert success is True
assert spend_bundle is not None
trade_manager_2.write_offer_to_disk(file_path, spend_bundle)
success, offer, error = await trade_manager_1.get_discrepancies_for_offer(file_path)
assert error is None
assert success is True
assert offer is not None
assert offer["chia"] == 10
assert offer[colour] == -30
success = await trade_manager_1.respond_to_offer(file_path)
assert success is True
for i in range(0, 4):
await full_node_1.farm_new_block(FarmNewBlockProtocol(token_bytes()))
await asyncio.sleep(5)
cc_2_confirmed_balance = await cc_wallet_2.get_confirmed_balance()
cc_2_unconfirmed_balance = await cc_wallet_2.get_confirmed_balance()
assert cc_2_confirmed_balance == 30
assert cc_2_unconfirmed_balance == 30