townforge/tests/functional_tests/cc.py

6457 lines
303 KiB
Python
Executable File

#!/usr/bin/env python3
#encoding=utf-8
# Copyright (c) 2019 Crypto City
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import print_function
import json
import time
import math
import os
import copy
import itertools
"""Test CC RPC
"""
from framework.daemon import Daemon
from framework.wallet import Wallet
from framework.daemon_util import get_daemon_state
FIRST_CITY_X = 0
FIRST_CITY_Y = 0
ITEM_STONE = 1
ITEM_MEDIUM_STONE = 2
ITEM_GRANITE = ITEM_MEDIUM_STONE
ITEM_WOOD = 4
ITEM_VEGETATION = 7
ITEM_RUNESTONE = 8
MATERIAL_PLACEHOLDER = 1
MATERIAL_VARIANT_PLOUGHED_FIELD = 33
MATERIAL_LOCKED_PINE = 27
ITEM_LABOUR = 256
ITEM_FIREWOOD = 257
ITEM_FIRST_PATENT = 4096
ITEM_FIRST_FOOD = 1024
ITEM_FIRST_GEMSTONE = 2048
ITEM_FOOD_VEGETABLES = ITEM_FIRST_FOOD
ITEM_FOOD_GRAIN = ITEM_FIRST_FOOD + 1
ITEM_FOOD_MEAT = ITEM_FIRST_FOOD + 2
ITEM_AMETHYST = 2048
NUM_PREDEFINED_ITEMS = ITEM_FIRST_PATENT + 4096
ITEM_FIRST_USER = NUM_PREDEFINED_ITEMS
ITEM_LAST_USER = 65536 + 1048576 - 1
ORIGIN_RESOURCE_RADIUS = 500
ROLE_EMPTY = 0
ROLE_AGRICULTURAL = 1
ROLE_CRAFT = 2
ROLE_RESIDENTIAL1 = 5
ROLE_MILITARY = 8
ROLE_SAWMILL = 11
ROLE_STONECUTTER = 10
ROLE_WORKFORCE = 14
MAX_DISTANCE_PERCENTAGE = 1600
MAX_CC_NAME_LENGTH = 64
account_creation_fee = 100000000
GAME_UPDATE_FREQUENCY = 720
BUILD_RATIO_ACTIVE_THRESHOLD_PERCENT = 0
MIN_TRADE_EXPIRATION = (86400 // 30)
MIN_RESEARCH_AMOUNT = 10000000
LABOUR_LAST_RESORT_PRICE = 40000
STONE_LAST_RESORT_PRICE = 600000
GRANITE_LAST_RESORT_PRICE = 2000000
RUNESTONE_LAST_RESORT_PRICE = 100000000
WOOD_LAST_RESORT_PRICE = 100000
NEW_ITEM_FEE = 100000000
NEW_MORTGAGE_FEE = 100000000
DISCOVERY_RESEARCH_EFFICIENCY = 12
DISCOVERY_RESEARCH_PRODUCTIVITY = 13
DISCOVERY_RESEARCH_EXPERTISE = 14
DISCOVERY_RESEARCH_MASTERY = 15
DISCOVERY_BUCKET_BRIGADE = 29
IGNORE_ACCOUNT = 0
IGNORE_FLAG = 1
IGNORE_ITEM = 2
IGNORE_CITY = 3
CROP_NONE = 0
CROP_VEGETABLES = 1
CROP_GRAIN = 2
NUM_STARTING_MOOSE = 1000
NUM_STARTING_BEARS = 250
GAME_ACCOUNT = 2
FORECLOSURE_ACCOUNT = 3
COINS_ITEM_GROUP = ITEM_FIRST_USER
MORTGAGE_ITEM_GROUP = ITEM_FIRST_USER + 1
RESTRICTED_ITEM_GROUP = ITEM_FIRST_USER + 2
COIN_ITEM_SETTLEMENT = ITEM_FIRST_USER + 3
COIN_MINTING_FEE = 10000000
COIN_SMELTING_FEE = 1000000
COIN_GOLD_CONTENT = 5000000000
COIN_MINTING_WINDOW = (7 * 86400 // 30)
NONCE_CANCELLATION_FEE = 10000000
collectible_coin_type_players = 1
collectible_coin_type_city = 2
collectible_coin_type_yearly = 3
collectible_coin_type_event = 4
NUM_CUSTOM_ITEM_USER_DATA = 4
CHOP_WOOD_LABOUR_PER_100_WOOD = 5
PINE_HEATING_POINTS = 5
OAK_HEATING_POINTS = 8
AUCTION_TYPE_FLAG = 0
AUCTION_TIME = (7 * 86400 // 30)
CUSTOM_ITEM_GOLD_GILDING_FEE_PER_THOUSAND = 5
CUSTOM_ITEM_GOLD_RECOVERY_FEE_PER_THOUSAND = 5
AUCTION_TYPE_FLAG = 0
AUCTION_TYPE_ITEM = 1
NEW_AUCTION_FEE = 100000000
AUCTION_BID_FEE = 10000000
MIN_AUCTION_BASE_TICKS = 8
MAX_AUCTION_BASE_TICKS = 56
BADGE_COSMOPOLITAN = 49
CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE = 20
SPECIAL_EVENT_NONE = 0
SPECIAL_EVENT_FIRE = 14
SPECIAL_EVENT_MOOSE_DISEASE = 19
FIREFIGHTING_COVERAGE_PERCENT = 350
HUNT_LABOUR_COST = 6500
def COIN_TYPE(x):
return x[0] >> 56
def get_gold_content(coin_type):
if coin_type == collectible_coin_type_players:
return 35
if coin_type == collectible_coin_type_city:
return 20
if coin_type == collectible_coin_type_yearly:
return 10
if coin_type == collectible_coin_type_event:
return 50
assert False
return 0
def get_distance(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1):
left = ax1 < bx0
right = ax0 > bx1
top = ay1 < by0
bottom = ay0 > by1
if left:
if top:
return max(bx0 - ax1, by0 - ay1)
if bottom:
return max(bx0 - ax1, ay0 - by1)
return bx0 - ax1
if right:
if top:
return max(ax0 - bx1, by0 - ay1)
if bottom:
return max(ax0 - bx1, ay0 - by1)
return ax0 - bx1
if top:
return by0 - ay1
if bottom:
return ay0 - by1
return 0
def feq(x, y):
return math.fabs(x - y) < 0.01
class CCTest():
def run_test(self):
self.reset()
self.create()
state0 = self.get_state()
self.mine()
self.recordreorg_root()
self.check_no_items()
self.check_predefined_items()
self.check_gold_consistency()
self.check_item_count_consistency()
self.test_get_new_nonces()
self.test_cc_account_creation()
self.check_gold_consistency()
self.test_names()
self.test_cc_transfers()
self.check_gold_consistency()
self.check_event_balances()
self.test_buy_land()
self.test_buy_items()
self.test_firewood()
self.test_build()
self.test_trading()
self.test_interacting_trades()
self.test_accrual_trades()
self.test_trade_third_party_matching()
self.test_cancel_nonce()
self.test_giving()
self.test_discoveries()
self.check_gold_consistency()
self.check_item_count_consistency()
self.check_event_balances()
self.test_game_update()
self.test_revert()
self.check_gold_consistency()
self.test_script_variables()
self.test_scripts()
self.test_runestones()
#self.test_custom_items()
self.check_gold_consistency()
self.check_item_count_consistency()
#self.test_mortgages()
self.test_auctions()
self.check_gold_consistency()
self.check_event_balances()
self.test_badges()
self.test_resize_flag()
self.test_attributes()
self.test_dice_roll()
self.test_hunt()
self.test_invitation()
self.test_chat()
self.test_ignore()
self.test_minting()
self.check_gold_consistency()
self.test_farming()
self.check_item_count_consistency()
self.test_derelict()
self.test_cities()
self.test_fire()
self.check_gold_consistency()
self.check_item_count_consistency()
self.check_event_balances()
self.test_reorg()
self.check_item_count_consistency()
self.check_gold_consistency()
self.check_event_balances()
self.reset_and_check_states()
state1 = self.get_state()
assert state0 == state1, self.get_diff(state0, state1)
self.check_no_items()
self.check_gold_consistency()
self.check_event_balances()
self.reset()
def assert_exception(self, f):
ok = False
try:
f()
except Exception as e:
ok = True
assert ok
def reset(self):
print('Resetting blockchain')
for idx in [2, 3, 4]:
daemon = Daemon(idx = idx)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
self.states = []
self.deposits = 0
def reset_and_check_states(self):
print('Resetting blockchain step by step and checking states after revert')
daemon = self.daemon
res = daemon.get_height()
height = res.height
while len(self.states) > 0 and self.states[-1][0]+1 >= height:
self.states = self.states[0:-1]
while len(self.states) > 0:
pop_height = self.states[-1][0]
pop_state = self.states[-1][1]
self.states = self.states[0:-1]
assert height-1 > pop_height
pop_blocks = height-1 - pop_height
daemon.pop_blocks(pop_blocks)
res = daemon.get_height()
height = res.height
assert height-1 == pop_height
state = self.get_state()
assert state == pop_state, self.get_diff(state, pop_state)
self.deposits = 0
def generate_blocks(self, address, nblocks, skip_update = False, game_account_key = ""):
daemon = self.daemon
res = daemon.get_height()
height = res.height
while len(self.states) > 0 and self.states[-1][0]+1 >= height:
self.states = self.states[0:-1]
self.states.append((height - 1, self.get_state()))
res = daemon.generateblocks(address, nblocks, game_account_key = game_account_key)
if skip_update and (res.height - 1) % GAME_UPDATE_FREQUENCY == 0:
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
def create(self):
print('Creating wallets')
seeds = [
'teardrop owls later width skater gadget different pegs yard ahead onslaught dynamite thorn espionage dwelt rural eels aimless shipped toaster shocking rounded maverick mystery thorn',
'geek origin industrial payment friendly physics width putty beyond addicted rogue metro midst anvil unplugs tequila efficient feast elapse liquid degrees smuggled also bawled tequila',
'tidy heron aching outbreak terminal inorganic nexus umpire economics auctions hope soapy hive vigilant hunter tadpoles hippo southern observant rabbits asked vector gimmick godfather heron',
'aimless zesty juvenile hedgehog sash eden inflamed faxed abbey piloted silk gigantic affair snout guarded inquest utmost tunnel remedy sighting otherwise alley illness jubilee gigantic',
'journal vitals technical dying dice taken evicted essential pepper dented psychic vaults oatmeal pairing wrong request damp rugged buffet lids bias pouch ladder leisure rugged',
]
self.wallet = [None] * len(seeds)
self.pkeys = [None] * len(seeds)
for i in range(len(seeds)):
self.wallet[i] = Wallet(idx = i)
# close the wallet if any, will throw if none is loaded
try: self.wallet[i].close_wallet()
except: pass
res = self.wallet[i].restore_deterministic_wallet(seed = seeds[i])
ok = False
try: res = self.wallet[i].cc_get_info()
except: ok = True
assert ok
self.wallet[i].set_daemon('127.0.0.1:18182')
self.daemon = Daemon(idx = 2)
global FIRST_CITY_X
global FIRST_CITY_Y
res = self.daemon.cc_get_city(0)
FIRST_CITY_X = res.ox
FIRST_CITY_Y = res.oy
def mine(self):
print("Mining many blocks")
daemon = self.daemon
res = daemon.get_info()
height = res.height
blocks_mined = 0
for data in [
['TF1MMEY5v2dN49XKNCKCdBqmTMM6GdZZA5UBbDgTewaUd3c2jDazN5yKrG1BBHX3UyPqKD9hrh3DpPTDmWiCmsuRpePT1MTaPxm', 30], # wallet[0]
['TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 30], # wallet[1]
['TF1MMCHHDFKYSQ1DkLWskTxU2fC7jtZLwcATXHP3uvJo4dV757dtGwUE9ZL6SE9DJhxrvzThAsG4drVmAPHzEvh6TCq19AbzTo4', 660], # wallet[3]
['TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1000], # wallet[2]
['TF1MMA4mMyUGkR373BQYWhkssgXWPuWdSauDXiRLJEmSXiJKbw3um94tDxogJShqXHt51a8UBotBRXuMvjFjaxSumjRXDrY7wSm', 0], # wallet[4]
]:
self.generate_blocks(data[0], data[1])
blocks_mined += data[1]
# get past the next update so we don't get balance changes fucking the tests up
res = daemon.get_info()
blocks = (GAME_UPDATE_FREQUENCY * 8000 - res.height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
blocks_mined += blocks
for i in range(len(self.wallet)):
self.wallet[i].refresh()
res = self.wallet[i].get_height()
assert res.height == height + blocks_mined
def recordreorg_root(self):
daemon = self.daemon
res = daemon.get_info()
self.reorg_root_hash = res.top_block_hash
assert len(self.reorg_root_hash) == 64
self.reorg_root_height = res.height - 1
assert self.reorg_root_height > 0
def check_no_items(self):
daemon = self.daemon
print('Checking there are no items')
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)] + [x + ITEM_FIRST_USER for x in range(256)])
if 'count' in res:
for x in res.count:
assert x == 0
self.check_item_count_consistency()
def check_predefined_items(self):
daemon = self.daemon
print('Checking predefined items info')
self.assert_exception(lambda: daemon.cc_get_predefined_item_info([0]))
self.assert_exception(lambda: daemon.cc_get_predefined_item_info([0, 1]))
self.assert_exception(lambda: daemon.cc_get_predefined_item_info([1, 0]))
self.assert_exception(lambda: daemon.cc_get_predefined_item_info([11111111]))
self.assert_exception(lambda: daemon.cc_get_predefined_item_info([ITEM_FIRST_USER]))
res = daemon.cc_get_predefined_item_info([ITEM_STONE, ITEM_WOOD, ITEM_LABOUR, ITEM_AMETHYST, ITEM_FOOD_GRAIN])
assert len(res.items_) == 5
e = res.items_[0]
assert e.id == ITEM_STONE
assert len(e.name) > 0
assert e.amount == 0
assert e.is_block
assert e.is_stone
assert not e.is_wood
assert not e.is_gemstone
assert not e.is_food
assert not e.is_patent
assert e.food == 0
assert e.heating == 0
e = res.items_[1]
assert e.id == ITEM_WOOD
assert len(e.name) > 0
assert e.amount == 0
assert e.is_block
assert not e.is_stone
assert e.is_wood
assert not e.is_gemstone
assert not e.is_food
assert not e.is_patent
assert e.food == 0
assert e.heating > 0
e = res.items_[2]
assert e.id == ITEM_LABOUR
assert len(e.name) > 0
assert e.amount == 0
assert not e.is_block
assert not e.is_stone
assert not e.is_wood
assert not e.is_gemstone
assert not e.is_food
assert not e.is_patent
assert e.food == 0
assert e.heating == 0
e = res.items_[3]
assert e.id == ITEM_AMETHYST
assert len(e.name) > 0
assert e.amount == 0
assert not e.is_block
assert not e.is_stone
assert not e.is_wood
assert e.is_gemstone
assert not e.is_food
assert not e.is_patent
assert e.food == 0
assert e.heating == 0
e = res.items_[4]
assert e.id == ITEM_FOOD_GRAIN
assert len(e.name) > 0
assert e.amount == 0
assert not e.is_block
assert not e.is_stone
assert not e.is_wood
assert not e.is_gemstone
assert e.is_food
assert not e.is_patent
assert e.food > 0
assert e.heating == 0
def test_get_new_nonces(self):
daemon = self.daemon
print("Testing getting new nonces")
self.assert_exception(lambda: daemon.cc_get_new_nonces(1024))
res = daemon.cc_get_new_nonces(0)
assert not 'nonces' in res or len(res.nonces) == 0
res = daemon.cc_get_new_nonces(2)
assert len(res.nonces) == 2
assert res.nonces[0] != 0
assert res.nonces[1] != 0
assert res.nonces[0] != res.nonces[1]
def test_cc_account_creation(self):
daemon = self.daemon
print("Testing CC account creation")
n_wallets = 3
self.wallet[0].refresh()
res = self.wallet[0].get_balance()
balance0 = res.balance
assert res.cc_balance == 0
res = self.wallet[0].cc_deposit(amount = 100000000, name = 'wallet 0')
self.deposits += 100000000
assert len(res.fee_list) == 1
fee0 = res.fee_list[0]
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
self.wallet[1].refresh()
res = self.wallet[1].get_balance()
balance1 = res.balance
assert res.cc_balance == 0
res = self.wallet[1].cc_deposit(amount = 200000000, name = 'wallet 1')
self.deposits += 200000000
assert len(res.fee_list) == 1
fee1 = res.fee_list[0]
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
self.wallet[0].refresh()
res = self.wallet[0].get_balance()
assert res.balance == balance0 - 100000000 - fee0
assert res.cc_balance == 100000000 - account_creation_fee
self.wallet[1].refresh()
res = self.wallet[1].get_balance()
assert res.balance == balance1 - 200000000 - fee1
assert res.cc_balance == 200000000 - account_creation_fee
for i in range(2):
res = self.wallet[i].cc_get_info()
self.pkeys[i] = res.cc_pkey
def test_cc_transfers(self):
daemon = self.daemon
print("Testing CC account deposits/withdrawals")
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
n_wallets = 3
balance = [None] * n_wallets
cc_balance = [None] * n_wallets
for i in range(n_wallets):
self.wallet[i].refresh()
res = self.wallet[i].get_balance()
balance[i] = res.balance
cc_balance[i] = res.cc_balance
res = self.wallet[1].cc_withdraw(amount = 25000000)
self.deposits -= 25000000
balance[1] += 25000000 - res.fee_list[0]
cc_balance[1] -= 25000000
res = self.wallet[2].cc_deposit(amount = 500000000, name = 'wallet 2')
self.deposits += 500000000
balance[2] -= 500000000 + res.fee_list[0]
cc_balance[2] += 500000000 - account_creation_fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
for i in range(n_wallets):
self.wallet[i].refresh()
res = self.wallet[i].get_balance()
assert res.balance == balance[i]
assert res.cc_balance == cc_balance[i]
# try to withdraw more
ok = False
try: res = self.wallet[0].cc_withdraw(amount = 1)
except: ok = True
assert ok
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
for i in range(n_wallets):
self.wallet[i].refresh()
res = self.wallet[i].get_balance()
assert res.balance == balance[i]
assert res.cc_balance == cc_balance[i]
# down to 0
res = self.wallet[1].cc_withdraw(amount = cc_balance[1])
self.deposits -= cc_balance[1]
balance[1] += cc_balance[1] - res.fee_list[0]
cc_balance[1] = 0
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
for i in range(n_wallets):
self.wallet[i].refresh()
res = self.wallet[i].get_balance()
assert res.balance == balance[i]
assert res.cc_balance == cc_balance[i]
# re-deposit on empty address, and transfer to it
res = self.wallet[1].cc_deposit(amount = 1200000000)
self.deposits += 1200000000
balance[1] -= 1200000000 + res.fee_list[0]
cc_balance[1] += 1200000000
res = self.wallet[2].cc_transfer(self.pkeys[0], amount = 18000000)
cc_balance[2] -= 18000000 + res.fee_list[0]
cc_balance[0] += 18000000
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# and add lots of balance to the third wallet so we can do interesting stuff later on
res = self.wallet[2].cc_deposit(amount = 450000000000)
self.deposits += 450000000000
balance[2] -= 450000000000 + res.fee_list[0]
cc_balance[2] += 450000000000
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
res = self.wallet[2].cc_deposit(amount = 450000000000)
self.deposits += 450000000000
balance[2] -= 450000000000 + res.fee_list[0]
cc_balance[2] += 450000000000
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
res = self.wallet[2].cc_deposit(amount = 450000000000)
self.deposits += 450000000000
balance[2] -= 450000000000 + res.fee_list[0]
cc_balance[2] += 450000000000
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
res = self.wallet[2].cc_deposit(amount = 450000000000)
self.deposits += 450000000000
balance[2] -= 450000000000 + res.fee_list[0]
cc_balance[2] += 450000000000
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
for i in range(n_wallets):
self.wallet[i].refresh()
res = self.wallet[i].get_balance()
assert res.balance == balance[i]
assert res.cc_balance == cc_balance[i]
res = self.wallet[i].cc_get_info()
assert res.cc_balance == cc_balance[i]
assert not 'flags' in res or len(res.flags) == 0
assert not 'item_balances' in res or res.item_balances == 0
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
def test_buy_land(self):
daemon = self.daemon
print("Testing buying land")
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
res = self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
cc_balance = res.cc_balance
account_id = res.account_id
res = daemon.cc_get_new_flag_cost(0, 800, 400, 800 + 32 - 1, 400 + 24 - 1)
self.assert_exception(lambda: self.wallet[2].cc_buy_land(800, 400, 800 + 32 - 1, 400 + 24 - 1))
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X + 800, FIRST_CITY_Y + 400, FIRST_CITY_X + 800 + 32 - 1, FIRST_CITY_Y + 400 + 24 - 1)
cost = res.cost
res = self.wallet[2].cc_buy_land(FIRST_CITY_X+ 800, FIRST_CITY_Y + 400, FIRST_CITY_X + 800 + 32 - 1, FIRST_CITY_Y + 400 + 24 - 1)
fee = res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
assert res.cc_balance == cc_balance - cost - fee
cc_balance = res.cc_balance
res = daemon.cc_get_account(account_id)
assert len(res.flags) == 1
flag_id = res.flags[0]
assert flag_id > 0
res = daemon.cc_get_flag(flag_id)
assert res.owner == account_id
assert res.city == 0
assert res.x0 == FIRST_CITY_X + 800
assert res.y0 == FIRST_CITY_Y + 400
assert res.x1 == FIRST_CITY_X + 800 + 32 - 1
assert res.y1 == FIRST_CITY_Y + 400 + 24 - 1
assert res.repair == 0
cost = 0
fee = 0
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X + 400, FIRST_CITY_Y + 400, FIRST_CITY_X + 400 + 90 - 1, FIRST_CITY_Y + 400 + 90 - 1)
cost += res.cost
res = self.wallet[2].cc_buy_land(FIRST_CITY_X + 400, FIRST_CITY_Y + 400, FIRST_CITY_X + 400 + 90 - 1, FIRST_CITY_Y + 400 + 90 - 1)
fee += res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X + 400, FIRST_CITY_Y + 500, FIRST_CITY_X + 400 + 90 - 1, FIRST_CITY_Y + 500 + 90 - 1)
cost += res.cost
res = self.wallet[2].cc_buy_land(FIRST_CITY_X + 400, FIRST_CITY_Y + 500, FIRST_CITY_X + 400 + 90 - 1, FIRST_CITY_Y + 500 + 90 - 1)
fee += res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X + 500, FIRST_CITY_Y + 400, FIRST_CITY_X + 500 + 90 - 1, FIRST_CITY_Y + 400 + 90 - 1)
cost += res.cost
res = self.wallet[2].cc_buy_land(FIRST_CITY_X + 500, FIRST_CITY_Y + 400, FIRST_CITY_X + 500 + 90 - 1, FIRST_CITY_Y + 400 + 90 - 1)
fee += res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X + 500, FIRST_CITY_Y + 500, FIRST_CITY_X + 500 + 90 - 1, FIRST_CITY_Y + 500 + 90 - 1)
cost += res.cost
res = self.wallet[2].cc_buy_land(FIRST_CITY_X + 500, FIRST_CITY_Y + 500, FIRST_CITY_X + 500 + 90 - 1, FIRST_CITY_Y + 500 + 90 - 1)
fee += res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert len(res.flags) == 5
flag_id = res.flags[0]
assert flag_id > 0
res = daemon.cc_get_flag(flag_id)
assert res.owner == account_id
assert res.city == 0
assert res.x0 == FIRST_CITY_X + 800
assert res.y0 == FIRST_CITY_Y + 400
assert res.x1 == FIRST_CITY_X + 800 + 32 - 1
assert res.y1 == FIRST_CITY_Y + 400 + 24 - 1
assert res.repair == 0
self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
assert res.cc_balance == cc_balance - cost - fee
print('Checking we can not buy overlapping land')
ok = False
try: res = self.wallet[2].cc_buy_land(FIRST_CITY_X + 460, FIRST_CITY_Y + 460, FIRST_CITY_X + 550, FIRST_CITY_Y + 550)
except: ok = True
assert ok
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
def add_item(self, item_balances, type, amount):
if amount == 0:
return item_balances
for i in range(len(item_balances)):
if item_balances[i]['type'] == type:
item_balances[i]['amount'] += amount
if item_balances[i]['amount'] == 0:
del item_balances[i]
return item_balances
item_balances.append({u'type': type, u'amount': amount})
item_balances = sorted(item_balances, key=lambda x: x['type'])
return item_balances
def remove_item(self, item_balances, type):
for i in range(len(item_balances)):
if item_balances[i]['type'] == type:
del item_balances[i]
break;
return item_balances
def remove_wood(self, item_balances):
item_balances = self.remove_item(item_balances, ITEM_WOOD)
item_balances = self.remove_item(item_balances, ITEM_FIREWOOD)
return item_balances
def test_buy_items(self):
daemon = self.daemon
print("Testing buying items")
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
res = self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
cc_balance = res.cc_balance
account_id = res.account_id
res = daemon.cc_get_account(account_id)
assert not 'item_balances' in res or len(res.item_balances) == 0
item_balances = []
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 15}, {'type': ITEM_WOOD, 'amount': 40}])
item_balances = self.add_item(item_balances, ITEM_STONE, 15)
item_balances = self.add_item(item_balances, ITEM_WOOD, 40)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert res.item_balances == item_balances
item_counts[ITEM_STONE] += 15
item_counts[ITEM_WOOD] += 40
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
ok = False
try: res = self.wallet[2].cc_buy_items(entries = [{'type': 0, 'amount': 15}])
except: ok = True
assert ok
ok = False
try: res = self.wallet[2].cc_buy_items(entries = [{'type': 255, 'amount': 15}])
except: ok = True
assert ok
ok = False
try: res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 0}])
except: ok = True
assert ok
ok = False
try: res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 0xffffffff}])
except: ok = True
assert ok
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 8*650}, {'type': ITEM_WOOD, 'amount': 8*4660}, {'type': ITEM_MEDIUM_STONE, 'amount': 8*50}, {'type': ITEM_LABOUR, 'amount': 8*100000}])
item_balances = self.add_item(item_balances, ITEM_STONE, 8*650)
item_balances = self.add_item(item_balances, ITEM_MEDIUM_STONE, 8*50)
item_balances = self.add_item(item_balances, ITEM_WOOD, 8*4660)
item_balances = self.add_item(item_balances, ITEM_LABOUR, 8*100000)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert res.item_balances == item_balances
item_counts[ITEM_STONE] += 8*650
item_counts[ITEM_WOOD] += 8*4660
item_counts[ITEM_MEDIUM_STONE] += 8*50
item_counts[ITEM_LABOUR] += 8*100000
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
def test_firewood(self):
daemon = self.daemon
print("Testing firewood")
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_WOOD, 'amount': 38}, {'type': ITEM_WOOD + 1, 'amount': 2}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_LABOUR, 'amount': (CHOP_WOOD_LABOUR_PER_100_WOOD * 40 + 99) // 100}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.assert_exception(lambda: self.wallet[2].cc_chop_wood([{'type': ITEM_STONE, 'amount': 1}]))
self.assert_exception(lambda: self.wallet[2].cc_chop_wood([{'type': ITEM_LABOUR, 'amount': 1}]))
self.assert_exception(lambda: self.wallet[2].cc_chop_wood([{'type': ITEM_WOOD, 'amount': 100000000}]))
self.assert_exception(lambda: self.wallet[2].cc_chop_wood([{'type': ITEM_WOOD, 'amount': 0}]))
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
res = self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
cc_balance = res.cc_balance
account_id = res.account_id
res = daemon.cc_get_account(account_id)
assert len(res.item_balances) > 0
item_balances = []
if 'item_balances' in res:
for e in res.item_balances:
item_balances = self.add_item(item_balances, e.type, e.amount)
res = self.wallet[2].cc_chop_wood([{'type': ITEM_WOOD, 'amount': 38}, {'type': ITEM_WOOD + 1, 'amount': 2}])
item_balances = self.add_item(item_balances, ITEM_WOOD, -38)
item_counts[ITEM_WOOD] -= 38
item_balances = self.add_item(item_balances, ITEM_WOOD + 1, -2)
item_counts[ITEM_WOOD + 1] -= 2
item_balances = self.add_item(item_balances, ITEM_FIREWOOD, 38 * PINE_HEATING_POINTS + 2 * OAK_HEATING_POINTS)
item_counts[ITEM_FIREWOOD] += 38 * PINE_HEATING_POINTS + 2 * OAK_HEATING_POINTS
item_balances = self.add_item(item_balances, ITEM_LABOUR, -((40 * CHOP_WOOD_LABOUR_PER_100_WOOD + 99) // 100))
item_counts[ITEM_LABOUR] -= (40 * CHOP_WOOD_LABOUR_PER_100_WOOD + 99) // 100
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.assert_exception(lambda: self.wallet[2].cc_chop_wood([{'type': ITEM_FIREWOOD, 'amount': 1}]))
res = daemon.cc_get_account(account_id)
assert res.item_balances == item_balances
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
def test_build(self):
daemon = self.daemon
print("Testing building")
flag_id = 3
res = daemon.cc_get_flag(flag_id)
construction_height = res.construction_height
# good in theory, but no role set yet
ok = False
try: self.wallet[2].cc_build(flag_id, 0, 0, 1, 1, block_data = [0, ITEM_STONE], encoded = True)
except: ok = True
assert ok
# can't set role 0 (empty)
ok = False
try: self.wallet[2].cc_building_settings(flag_id, 0, 100, construction_height)
except: ok = True
assert ok
# can't set large out of range role
ok = False
try: self.wallet[2].cc_building_settings(flag_id, 254, 100, construction_height)
except: ok = True
assert ok
# can't set 0 economic power
ok = False
try: self.wallet[2].cc_building_settings(flag_id, 1, 0, construction_height)
except: ok = True
assert ok
# can't set silly large economic power
ok = False
try: self.wallet[2].cc_building_settings(flag_id, 1, 100000, construction_height)
except: ok = True
assert ok
# 150 is the last one we can use before we research civil engineering
ok = False
try: self.wallet[2].cc_building_settings(flag_id, 1, 160, construction_height)
except: ok = True
assert ok
# ok
res = self.wallet[2].cc_building_settings(flag_id, 1, 100, construction_height, name = 'test building')
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# not our flag
ok = False
try: self.wallet[0].cc_build(flag_id, 0, 0, 1, 1, block_data = [0, ITEM_STONE], encoded = True)
except: ok = True
assert ok
# good
res = self.wallet[2].cc_build(flag_id, 0, 0, 1, 1, block_data = [0, ITEM_STONE], encoded = True)
assert len(res.tx_hash) == 64
# out of the flag
ok = False
try: self.wallet[2].cc_build(flag_id, 252, 252, 1, 1, block_data = [0, ITEM_STONE], encoded = True)
except: ok = True
assert ok
# out of the flag 2
ok = False
try: self.wallet[2].cc_build(flag_id, 0, 0, 254, 254, block_data = [0, ITEM_STONE], encoded = True)
except: ok = True
assert ok
# empty data
ok = False
try: self.wallet[2].cc_build(flag_id, 0, 0, 2, 2, block_data = [], encoded = True)
except: ok = True
assert ok
# too few blocks
ok = False
try: self.wallet[2].cc_build(flag_id, 0, 0, 2, 2, block_data = [1, 0], encoded = True)
except: ok = True
assert ok
# invalid block type
ok = False
try: self.wallet[2].cc_build(flag_id, 0, 0, 1, 1, block_data = [0, 250], encoded = True)
except: ok = True
assert ok
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# check tile state - should be just one
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_flag(flag_id, get_unpacked_tiles = True)
assert res.owner == account_id
assert res.city == 0
assert res.x0 == FIRST_CITY_X + 400
assert res.y0 == FIRST_CITY_Y + 400
assert res.x1 == FIRST_CITY_X + 400 + 90 - 1
assert res.y1 == FIRST_CITY_Y + 400 + 90 - 1
assert res.repair == 1000000
assert len(res.tiles) == 90 * 90
for i in range(90*90):
if i == 0:
assert res.tiles[i].data == [ITEM_STONE], res.tiles[i].data
else:
assert not 'data' in res.tiles[i]
# build nearby, no height
res = self.wallet[2].cc_build(flag_id, 0, 0, 2, 2, block_data = [3, 0, 0, ITEM_STONE, 0], encoded = True)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# build on top, with height
res = self.wallet[2].cc_build(flag_id, 0, 0, 2, 2, block_data = [3, ITEM_STONE, 0, 0, 0], build_height = 1, encoded = True)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id, get_unpacked_tiles = True)
assert res.owner == account_id
assert res.city == 0
assert res.x0 == FIRST_CITY_X + 400
assert res.y0 == FIRST_CITY_Y + 400
assert res.x1 == FIRST_CITY_X + 400 + 90 - 1
assert res.y1 == FIRST_CITY_Y + 400 + 90 - 1
assert res.repair == 1000000
assert len(res.tiles) == 90*90
for i in range(90*90):
if i == 0:
assert res.tiles[i].data == [ITEM_STONE, ITEM_STONE]
elif i == 90:
assert res.tiles[i].data == [ITEM_STONE]
else:
assert not 'data' in res.tiles[i]
# build with build_height
res = self.wallet[2].cc_build(flag_id, 0, 0, 1, 3, block_data = [ITEM_WOOD, ITEM_WOOD, ITEM_VEGETATION], encoded = False, build_height = 2)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id, get_unpacked_tiles = True)
assert res.owner == account_id
assert res.city == 0
assert res.x0 == FIRST_CITY_X + 400
assert res.y0 == FIRST_CITY_Y + 400
assert res.x1 == FIRST_CITY_X + 400 + 90 - 1
assert res.y1 == FIRST_CITY_Y + 400 + 90 - 1
assert res.repair == 1000000
assert len(res.tiles) == 90*90
for i in range(90*90):
if i == 0:
assert res.tiles[i].data == [ITEM_STONE, ITEM_STONE, ITEM_WOOD]
elif i == 90:
assert res.tiles[i].data == [ITEM_STONE, 0, ITEM_WOOD]
elif i == 180:
assert res.tiles[i].data == [0, 0, ITEM_VEGETATION]
else:
assert not 'data' in res.tiles[i]
# embedded palette changes
res = self.wallet[2].cc_build(flag_id, 0, 3, 3, 1, block_data = [9, 0, 9], encoded = False, palette = [{'index': 9, 'variant_delta': MATERIAL_VARIANT_PLOUGHED_FIELD}])
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id, get_unpacked_tiles = True)
assert len(res.tiles) == 90*90
assert res.tiles[270].data == [9]
assert 'data' not in res.tiles[271] or res.tiles[271].data == []
assert res.tiles[272].data == [9]
# locked materials
self.assert_exception(lambda: self.wallet[2].cc_build(flag_id, 0, 3, 3, 1, block_data = [10, 0, 10], encoded = False, palette = [{'index': 10, 'variant_delta': MATERIAL_LOCKED_PINE}]))
# can't build using placeholder
self.assert_exception(lambda: self.wallet[2].cc_build(flag_id, 0, 3, 3, 1, block_data = [10, 0, 10], encoded = False, palette = [{'index': 10, 'variant_delta': MATERIAL_PLACEHOLDER}]))
print("Testing assign items")
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_WOOD, 'amount': 10}, {'type': ITEM_STONE, 'amount': 10}, {'type': ITEM_LABOUR, 'amount': 1}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
self.assert_exception(lambda self: self.wallet[2].cc_assign_items(flag_id, [{'type': ITEM_STONE, 'amount': 2000000000}]))
self.assert_exception(lambda self: self.wallet[2].cc_assign_items(flag_id, [{'type': ITEM_WOOD, 'amount': 0}]))
self.assert_exception(lambda self: self.wallet[2].cc_assign_items(flag_id, [{'type': 0, 'amount': 1}]))
self.wallet[2].cc_assign_items(flag_id, [{'type': ITEM_WOOD, 'amount': 1}, {'type': ITEM_STONE, 'amount': 10}, {'type': ITEM_LABOUR, 'amount': 10}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
print("Testing renaming flag")
self.assert_exception(lambda self: self.wallet[2].cc_rename_flag(flag_id, old_name = 'nope', new_name = 'good name'))
self.assert_exception(lambda self: self.wallet[2].cc_rename_flag(flag_id, old_name = 'test building', new_name = 'bad\rname'))
self.assert_exception(lambda self: self.wallet[2].cc_rename_flag(flag_id, old_name = 'test building', new_name = 'long name' + 'a'*128))
self.wallet[2].cc_rename_flag(flag_id, old_name = 'test building', new_name = 'good name')
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
print("Testing invalid demolishing")
res = daemon.cc_get_flag(flag_id, get_packed_tiles = True)
service_price = res.service_price if 'service_price' in res else 0
tiles = res.tiles if 'tiles' in res else []
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = ROLE_CRAFT, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = 130, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair - 1, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height + 1, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height + 1, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price + 1, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = "oops", ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = not res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = not res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = 2-res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height + 1, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks + 1, budget = res.budget, tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget + [{'type': ITEM_GRANITE, 'amount': 1}], tiles = tiles))
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = service_price, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = tiles + [1]))
def test_runestones(self):
daemon = self.daemon
print("Testing runestones")
flag_id = 3
self.wallet[2].cc_buy_items(entries = [{'type': ITEM_RUNESTONE, 'amount': 2}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
self.wallet[2].cc_assign_items(flag_id, [{'type': ITEM_RUNESTONE, 'amount': 2}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = self.wallet[2].cc_build(flag_id, 2, 4, 1, 1, block_data = [ITEM_RUNESTONE], encoded = False, build_height = 1)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# bad flag
self.assert_exception(lambda: self.wallet[2].cc_carve_runestone(flag_id + 1, 2, 4, 1, "", "", 0))
# bad owner
self.assert_exception(lambda: self.wallet[1].cc_carve_runestone(flag_id, 2, 4, 1, "", "", 0))
# out of the flag
self.assert_exception(lambda: self.wallet[2].cc_carve_runestone(flag_id, 200, 4, 1, "", "", 0))
self.assert_exception(lambda: self.wallet[2].cc_carve_runestone(flag_id, 2, 149, 1, "", "", 0))
# no block
self.assert_exception(lambda: self.wallet[2].cc_carve_runestone(flag_id, 2, 4, 199, "", "", 0))
# block without a runestone
self.assert_exception(lambda: self.wallet[2].cc_carve_runestone(flag_id, 0, 1, 1, "", "", 0))
# bad message
self.assert_exception(lambda: self.wallet[2].cc_carve_runestone(flag_id, 2, 4, 1, "foo", "", 0))
# no change
self.assert_exception(lambda: self.wallet[2].cc_carve_runestone(flag_id, 2, 4, 1, "", "", 0))
res = self.wallet[2].cc_carve_runestone(flag_id, 2, 4, 1, "", "foo", 0)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_carve_runestone(flag_id, 2, 4, 1, "foo", "bar", 0)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# can't be demolished with a carved runestone on it
res = daemon.cc_get_flag(flag_id)
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, res.role, res.economic_power, res.repair, res.construction_height, res.last_service_height, res.service_price if 'service_price' in res else 0, res.name if 'name' in res else '', res.ignore, res.active, res.crop, res.sow_height, res.num_missed_ticks, res.budget, res.packed_tiles if 'packed_tiles' in res else []))
# remove the runestone - it can only be removed manually
res = daemon.cc_get_runestones([{'flag': flag_id, 'x': 2, 'y': 4, 'h': 1}])
assert len(res.runestones) == 1
self.assert_exception(lambda: self.wallet[2].cc_build(flag_id, 2, 4, 1, 1, block_data = [ITEM_RUNESTONE], encoded = False, build_height = 1, remove = True))
res = self.wallet[2].cc_carve_runestone(flag_id, 2, 4, 1, "bar", "", 0)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.assert_exception(lambda: daemon.cc_get_runestones([{'flag': flag_id, 'x': 2, 'y': 4, 'h': 1}]))
self.wallet[2].cc_build(flag_id, 2, 4, 1, 1, block_data = [ITEM_RUNESTONE], encoded = False, build_height = 1, remove = True)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.assert_exception(lambda: daemon.cc_get_runestones([{'flag': flag_id, 'x': 2, 'y': 4, 'h': 1}]))
# now it can be removed
res = daemon.cc_get_flag(flag_id, get_packed_tiles = True)
self.wallet[2].cc_demolish(flag_id, res.role, res.economic_power, res.repair, res.construction_height, res.last_service_height, res.service_price if 'service_price' in res else 0, res.name if 'name' in res else '', res.ignore, res.active, res.crop, res.sow_height, res.num_missed_ticks, res.budget, res.packed_tiles if 'packed_tiles' in res else [])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.assert_exception(lambda: daemon.cc_get_runestones([{'flag': flag_id, 'x': 2, 'y': 4, 'h': 1}]))
# revert the destruction
daemon.pop_blocks(1)
daemon.flush_txpool()
# add it back
res = self.wallet[2].cc_build(flag_id, 2, 4, 1, 1, block_data = [ITEM_RUNESTONE], encoded = False, build_height = 1)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.assert_exception(lambda: daemon.cc_get_runestones([{'flag': flag_id, 'x': 2, 'y': 4, 'h': 1}]))
res = self.wallet[2].cc_carve_runestone(flag_id, 2, 4, 1, "", "bar", 0)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_runestones([{'flag': flag_id, 'x': 2, 'y': 4, 'h': 1}])
assert len(res.runestones) == 1
def test_mortgages(self):
daemon = self.daemon
print("Testing mortgages")
expected_balances = [None, None, None, None]
expected_item_balances = [None, None, None, None]
account_id = [None, None, None, None]
account_public_key = [None, None, None, None]
for i in range(4):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
account_public_key[i] = res.cc_pkey
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
res = daemon.cc_get_account(account_id[3])
assert len(res.flags) > 0
other_flag_id = res.flags[-1]
res = daemon.cc_get_account(account_id[2])
assert len(res.flags) > 0
flag_id = res.flags[-1]
res = daemon.cc_get_flag(flag_id)
assert res.mortgage == 0
# wrong account
self.assert_exception(lambda: self.wallet[1].cc_create_mortgage(flag_id, "test", "test", ITEM_LABOUR, 2, 10, 0, 1, 100))
# not a flag
self.assert_exception(lambda: self.wallet[2].cc_create_mortgage(99999, "test", "test", ITEM_LABOUR, 2, 10, 0, 1, 100))
# a flag we do not own
self.assert_exception(lambda: self.wallet[2].cc_create_mortgage(other_flag_id, "test", "test", ITEM_LABOUR, 2, 10, 0, 1, 100))
# too many shares
self.assert_exception(lambda: self.wallet[2].cc_create_mortgage(flag_id, "test", "test", ITEM_LABOUR, 2, 10, 0, 1, 1000000000))
# paying invalid item
self.assert_exception(lambda: self.wallet[2].cc_create_mortgage(flag_id, "test", "test", 999999, 2, 10, 0, 1, 100))
# paying 0 per tick for at least one tick
self.assert_exception(lambda: self.wallet[2].cc_create_mortgage(flag_id, "test", "test", ITEM_LABOUR, 0, 10, 0, 1, 100))
res = daemon.get_height()
height = res.height
res = self.wallet[2].cc_create_mortgage(flag_id, "test", "test", ITEM_LABOUR, 2, 10, 0, 1, 100)
expected_balances[2] -= res.fee + NEW_MORTGAGE_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
assert len(res.items_) > 0
custom_item = res.items_[-1]
custom_item_id = custom_item.id
assert custom_item.id > 0
assert custom_item.creator == account_id[2]
assert custom_item.creation_height == height
assert custom_item.amount == 100
assert not custom_item.is_group
assert custom_item.group == MORTGAGE_ITEM_GROUP
assert not custom_item.ignore
assert custom_item.name == "test"
assert custom_item.pdesc == "test"
expected_item_balances[2] = self.add_item(expected_item_balances[2], custom_item_id, 100)
res = daemon.cc_get_item_count([custom_item_id])
assert res.counts == [100], res
res = daemon.cc_get_flag(flag_id)
assert res.mortgage == custom_item_id
# check balances
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = self.wallet[2].cc_give(account_public_key[1], [{'type': custom_item_id, 'amount': 25}])
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_item_balances[2] = self.add_item(expected_item_balances[2], custom_item_id, -25)
expected_item_balances[1] = self.add_item(expected_item_balances[1], custom_item_id, 25)
# pass an update - tick
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_LABOUR, -25 * 2)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 25 * 2)
# check item balances (gold/wood balances are unknown past the update)
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert (self.remove_wood(res.item_balances if 'item_balances' in res else [])) == self.remove_wood(expected_item_balances[i])
# can't demolish a mortgaged flag
res = daemon.cc_get_flag(flag_id, get_packed_tiles = True)
self.assert_exception(lambda: self.wallet[2].cc_demolish(flag_id, role = res.role, economic_power = res.economic_power, repair = res.repair, construction_height = res.construction_height, last_service_height = res.last_service_height, service_price = res.service_price if 'service_price' in res else 0, name = res.name, ignore = res.ignore, active = res.active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = res.budget, tiles = res.packed_tiles if 'packed_tiles' in res else []))
# nor give it
self.assert_exception(lambda: self.wallet[2].cc_give(account_public_key[0], flag = flag_id))
# not trade it
res = daemon.get_height()
current_height = res.height
self.assert_exception(lambda: self.wallet[2].cc_trade_flag(False, flag_id, 1000, 0, 0, 0, 0, expiration = current_height + MIN_TRADE_EXPIRATION + 1000))
# pass an update - maturity
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_LABOUR, -25 * 10)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 25 * 10)
expected_item_balances[2] = self.add_item(expected_item_balances[2], custom_item_id, -75)
expected_item_balances[1] = self.add_item(expected_item_balances[1], custom_item_id, -25)
# check item balances (gold/wood balances are unknown past the update)
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert (self.remove_wood(res.item_balances if 'item_balances' in res else [])) == self.remove_wood(expected_item_balances[i])
# pass an update - no further changes
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
# check item balances (gold/wood balances are unknown past the update)
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert (self.remove_wood(res.item_balances if 'item_balances' in res else [])) == self.remove_wood(expected_item_balances[i])
res = daemon.cc_get_flag(flag_id)
assert res.mortgage == 0
res = daemon.cc_get_item_count([custom_item_id])
assert res.counts == [0], res
# a defaulting mortgage on the same flag
res = daemon.cc_get_flag(flag_id)
assert res.mortgage == 0
# get rid of all labour, buy a known amount
res = daemon.cc_get_account(account_id[2])
labour = 0
if 'item_balances' in res:
for e in res.item_balances:
if e['type'] == ITEM_LABOUR:
labour += int(e['amount'])
if labour > 0:
res = self.wallet[2].cc_give(account_public_key[1], [{'type': ITEM_LABOUR, 'amount': labour}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_LABOUR, 'amount': 159}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
for i in range(4):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
account_public_key[i] = res.cc_pkey
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
res = daemon.get_height()
height = res.height
res = self.wallet[2].cc_create_mortgage(flag_id, "test2", "test2", ITEM_LABOUR, 2, 10, 0, 1, 100)
expected_balances[2] -= res.fee + NEW_MORTGAGE_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
assert len(res.items_) > 0
custom_item = res.items_[-1]
custom_item_id = custom_item.id
assert custom_item.id > 0
assert custom_item.creator == account_id[2]
assert custom_item.creation_height == height
assert custom_item.amount == 100
assert not custom_item.is_group
assert custom_item.group == MORTGAGE_ITEM_GROUP
assert not custom_item.ignore
assert custom_item.name == "test2"
assert custom_item.pdesc == "test2"
expected_item_balances[2] = self.add_item(expected_item_balances[2], custom_item_id, 100)
res = daemon.cc_get_item_count([custom_item_id])
assert res.counts == [100], res
res = daemon.cc_get_flag(flag_id)
assert res.mortgage == custom_item_id
res = self.wallet[2].cc_give(account_public_key[1], [{'type': custom_item_id, 'amount': 80}])
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_item_balances[2] = self.add_item(expected_item_balances[2], custom_item_id, -80)
expected_item_balances[1] = self.add_item(expected_item_balances[1], custom_item_id, 80)
# check balances
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# we need 160 labour, and only have 159, we will default after spending 80
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_LABOUR, -80)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 80)
res = daemon.get_info()
mortgage_default_height = res.height - 1
# check balances, gold/wood are unknown after the tick
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert (self.remove_wood(res.item_balances if 'item_balances' in res else [])) == self.remove_wood(expected_item_balances[i])
res = daemon.cc_get_account(account_id[2])
assert 'flags' not in res or flag_id not in res.flags
res = daemon.cc_get_account(FORECLOSURE_ACCOUNT)
assert res.flags == [flag_id]
# give back labour we got earlier
if labour > 0:
res = self.wallet[1].cc_give(account_public_key[2], [{'type': ITEM_LABOUR, 'amount': labour}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# there should be an auction now
res = daemon.cc_get_auctions()
assert len(res.auctions) == 1
assert res.auctions[0].seller == account_id[2]
assert res.auctions[0].type == AUCTION_TYPE_FLAG
assert len(res.auctions[0].entries) == 1
assert res.auctions[0].entries[0].object == flag_id
assert res.auctions[0].entries[0].amount == 1
assert res.auctions[0].mortgage == custom_item_id
assert res.auctions[0].creation_height == mortgage_default_height
assert res.auctions[0].bid_account == 0 # none yet
assert res.auctions[0].bid_price == 0 # none yet
assert res.auctions[0].bid_height == 0 # none yet
# wait for the lifetime, but if no bids, it should not end yet
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
blocks = AUCTION_TIME + GAME_UPDATE_FREQUENCY
while blocks > 0:
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY)
blocks -= GAME_UPDATE_FREQUENCY
# there should be an auction now
res = daemon.cc_get_auctions()
assert len(res.auctions) == 1
assert res.auctions[0].mortgage == custom_item_id
# bid once
self.wallet[2].cc_auction_bid(1, 500)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.get_info()
height = res.height
res = daemon.cc_get_auctions()
assert len(res.auctions) == 1
assert res.auctions[0].mortgage == custom_item_id
assert res.auctions[0].bid_price == 500
assert res.auctions[0].bid_account == account_id[2]
assert res.auctions[0].bid_height == height - 1
# invalid bids
self.assert_exception(lambda: self.wallet[1].cc_auction_bid(2, 600)) # bad auction
self.assert_exception(lambda: self.wallet[1].cc_auction_bid(1, 6000000000000)) # too expensive
self.assert_exception(lambda: self.wallet[1].cc_auction_bid(1, 400)) # lower
self.assert_exception(lambda: self.wallet[1].cc_auction_bid(1, 500)) # no higher
# bid again
self.wallet[0].cc_auction_bid(1, 550)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.get_info()
height = res.height
res = daemon.cc_get_auctions()
assert len(res.auctions) == 1
assert res.auctions[0].mortgage == custom_item_id
assert res.auctions[0].bid_price == 550
assert res.auctions[0].bid_account == account_id[0]
assert res.auctions[0].bid_height == height - 1
# wait a couple updates
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY * 2)
res = daemon.cc_get_flag(flag_id)
assert res.owner == account_id[0]
res = daemon.cc_get_auctions()
assert 'auctions' not in res or len(res.auctions) == 0
# can't bid anymore
self.assert_exception(lambda: self.wallet[0].cc_auction_bid(1, 850))
def test_auctions(self):
daemon = self.daemon
print("Testing auctions")
expected_balances = [None, None, None, None]
expected_item_balances = [None, None, None, None]
account_id = [None, None, None, None]
account_public_key = [None, None, None, None]
for i in range(4):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
account_public_key[i] = res.cc_pkey
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
res = daemon.cc_get_auctions()
num_auctions_at_start = len(res.auctions) if 'auctions' in res else 0
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 100}, {'type': ITEM_RUNESTONE, 'amount': 10}, {'type': ITEM_LABOUR, 'amount': 25}])
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_STONE, 100)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_RUNESTONE, 10)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_LABOUR, 25)
expected_balances[2] -= res.fee
expected_balances[2] -= 100 * STONE_LAST_RESORT_PRICE
expected_balances[2] -= 10 * RUNESTONE_LAST_RESORT_PRICE
expected_balances[2] -= 25 * LABOUR_LAST_RESORT_PRICE
res = self.wallet[1].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 30}])
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_STONE, 30)
expected_balances[1] -= res.fee
expected_balances[1] -= 30 * STONE_LAST_RESORT_PRICE
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account_id[2])
num_stone = 0
for e in res.item_balances:
if e.type == ITEM_STONE:
assert num_stone == 0
num_stone += e.amount
assert num_stone >= 10
# invalid type
self.assert_exception(lambda: self.wallet[2].cc_create_auction(90, [{'object': ITEM_STONE, 'amount': 1}], 8))
# invalid item
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_LAST_USER + 1, 'amount': 1}], 8))
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': 255, 'amount': 1}], 8))
# 0 items
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 0}], 8))
# too many items
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': num_stone + 1}], 8))
# too short
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 10}], MIN_AUCTION_BASE_TICKS - 1))
# too long
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 10}], MAX_AUCTION_BASE_TICKS + 1))
# bad name
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 10}], 8, "aaaa\n"))
# bad description
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 10}], 8, "", "\01"))
# unsorted items
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_LABOUR, 'amount': 10}, {'object': ITEM_STONE, 'amount': 10}]))
# duplicated item
self.assert_exception(lambda: self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 10}, {'object': ITEM_STONE, 'amount': 10}]))
# check balances
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 10}], 8, "title", "This is\nits description")
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
expected_balances[2] -= res.fee
expected_balances[2] -= NEW_AUCTION_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_STONE, -10) # goes to reserve
# check balances
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_account(account_id[2])
for e in res.item_balances:
if e.type == ITEM_STONE:
assert e.amount == num_stone - 10
res = daemon.cc_get_auctions()
assert len(res.auctions) == num_auctions_at_start + 1
auction_id = res.auctions[-1].id
assert res.auctions[-1].title == "title"
assert res.auctions[-1].description == "This is\nits description"
# wait till past timeout
for i in range(8 + 1):
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY)
# if no bids, the auction carries over for another tick
res = daemon.cc_get_auctions()
assert len(res.auctions) == num_auctions_at_start + 1
# buy it on the cheap
res = self.wallet[2].cc_auction_bid(auction_id, 12345)
expected_balances[2] -= res.fee
expected_balances[2] -= 12345
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY)
res = daemon.cc_get_auctions()
assert (len(res.auctions) if 'auctions' in res else 0) == num_auctions_at_start
# not sold (well, sold back to seller), goes back from reserve
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_STONE, 10)
# check item balances (gold/wood balances are unknown past the update)
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert (self.remove_wood(res.item_balances if 'item_balances' in res else [])) == self.remove_wood(expected_item_balances[i])
print('Testing overlapping auctions')
res = self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 30}, {'object': ITEM_LABOUR, 'amount': 15}], 8)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
auction_2_stone30_id = auction_id + 1
expected_balances[2] -= res.fee + NEW_AUCTION_FEE
res = self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_RUNESTONE, 'amount': 2}], 10)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
auction_2_runestone2_id = auction_id + 2
expected_balances[2] -= res.fee + NEW_AUCTION_FEE
res = self.wallet[1].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 5}, {'object': ITEM_LABOUR, 'amount': 1}], 8)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
auction_1_stone5_id = auction_id + 3
expected_balances[1] -= res.fee + NEW_AUCTION_FEE
res = self.wallet[1].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': ITEM_STONE, 'amount': 10}], 9)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
auction_1_stone10_id = auction_id + 4
expected_balances[1] -= res.fee + NEW_AUCTION_FEE
res = daemon.cc_get_auctions()
assert len(res.auctions) == num_auctions_at_start + 4
# sanity - give 500 leeway since we might have got prestige income
self.assert_exception(lambda: self.wallet[2].cc_auction_bid(auction_2_stone30_id, expected_balances[2] + 100000000 * 500))
# 2 bid a few times
res = self.wallet[2].cc_auction_bid(auction_1_stone10_id, 1000)
expected_balances[2] -= res.fee
res = self.wallet[2].cc_auction_bid(auction_2_stone30_id, 500)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 2)
# lower bids now forbidden
self.assert_exception(lambda: self.wallet[2].cc_auction_bid(auction_2_stone30_id, 499))
self.assert_exception(lambda: self.wallet[2].cc_auction_bid(auction_2_stone30_id, 498))
self.assert_exception(lambda: self.wallet[1].cc_auction_bid(auction_2_stone30_id, 499))
self.assert_exception(lambda: self.wallet[1].cc_auction_bid(auction_2_stone30_id, 498))
# 1 then bids too
res = self.wallet[1].cc_auction_bid(auction_1_stone10_id, 1200)
expected_balances[1] -= res.fee
res = self.wallet[1].cc_auction_bid(auction_2_stone30_id, 700)
expected_balances[1] -= res.fee
res = self.wallet[1].cc_auction_bid(auction_2_runestone2_id, 2600)
expected_balances[1] -= res.fee
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 3)
# 2 bids back
res = self.wallet[2].cc_auction_bid(auction_1_stone10_id, 1250)
expected_balances[2] -= res.fee
res = self.wallet[2].cc_auction_bid(auction_2_stone30_id, 900)
expected_balances[2] -= res.fee
res = self.wallet[2].cc_auction_bid(auction_2_runestone2_id, 3000)
expected_balances[2] -= res.fee
res = self.wallet[2].cc_auction_bid(auction_1_stone5_id, 150)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 4)
# 1 wins on some
res = self.wallet[1].cc_auction_bid(auction_2_stone30_id, 940)
expected_balances[1] -= res.fee
res = self.wallet[1].cc_auction_bid(auction_2_runestone2_id, 3500)
expected_balances[1] -= res.fee
res = self.wallet[1].cc_auction_bid(auction_1_stone5_id, 3500)
expected_balances[1] -= res.fee
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 3)
# wait till the end of all auctions
for i in range(10 + 1):
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY)
res = daemon.cc_get_auctions()
assert (len(res.auctions) if 'auctions' in res else 0) == num_auctions_at_start
# end state: 1 wins the two auctions from 2 and one of theirs, 2 wins one auction from 1
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_STONE, -30)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_STONE, 30)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_LABOUR, -15)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 15)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_RUNESTONE, -2)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_RUNESTONE, 2)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_STONE, -10)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_STONE, 10)
# check item balances (gold/wood balances are unknown past the update)
for i in range(4):
res = daemon.cc_get_account(account_id[i])
assert (self.remove_wood(res.item_balances if 'item_balances' in res else [])) == self.remove_wood(expected_item_balances[i])
print('Testing maturing mortage auctions')
# create new flag
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 8192}, {'type': ITEM_WOOD, 'amount': 25000}, {'type': ITEM_LABOUR, 'amount': 250000}])
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X - 1200, FIRST_CITY_Y - 400, FIRST_CITY_X - 1200 + 64 - 1, FIRST_CITY_Y - 400 + 64 - 1)
res = self.wallet[2].cc_buy_land(FIRST_CITY_X - 1200, FIRST_CITY_Y - 400, FIRST_CITY_X - 1200 + 64 - 1, FIRST_CITY_Y - 400 + 64 - 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id[2])
flag_id = res.flags[-1]
res = daemon.cc_get_flag(flag_id)
construction_height = res.construction_height
res = self.wallet[2].cc_building_settings(flag_id, ROLE_RESIDENTIAL1, 100, construction_height)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# mortgage it
res = daemon.get_height()
height = res.height
res = self.wallet[2].cc_create_mortgage(flag_id, "test mortgage auction", "test", ITEM_LABOUR, 2, 10, 0, 2, 3)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id[2])
item_id = res.item_balances[-1]['type']
res = daemon.cc_get_custom_items([item_id])
assert 'items_' in res and len(res.items_) == 1
assert res.items_[0].id == item_id
assert res.items_[0].name == "test mortgage auction"
assert not res.items_[0].is_group
assert res.items_[0].group == MORTGAGE_ITEM_GROUP
assert res.items_[0].creator == account_id[2]
assert res.items_[0].amount == 3
# auction mortgage in three lots
auction_id = [0, 0, 0]
for i in range(3):
res = self.wallet[2].cc_create_auction(AUCTION_TYPE_ITEM, [{'object': item_id, 'amount': 1}], MIN_AUCTION_BASE_TICKS)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_auctions()
auction_id[i] = res.auctions[-1].id
# bid on one, have another bid on another, and leave the last one alone
res = self.wallet[2].cc_transfer(self.pkeys[1], amount = AUCTION_BID_FEE + 500)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.wallet[2].refresh()
self.wallet[2].cc_auction_bid(auction_id[2], 500)
self.wallet[1].refresh()
self.wallet[1].cc_auction_bid(auction_id[1], 500)
# wait till the end of the auctions
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
blocks = (MIN_AUCTION_BASE_TICKS + 2) * GAME_UPDATE_FREQUENCY + GAME_UPDATE_FREQUENCY
while blocks > 0:
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY)
blocks -= GAME_UPDATE_FREQUENCY
# they should all be over, including the one that did not get any bid, since the item is gone
res = daemon.cc_get_auctions()
auction_found = [False, False, False]
if 'auctions' in res:
for e in res.auctions:
for i in range(3):
if e.id == auction_id[i]:
print('found auction: ' + str(e))
assert not auction_found[i]
auction_found[i] = True
assert not auction_found[0]
assert not auction_found[1]
assert not auction_found[2]
# there should be no item left, and inventories/reserves should not have any either
res = daemon.cc_get_custom_items([item_id])
assert len(res.items_) == 1
assert res.items_[0].amount == 0
res = daemon.cc_get_item_count([item_id])
assert len(res.counts) == 1
assert res.counts[0] == 0
res = daemon.cc_get_item_ownership(item_id)
assert 'ownership' not in res or len(res.ownership) == 0
for i in range(len(account_id)):
res = daemon.cc_get_account(account_id[i])
for e in res.item_balances:
assert e.type != item_id
if 'reserves' in res:
for f in res.reserves:
if 'items_' in f:
for e in f.items_:
assert e.type != item_id
def test_trading(self):
daemon = self.daemon
print("Testing trading")
res = daemon.get_height()
base_height = res.height
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
expected_balances = [None, None, None]
expected_item_balances = [None, None, None]
account_id = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
res = daemon.cc_get_order_book(True, True)
assert not 'offers' in res or len(res.offers) == 0
assert not 'bids' in res or len(res.bids) == 0
# 2 has blocks, set some of them up for sale
res = self.wallet[2].cc_trade_item(False, 1, 5, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000)
assert len(res.tx_hash) == 64
offer_1_5_80_nonce = res.cc_nonce
assert offer_1_5_80_nonce > 0
offer_1_5_80_fee = res.fee
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
assert not 'bids' in res or len(res.bids) == 0
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert trade.account == account_id[2]
assert not trade.mined
assert not trade.bid
assert trade.type == 1
assert trade.id == 1
assert trade.amount == 5
assert trade.matchable == 5
assert trade.price == 80
assert trade.expiration == base_height + MIN_TRADE_EXPIRATION + 1000
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
assert not 'bids' in res or len(res.bids) == 0
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert not trade.mined # not matched, stays in the txpool
# add a compatible bid, but do not match, they'll coexist
res = self.wallet[1].cc_trade_item(True, 1, 2, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000)
assert len(res.tx_hash) == 64
bid_1_2_80_a_nonce = res.cc_nonce
assert bid_1_2_80_a_nonce > 0
bid_1_2_80_a_fee = res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
assert len(res.bids) == 1
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert trade.account == account_id[2]
assert not trade.mined # not matched, stays in the txpool
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.account == account_id[1]
assert not trade.mined # not matched, stays in the txpool
# add a matching one - first matching too few
ok = False
try: self.wallet[1].cc_trade_item(True, 1, 2, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 1}], cost = 80 * 1)
except: ok = True
assert ok
# then matching enough
res = self.wallet[1].cc_trade_item(True, 1, 2, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 2}], cost = 80 * 2)
assert len(res.tx_hash) == 64
bid_1_2_80_b_nonce = res.cc_nonce
assert bid_1_2_80_b_nonce > 0
bid_1_2_80_b_fee = res.fee
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
assert len(res.bids) == 2
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert not trade.mined # not matched yet
assert trade.amount == 5
assert trade.matchable == 5
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce or trade.nonce == bid_1_2_80_b_nonce
assert not trade.mined # not matched, stays in the txpool
trade = res.bids[1]
assert not trade.mined # not matched, stays in the txpool
# test filtering
res2 = daemon.cc_get_order_book(True, False)
assert res2.bids == res.bids
assert not 'offers' in res2
res2 = daemon.cc_get_order_book(False, True)
assert not 'bids' in res2
assert res2.offers == res.offers
res2 = daemon.cc_get_order_book(True, True, type=[234])
assert not 'offers' in res2
assert not 'bids' in res2
res2 = daemon.cc_get_order_book(True, True, id=[234])
assert not 'offers' in res2
assert not 'bids' in res2
res2 = daemon.cc_get_order_book(True, True, type=[1])
assert res2.offers == res.offers
assert res2.bids == res.bids
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[2] -= offer_1_5_80_fee
expected_balances[1] -= bid_1_2_80_b_fee
matched_amount = 2
matched_price = 80
expected_balances[1] -= matched_amount * matched_price
expected_item_balances[1] = self.add_item(expected_item_balances[1], 1, matched_amount)
expected_balances[2] += matched_amount * matched_price
expected_item_balances[2] = self.add_item(expected_item_balances[2], 1, -matched_amount)
# the offer should still be around, with 2 used and 3 left, and the last bid is gone
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
assert len(res.bids) == 1
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert trade.account == account_id[2]
assert trade.mined # but mined, and partially matched
assert trade.amount == 5
assert trade.matchable == 3
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.account == account_id[1]
assert not trade.mined # still not matched
assert trade.amount == 2
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# pop the block, we should get back to the same state
daemon.pop_blocks(1)
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
assert len(res.bids) == 2
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert not trade.mined # not matched yet
assert trade.amount == 5
assert trade.matchable == 5
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce or trade.nonce == bid_1_2_80_b_nonce
assert not trade.mined # not matched, stays in the txpool
trade = res.bids[1]
assert not trade.mined # not matched, stays in the txpool
# mine a block again
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# the offer should still be around, with 2 used and 3 left, and the last bid is gone
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
assert len(res.bids) == 1
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert trade.account == account_id[2]
assert trade.mined # but mined, and partially matched
assert trade.amount == 5
assert trade.matchable == 3
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.account == account_id[1]
assert not trade.mined # still not matched
assert trade.amount == 2
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# current orders
# 2 sells 3 at 80
# 1 buys 2 at 80
# matching to a non existing tx
ok = False
try: self.wallet[0].cc_trade_item(True, 1, 1, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'txid': "4"*64, 'amount': 1}], cost = 80)
except: ok = True
assert ok
# matching to an existing tx that's not a trade command
res = daemon.getblockheaderbyheight(50)
miner_tx_hash = res.block_header.miner_tx_hash
ok = False
try: self.wallet[0].cc_trade_item(True, 1, 1, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': miner_tx_hash, 'amount': 1}], cost = 80)
except: ok = True
assert ok
# matching with non matching price
ok = False
try: self.wallet[0].cc_trade_item(True, 1, 1, 79, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 1}], cost = 80)
except: ok = True
assert ok
# matching too much
ok = False
try: self.wallet[0].cc_trade_item(True, 1, 5, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 5}], cost = 800)
except: ok = True
assert ok
# matching nothing
ok = False
try: self.wallet[0].cc_trade_item(True, 1, 1, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 0}], cost = 1)
except: ok = True
assert ok
# matching the same tx twice
ok = False
try: self.wallet[0].cc_trade_item(True, 1, 2, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 1}, {'nonce': offer_1_5_80_nonce, 'amount': 1}], cost = 160)
except: ok = True
assert ok
# matching with oneself's order
ok = False
try: self.wallet[2].cc_trade_item(True, 1, 1, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 1}], cost = 80)
except: ok = True
assert ok
# matching with a better price uses the matched trade's price
res = self.wallet[0].cc_trade_item(True, 1, 1, 140, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 1}], cost = 80)
assert len(res.tx_hash) == 64
expected_balances[0] -= res.fee
expected_balances[0] -= 80
expected_balances[2] += 80
expected_item_balances[0] = self.add_item(expected_item_balances[0], 1, 1)
expected_item_balances[2] = self.add_item(expected_item_balances[2], 1, -1)
res = self.wallet[2].cc_trade_item(False, 1, 1, 40, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': bid_1_2_80_a_nonce, 'amount': 1}], cost = 0)
assert len(res.tx_hash) == 64
expected_balances[2] -= res.fee
expected_balances[2] += 80
expected_balances[1] -= 80
expected_item_balances[2] = self.add_item(expected_item_balances[2], 1, -1)
expected_item_balances[1] = self.add_item(expected_item_balances[1], 1, 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[1] -= bid_1_2_80_a_fee # finally!
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# the offer should still be around, with 3 used and 2 left, and the last bid is gone
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
assert len(res.bids) == 1
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert trade.mined
assert trade.amount == 5
assert trade.matchable == 2
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.mined
assert trade.amount == 2
assert trade.matchable == 1
# current orders
# 2 sells 2 at 80
# 1 buys 1 at 80
# add several offers in a ladder
res = self.wallet[2].cc_trade_item(False, 1, 1, 100, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000)
assert len(res.tx_hash) == 64
offer_1_1_100_nonce = res.cc_nonce
assert offer_1_1_100_nonce > 0
offer_1_1_100_fee = res.fee
res = self.wallet[1].cc_trade_item(False, 1, 3, 85, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000)
assert len(res.tx_hash) == 64
offer_1_3_85_nonce = res.cc_nonce
assert offer_1_3_85_nonce > 0
offer_1_3_85_fee = res.fee
res = self.wallet[2].cc_trade_item(False, 1, 2, 90, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000)
assert len(res.tx_hash) == 64
offer_1_2_90_nonce = res.cc_nonce
assert offer_1_2_90_nonce > 0
offer_1_2_90_fee = res.fee
# the new offer should all be there, sorted by price
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 4
assert len(res.bids) == 1
trade = res.offers[0]
assert trade.nonce == offer_1_5_80_nonce
assert trade.account == account_id[2]
assert trade.mined
assert trade.amount == 5
assert trade.matchable == 2
trade = res.offers[1]
assert trade.nonce == offer_1_3_85_nonce
assert trade.account == account_id[1]
assert not trade.mined
assert trade.amount == 3
trade = res.offers[2]
assert trade.nonce == offer_1_2_90_nonce
assert trade.account == account_id[2]
assert not trade.mined
assert trade.amount == 2
trade = res.offers[3]
assert trade.nonce == offer_1_1_100_nonce
assert trade.account == account_id[2]
assert not trade.mined
assert trade.amount == 1
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.account == account_id[1]
assert trade.mined
assert trade.amount == 2
assert trade.matchable == 1
# we can't match more than one unmined tx
ok = False
try: self.wallet[0].cc_trade_item(True, 1, 4, 100, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_3_85_nonce, 'amount': 3}, {'nonce': offer_1_2_90_nonce, 'amount': 1}], cost = 85 * 3 + 90 * 1)
except: ok = True
assert ok
# we can't grab only part of one while leaving some for another
ok = False
try: self.wallet[0].cc_trade_item(True, 1, 4, 100, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 1}, {'nonce': offer_1_2_90_nonce, 'amount': 1}], cost = 85 + 90)
except: ok = True
assert ok
# nibble at offer_1_3_85_nonce so it gets mined
res = self.wallet[0].cc_trade_item(True, 1, 1, 100, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_3_85_nonce, 'amount': 1}], cost = 85)
expected_balances[0] -= res.fee
expected_balances[0] -= 85
expected_item_balances[0] = self.add_item(expected_item_balances[0], 1, 1)
expected_balances[1] -= offer_1_3_85_fee
expected_balances[1] += 85
expected_item_balances[1] = self.add_item(expected_item_balances[1], 1, -1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# match several txes, once of them partially
res = self.wallet[0].cc_trade_item(True, 1, 5, 100, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_5_80_nonce, 'amount': 2}, {'nonce': offer_1_2_90_nonce, 'amount': 2}, {'nonce': offer_1_3_85_nonce, 'amount': 1}], cost = 2 * 80 + 2 * 90 + 1 * 85)
expected_balances[0] -= res.fee
expected_balances[0] -= 2 * 80 + 2 * 90 + 1 * 85
expected_item_balances[0] = self.add_item(expected_item_balances[0], 1, 5)
expected_balances[2] -= offer_1_2_90_fee
expected_balances[2] += 2 * 80 + 2 * 90
expected_item_balances[2] = self.add_item(expected_item_balances[2], 1, -4)
expected_balances[1] += 1 * 85
expected_item_balances[1] = self.add_item(expected_item_balances[1], 1, -1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# expiration too high
ok = False
try: self.wallet[2].cc_trade_item(False, 1, 1, 80, 0, 0, 0, 821500)
except: ok = True
assert ok
# expiration in the past
ok = False
try: self.wallet[2].cc_trade_item(False, 1, 1, 80, 0, 0, 0, 10)
except: ok = True
assert ok
# expiration too low
res = daemon.get_height()
height = res.height
ok = False
try: self.wallet[2].cc_trade_item(False, 1, 1, 80, 0, 0, 0, res.height + 100)
except: ok = True
assert ok
# current orders
# 2 sells 1 at 100
# 1 sells 1 at 85
# 1 buys 1 at 80
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 2
trade = res.offers[0]
assert trade.nonce == offer_1_3_85_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 3
assert trade.matchable == 1
assert trade.price == 85
assert len(res.bids) == 1
trade = res.offers[1]
assert trade.nonce == offer_1_1_100_nonce
assert not trade.mined
assert trade.account == account_id[2]
assert trade.amount == 1
assert trade.matchable == 1
assert trade.price == 100
assert len(res.bids) == 1
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 2
assert trade.matchable == 1
assert trade.price == 80
# matching a tx from an account that does not have money to back it anymore
self.wallet[1].refresh()
res = self.wallet[1].cc_withdraw(amount = expected_balances[1])
self.deposits -= expected_balances[1]
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[1] = 0
self.wallet[1].refresh()
res = daemon.cc_get_account(account_id[1])
assert res.balance == 0
ok = False
try: res = self.wallet[2].cc_trade_item(False, 1, 1, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': bid_1_2_80_a_nonce, 'amount': 1}], cost = 0)
except: ok = True
assert ok
self.wallet[1].refresh()
res = self.wallet[1].cc_deposit(amount = 1000000000)
self.deposits += 1000000000
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[1] = 1000000000
self.wallet[1].refresh()
res = daemon.cc_get_account(account_id[1])
assert res.balance == expected_balances[1]
# matching a tx from an account that does not have the blocks anymore
res = self.wallet[1].cc_trade_item(False, 1, 1, 90, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000)
assert len(res.tx_hash) == 64
nonce = res.cc_nonce
assert nonce > 0
expected_balances[1] -= res.fee
res = self.wallet[0].cc_trade_item(True, 1, 1, 90, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': nonce, 'amount': 1}], cost = 90)
assert len(res.tx_hash) == 64
expected_balances[0] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[1] += 90
expected_balances[0] -= 90
expected_item_balances[1] = self.add_item(expected_item_balances[1], 1, -1)
expected_item_balances[0] = self.add_item(expected_item_balances[0], 1, 1)
ok = False
try: self.wallet[2].cc_trade_item(True, 1, 1, 85, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_3_85_nonce, 'amount': 1}], cost = 85)
except: ok = True
assert ok
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# sell a flag we do not have
res = daemon.cc_get_account(account_id[2])
assert len(res.flags) >= 1
flag_id = res.flags[0]
assert flag_id > 0
ok = False
try: self.wallet[1].cc_trade_flag(False, flag_id, 1000, 0, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000)
except: ok = True
assert ok
# sell a flag we do have
res = self.wallet[2].cc_trade_flag(False, flag_id, 1000, 0, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000)
assert len(res.tx_hash) == 64
flag_nonce = res.cc_nonce
assert flag_nonce > 0
expected_balances[2] -= res.fee
# try to buy a different one
ok = False
try: self.wallet[1].cc_trade_flag(True, flag_id + 1, 1000, 0, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, match_nonce = flag_nonce, cost = 1000)
except: ok = True
assert ok
# buy it
res = self.wallet[1].cc_trade_flag(True, flag_id, 1001, 0, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, match_nonce = flag_nonce, cost = 1000)
assert len(res.tx_hash) == 64
expected_balances[1] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[2] += 1000
expected_balances[1] -= 1000
res = daemon.cc_get_account(account_id[2])
assert flag_id not in res.flags
res = daemon.cc_get_account(account_id[1])
assert len(res.flags) >= 1
assert flag_id in res.flags
# cannot buy twice
for i in [0, 1]:
ok = False
try: self.wallet[1].cc_trade_flag(True, flag_id, 1001, 0, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, match_nonce = flag_nonce, cost = 1000)
except: ok = True
assert ok
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
daemon.flush_txpool()
# current orders
# 1 sells 1 at 85
# 1 buys 1 at 80
# refill so out order can be matched
res = self.wallet[1].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 5}])
expected_balances[1] -= res.fee
item_counts[ITEM_STONE] += 5
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[1] -= 5 * STONE_LAST_RESORT_PRICE
expected_item_balances[1] = self.add_item(expected_item_balances[1], 1, 5)
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
trade = res.offers[0]
assert trade.nonce == offer_1_3_85_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 3
assert trade.matchable == 1
assert trade.price == 85
assert len(res.bids) == 1
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 2
assert trade.matchable == 1
assert trade.price == 80
# match offer_1_3_85 a second time
res = self.wallet[2].cc_trade_item(True, 1, 1, 85, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': offer_1_3_85_nonce, 'amount': 1}], cost = 85)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[2] -= 85
expected_balances[1] += 85
expected_item_balances[2] = self.add_item(expected_item_balances[2], 1, 1)
expected_item_balances[1] = self.add_item(expected_item_balances[1], 1, -1)
# current orders
# 1 buys 1 at 80
res = daemon.cc_get_order_book(True, True)
assert 'offers' not in res or len(res.offers) == 0
assert len(res.bids) == 1
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 2
assert trade.matchable == 1
assert trade.price == 80
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
# pop 1 block, we should have the order back, plus the yet unmined new one
daemon.pop_blocks(1)
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
trade = res.offers[0]
assert trade.nonce == offer_1_3_85_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 3
assert trade.matchable == 1
assert trade.price == 85
assert len(res.bids) == 2
bid_1_2_80_a_index = 0 if res.bids[0].nonce == bid_1_2_80_a_nonce else 1
trade = res.bids[bid_1_2_80_a_index]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 2
assert trade.matchable == 1
assert trade.price == 80
trade = res.bids[1 - bid_1_2_80_a_index]
assert not trade.mined
assert trade.account == account_id[2]
assert trade.amount == 1
assert trade.matchable == 1
assert trade.price == 85
# pop another (the buy_items), we should have the same order book, except one cannot be matched anymore
daemon.pop_blocks(1)
res = daemon.cc_get_order_book(True, True)
assert len(res.offers) == 1
trade = res.offers[0]
assert trade.nonce == offer_1_3_85_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 3
assert trade.matchable == 0
assert trade.price == 85
assert len(res.bids) == 2
bid_1_2_80_a_index = 0 if res.bids[0].nonce == bid_1_2_80_a_nonce else 1
trade = res.bids[bid_1_2_80_a_index]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 2
assert trade.matchable == 1
assert trade.price == 80
trade = res.bids[1 - bid_1_2_80_a_index]
assert not trade.mined
assert trade.account == account_id[2]
assert trade.amount == 1
assert trade.matchable == 1
assert trade.price == 85
# pop another two (the flag trades)
daemon.pop_blocks(2)
# pop another two (the item trades)
daemon.pop_blocks(2)
# then mine all these again
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 6)
# current orders
# 1 buys 1 at 80
res = daemon.cc_get_order_book(True, True)
assert 'offers' not in res or len(res.offers) == 0
assert len(res.bids) == 1
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.mined
assert trade.account == account_id[1]
assert trade.amount == 2
assert trade.matchable == 1
assert trade.price == 80
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
def test_interacting_trades(self):
daemon = self.daemon
print("Testing interacting trades")
res = daemon.get_height()
base_height = res.height
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
account_id = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
# sell a flag, two others buy it
res = daemon.cc_get_account(account_id[1])
assert len(res.flags) >= 1
flag_id = res.flags[0]
assert flag_id > 0
res = self.wallet[1].cc_trade_flag(False, flag_id, 1000, 0, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000)
assert len(res.tx_hash) == 64
flag_nonce = res.cc_nonce
assert flag_nonce > 0
res = self.wallet[0].cc_trade_flag(True, flag_id, 1000, 0, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, match_nonce = flag_nonce, cost = 1000)
assert len(res.tx_hash) == 64
# we can now, it just won't get mined
# ok = False
# try: self.wallet[2].cc_trade_flag(True, flag_id, 1000, 0, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, match_nonce = flag_nonce, cost = 1000)
# except: ok = True
# assert ok
# sell blocks, many people try to buy, overflowing the sold amount
daemon.get_connections()
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
res = daemon.cc_get_account(res.account_id)
res = self.wallet[2].cc_trade_item(False, 1, 10, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000)
nonce = res.cc_nonce
res = self.wallet[1].cc_trade_item(True, 1, 8, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': nonce, 'amount': 8}], cost = 8*80)
res = self.wallet[0].cc_trade_item(True, 1, 4, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': nonce, 'amount': 4}], cost = 4*80)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
daemon.flush_txpool()
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
def test_giving(self):
daemon = self.daemon
print("Testing giving")
res = daemon.get_height()
base_height = res.height
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
expected_balances = [None, None, None]
expected_item_balances = [None, None, None]
account_id = [None, None, None]
account_public_key = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
account_public_key[i] = res.cc_pkey
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
stone = 0
res = daemon.cc_get_account(account_id[1])
for i in range(len(res.item_balances)):
if res.item_balances[i]['type'] == ITEM_STONE:
stone = res.item_balances[i]['amount']
if stone > 0:
res = self.wallet[1].cc_destroy_items(ITEM_STONE, stone)
item_counts[ITEM_STONE] -= stone
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_STONE, -stone)
expected_balances[1] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[1].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 8}, {'type': ITEM_WOOD, 'amount': 4}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_STONE, 8)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_WOOD, 4)
item_counts[ITEM_STONE] += 8
item_counts[ITEM_WOOD] += 4
# first try to give more
ok = False
try: self.wallet[1].cc_give(account_public_key[0], [{'type': ITEM_STONE, 'amount': 9}])
except: ok = True
assert ok
ok = False
try: self.wallet[1].cc_give(account_public_key[0], [{'type': ITEM_WOOD, 'amount': 5}])
except: ok = True
assert ok
ok = False
try: self.wallet[1].cc_give(account_public_key[0], [{'type': ITEM_LABOUR, 'amount': 100}])
except: ok = True
assert ok
ok = False
try: self.wallet[1].cc_give(account_public_key[0], [{'type': ITEM_STONE, 'amount': 1}, {'type': ITEM_WOOD, 'amount': 5}])
except: ok = True
assert ok
self.wallet[1].cc_give(account_public_key[0], [{'type': ITEM_STONE, 'amount': 3}, {'type': ITEM_WOOD, 'amount': 2}])
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_STONE, -3)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_STONE, 3)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_WOOD, -2)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_WOOD, 2)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
# give a flag
res = daemon.cc_get_account(account_id[2])
flags2 = res.flags if 'flags' in res else []
assert len(flags2) > 0
res = daemon.cc_get_account(account_id[0])
flags0 = res.flags if 'flags' in res else []
flag_id = flags2[-1]
self.wallet[2].cc_give(account_public_key[0], flag = flag_id)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id[2])
flags2new = res.flags if 'flags' in res else []
assert len(flags2) > 0
res = daemon.cc_get_account(account_id[0])
flags0new = res.flags if 'flags' in res else []
assert flag_id not in flags2new
assert flag_id in flags0new
res = daemon.cc_get_flag(flag_id)
assert res.owner == account_id[0]
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
# give back
self.wallet[0].cc_give(account_public_key[2], flag = flag_id)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id[2])
flags2new = res.flags if 'flags' in res else []
assert len(flags2) > 0
res = daemon.cc_get_account(account_id[0])
flags0new = res.flags if 'flags' in res else []
assert flag_id in flags2new
assert flag_id not in flags0new
res = daemon.cc_get_flag(flag_id)
assert res.owner == account_id[2]
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
def test_accrual_trades(self):
daemon = self.daemon
print("Testing accrual trades")
res = daemon.get_height()
base_height = res.height
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
account_id = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
res = self.wallet[0].cc_buy_items(entries = [{'type': ITEM_LABOUR, 'amount': 1}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
item_counts[ITEM_LABOUR] += 1
# sell a flag, it should become buyable after a couple updates
res = daemon.cc_get_account(account_id[0])
assert len(res.flags) >= 1
flag_id = res.flags[0]
assert flag_id > 0
res = self.wallet[0].cc_trade_flag(False, flag_id, 1000, base_height + 1, -1, 0, 0, base_height + MIN_TRADE_EXPIRATION + 10000)
assert len(res.tx_hash) == 64
flag_nonce = res.cc_nonce
assert flag_nonce > 0
# sell an item too
res = self.wallet[0].cc_trade_item(False, ITEM_LABOUR, 1, 90, base_height + 1, -1, 0, base_height + MIN_TRADE_EXPIRATION + 10000)
labour_nonce = res.cc_nonce
# they should not be matchable at a lower price yet
ok = False
try: self.wallet[2].cc_trade_flag(True, flag_id, 998, 0, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, match_nonce = flag_nonce, cost = 998)
except: ok = True
assert ok
ok = False
try: self.wallet[2].cc_trade_item(True, ITEM_LABOUR, 1, 88, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': labour_nonce, 'amount': 1}], cost = 88)
except: ok = True
assert ok
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
# pass an update
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
# the update might have spoiled food, etc
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
# they should still not be matchable at a lower price yet
ok = False
try: self.wallet[2].cc_trade_flag(True, flag_id, 998, 0, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, match_nonce = flag_nonce, cost = 998)
except: ok = True
assert ok
ok = False
try: self.wallet[2].cc_trade_item(True, ITEM_LABOUR, 1, 88, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': labour_nonce, 'amount': 1}], cost = 88)
except: ok = True
assert ok
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
# pass an update
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
# the update might have spoiled food, etc
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
res = daemon.get_height()
base_height = res.height
# they should now be matchable
self.wallet[2].cc_trade_flag(True, flag_id, 998, 0, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, match_nonce = flag_nonce, cost = 998)
self.wallet[2].cc_trade_item(True, ITEM_LABOUR, 1, 88, 0, 0, 0, expiration = base_height + MIN_TRADE_EXPIRATION + 1000, matches = [{'nonce': labour_nonce, 'amount': 1}], cost = 88)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 2)
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
daemon.flush_txpool()
def test_trade_third_party_matching(self):
daemon = self.daemon
print("Testing trade third party matching")
account_id = [None, None, None]
account_pkey = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
res = daemon.cc_get_account(account_id[i])
account_pkey[i] = res.public_key
# first make sure any trade that can go in has already gone in before we start tracking balances
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 25, game_account_key = account_pkey[0])
expected_balances = [None, None, None]
expected_item_balances = [None, None, None]
for i in range(3):
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
res = daemon.get_height()
base_height = res.height
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
# give wallet 0 some more money to buy test stuff
res = self.wallet[2].cc_transfer(self.pkeys[0], amount = 150000000)
assert len(res.fee_list) == 1
expected_balances[2] -= res.fee_list[0]
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
expected_balances[2] -= 150000000
expected_balances[0] += 150000000
res = self.wallet[0].cc_buy_items(entries = [{'type': ITEM_LABOUR, 'amount': 25}, {'type': ITEM_STONE, 'amount': 10}])
expected_balances[0] -= res.fee
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_LABOUR, 25)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_STONE, 10)
expected_balances[0] -= 25 * LABOUR_LAST_RESORT_PRICE + 10 * STONE_LAST_RESORT_PRICE
item_counts[ITEM_LABOUR] += 25
item_counts[ITEM_STONE] += 10
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# a sell and a buy at the same price, without specifying a match
res = self.wallet[0].cc_trade_item(False, ITEM_LABOUR, 5, 79, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 100)
txid_0 = res.tx_hash
nonce_0 = res.cc_nonce
fee_0 = res.fee
res = self.wallet[1].cc_trade_item(True, ITEM_LABOUR, 2, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 100)
txid_1_0 = res.tx_hash
nonce_1_0 = res.cc_nonce
fee_1_0 = res.fee
res = self.wallet[1].cc_trade_item(True, ITEM_LABOUR, 2, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 100)
txid_1_1 = res.tx_hash
nonce_1_1 = res.cc_nonce
fee_1_1 = res.fee
res = self.wallet[1].cc_trade_item(True, ITEM_LABOUR, 2, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 100)
txid_1_2 = res.tx_hash
nonce_1_2 = res.cc_nonce
fee_1_2 = res.fee
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# they should still be in the txpool
res = daemon.get_transaction_pool_hashes()
assert txid_0 in res.tx_hashes
assert txid_1_0 in res.tx_hashes
assert txid_1_1 in res.tx_hashes
assert txid_1_2 in res.tx_hashes
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# match too much
ok = False
try: res = self.wallet[2].cc_match(matches = [{'buy_nonce': nonce_1_0, 'sell_nonce': nonce_0, 'amount': 6}])
except: ok = True
assert ok
# match 0 amount
ok = False
try: res = self.wallet[2].cc_match(matches = [{'buy_nonce': nonce_1_0, 'sell_nonce': nonce_0, 'amount': 0}])
except: ok = True
assert ok
# buy/sell switched
ok = False
try: res = self.wallet[2].cc_match(matches = [{'buy_nonce': nonce_0, 'sell_nonce': nonce_1_0, 'amount': 2}])
except: ok = True
assert ok
# match one
res = self.wallet[2].cc_match(matches = [{'buy_nonce': nonce_1_0, 'sell_nonce': nonce_0, 'amount': 2}])
txid_M = res.tx_hash
fee_M = res.fee
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 3)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_LABOUR, -2)
expected_balances[0] += 2 * 79
expected_balances[0] -= fee_0
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 2)
expected_balances[1] -= 2 * 80
expected_balances[1] -= fee_1_0
expected_balances[2] -= fee_M
expected_balances[2] += 2 * 1
# they should not be in the txpool anymore, except for the two unused bids
res = daemon.get_transaction_pool_hashes()
tx_hashes = res.tx_hashes if 'tx_hashes' in res else []
assert not txid_M in tx_hashes
assert not txid_0 in tx_hashes
assert not txid_1_0 in tx_hashes
assert txid_1_1 in tx_hashes
assert txid_1_2 in tx_hashes
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# we can still match some of it
res = self.wallet[2].cc_match(matches = [{'buy_nonce': nonce_1_1, 'sell_nonce': nonce_0, 'amount': 2}])
txid_M = res.tx_hash
fee_M = res.fee
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 3)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_LABOUR, -2)
expected_balances[0] += 2 * 79
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 2)
expected_balances[1] -= 2 * 80
expected_balances[1] -= fee_1_1
expected_balances[2] -= fee_M
expected_balances[2] += 2 * 1
# they should not be in the txpool anymore, except the last one
res = daemon.get_transaction_pool_hashes()
tx_hashes = res.tx_hashes if 'tx_hashes' in res else []
assert not txid_M in tx_hashes
assert not txid_0 in tx_hashes
assert not txid_1_0 in tx_hashes
assert not txid_1_1 in tx_hashes
assert txid_1_2 in tx_hashes
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# match too much for what is left (1)
ok = False
try: res = self.wallet[2].cc_match(matches = [{'buy_nonce': nonce_1, 'sell_nonce': nonce_0, 'amount': 2}])
except: ok = True
assert ok
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
daemon.flush_txpool()
print("Testing automated trade third party matching")
res = self.wallet[0].cc_buy_items(entries = [{'type': ITEM_GRANITE, 'amount': 20}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
expected_balances[0] -= res.fee
expected_balances[0] -= 20 * GRANITE_LAST_RESORT_PRICE
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_GRANITE, 20)
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.get_height()
base_height = res.height
res = self.wallet[0].cc_trade_item(False, ITEM_GRANITE, 5, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 100)
nonce_1 = res.cc_nonce
expected_balances[0] -= res.fee
res = self.wallet[0].cc_trade_item(False, ITEM_GRANITE, 15, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 100)
nonce_2 = res.cc_nonce
expected_balances[0] -= res.fee
res = self.wallet[1].cc_trade_item(True, ITEM_GRANITE, 20, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 100, matches = [{'nonce': nonce_1, 'amount': 5}], cost = 80 * 5)
expected_balances[1] -= res.fee
res = self.wallet[1].cc_trade_item(True, ITEM_GRANITE, 10, 80, 0, 0, 0, base_height + MIN_TRADE_EXPIRATION + 100, matches = [{'nonce': nonce_2, 'amount': 10}], cost = 80 * 10)
expected_balances[1] -= res.fee
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_GRANITE, -15)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_GRANITE, 15)
expected_balances[0] += 80 * 15
expected_balances[1] -= 80 * 15
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1, game_account_key = account_pkey[0])
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_GRANITE, -5)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_GRANITE, 5)
expected_balances[0] += 80 * 5
expected_balances[1] -= 80 * 5
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
def test_cancel_nonce(self):
daemon = self.daemon
print("Testing nonce cancellation")
account_id = [None, None, None]
expected_balances = [None, None, None]
expected_item_balances = [None, None, None]
account_public_key = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
account_public_key[i] = res.cc_pkey
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
daemon.flush_txpool()
res = daemon.get_info()
height = res.height
res = self.wallet[2].cc_trade_item(False, ITEM_LABOUR, 5, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 100)
txid = res.tx_hash
fee = res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.get_transaction_pool_hashes()
assert txid in res.tx_hashes
res = daemon.get_transactions([txid], get_cc_data = True)
assert len(res.txs) == 1
e = res.txs[0]
assert e.tx_hash == txid
assert e.in_pool
nonce = e.cc_nonce
# not mined yet
res = daemon.cc_is_nonce_used(nonce)
assert not res.used
# other accounts may cancel, but this will not prevent the trade from being mined
res = self.wallet[1].cc_cancel_nonces([nonce])
expected_balances[1] -= res.fee
expected_balances[1] -= NONCE_CANCELLATION_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_is_nonce_used(nonce)
assert res.used
assert res.accounts == [account_id[1]]
assert res.txid == '0' * 64
self.assert_exception(lambda: self.wallet[1].cc_cancel_nonces([nonce]))
res = self.wallet[0].cc_cancel_nonces([nonce])
expected_balances[0] -= res.fee
expected_balances[0] -= NONCE_CANCELLATION_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_is_nonce_used(nonce)
assert res.used
assert sorted(res.accounts) == sorted([account_id[1], account_id[0]])
assert res.txid == '0' * 64
res = self.wallet[1].cc_trade_item(True, ITEM_LABOUR, 5, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 10, matches = [{'nonce': nonce, 'amount': 5}], cost = 80*5)
expected_balances[1] -= res.fee
expected_balances[2] -= fee
expected_balances[1] -= 80 * 5
expected_balances[2] += 80 * 5
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 5)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_LABOUR, -5)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.get_transaction_pool_hashes()
assert 'tx_hashes' not in res or not txid in res.tx_hashes
# now points to the mined tx, the other accounts' cancellations are still there
res = daemon.cc_is_nonce_used(nonce)
assert res.used
assert sorted(res.accounts) == sorted([account_id[1], account_id[0]])
assert res.txid == txid
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i], "balance diff: " + str(res.balance - expected_balances[i])
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# again, with actual cancellation of an unmined tx
res = self.wallet[2].cc_trade_item(False, ITEM_LABOUR, 5, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 100)
txid = res.tx_hash
fee = res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.get_transaction_pool_hashes()
assert txid in res.tx_hashes
res = daemon.get_transactions([txid], get_cc_data = True)
assert len(res.txs) == 1
e = res.txs[0]
assert e.tx_hash == txid
assert e.in_pool
nonce = e.cc_nonce
res = daemon.get_transaction_pool_hashes()
assert txid in res.tx_hashes
res = daemon.cc_is_nonce_used(nonce)
assert not res.used
res = self.wallet[2].cc_cancel_nonces([nonce])
expected_balances[2] -= res.fee
expected_balances[2] -= NONCE_CANCELLATION_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_is_nonce_used(nonce)
assert res.used
assert res.accounts == [account_id[2]]
assert res.txid == '0' * 64
res = daemon.get_transaction_pool_hashes()
assert 'tx_hashes' not in res or not txid in res.tx_hashes
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# and cancellation of a partially matched tx
res = self.wallet[2].cc_trade_item(False, ITEM_LABOUR, 5, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 100)
txid = res.tx_hash
fee = res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.get_transaction_pool_hashes()
assert txid in res.tx_hashes
res = daemon.get_transactions([txid], get_cc_data = True)
assert len(res.txs) == 1
e = res.txs[0]
assert e.tx_hash == txid
assert e.in_pool
nonce = e.cc_nonce
res = daemon.get_transaction_pool_hashes()
assert txid in res.tx_hashes
res = daemon.cc_is_nonce_used(nonce)
assert not res.used
assert not 'accounts' in res or len(res.accounts) == 0
res = self.wallet[1].cc_trade_item(True, ITEM_LABOUR, 1, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 10, matches = [{'nonce': nonce, 'amount': 1}], cost = 80*1)
expected_balances[1] -= res.fee
expected_balances[2] -= fee
expected_balances[1] -= 80 * 1
expected_balances[2] += 80 * 1
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 1)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_LABOUR, -1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.get_transaction_pool_hashes()
assert 'tx_hashes' not in res or not txid in res.tx_hashes
res = self.wallet[2].cc_cancel_nonces([nonce])
expected_balances[2] -= res.fee
expected_balances[2] -= NONCE_CANCELLATION_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# the trade tx is mined, but further matches can be prevented
self.assert_exception(lambda: self.wallet[1].cc_trade_item(True, ITEM_LABOUR, 1, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 10, matches = [{'nonce': nonce, 'amount': 1}], cost = 80*1))
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# someone else can't cancel it now
self.assert_exception(lambda: self.wallet[0].cc_cancel_nonces([nonce]))
# nor ourselves
self.assert_exception(lambda: self.wallet[2].cc_cancel_nonces([nonce]))
# keep balances as we found them
res = self.wallet[1].cc_give(account_public_key[2], [{'type': ITEM_LABOUR, 'amount': 6}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
def test_game_update(self):
daemon = self.daemon
print("Testing game update")
# mine enough to pass one update
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X - 100, FIRST_CITY_Y - 100, FIRST_CITY_X - 100 + 64 - 1, FIRST_CITY_Y - 100 + 64 - 1)
cost = res.cost
res = self.wallet[2].cc_buy_land(FIRST_CITY_X - 100, FIRST_CITY_Y - 100, FIRST_CITY_X - 100 + 64 - 1, FIRST_CITY_Y - 100 + 64 - 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X - 180, FIRST_CITY_Y - 180, FIRST_CITY_X - 180 + 64 - 1, FIRST_CITY_Y - 180 + 64 - 1)
cost = res.cost
res = self.wallet[2].cc_buy_land(FIRST_CITY_X - 180, FIRST_CITY_Y - 180, FIRST_CITY_X - 180 + 64 - 1, FIRST_CITY_Y - 180 + 64 - 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
flag0_id = res.flags[-1]
assert flag0_id > 0
flag1_id = res.flags[-2]
assert flag1_id > 0
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 2949}, {'type': ITEM_WOOD, 'amount': 2150}, {'type': ITEM_LABOUR, 'amount': 50000}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag0_id)
construction_height = res.construction_height
res = self.wallet[2].cc_building_settings(flag0_id, ROLE_AGRICULTURAL, 100, construction_height)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag1_id)
construction_height = res.construction_height
res = self.wallet[2].cc_building_settings(flag1_id, ROLE_AGRICULTURAL, 100, construction_height)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
# buildings start off inactive
res = daemon.cc_get_flag(flag0_id)
assert not res.active
res = daemon.cc_get_flag(flag1_id)
assert not res.active
res = self.wallet[2].cc_build(flag0_id, 0, 0, 8, 8, block_data = [0x80 | (64-1), ITEM_WOOD], encoded = True)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
# hunt so the game update has data that depends on the seed
for i in range(3):
res = daemon.cc_get_city(0)
moose = res.moose
bears = res.bears
res = self.wallet[2].cc_hunt(target = 0, city = 0)
res = self.wallet[2].cc_hunt(target = 1, city = 0)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 2)
# mine a game update, pop it, state should be unchanged
state0 = self.get_state()
# mine enough to pass one update
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
state1 = self.get_state()
# the building with enough built blocks should now be active, but the other should not be, unless BUILD_RATIO_ACTIVE_THRESHOLD_PERCENT is 0
res = daemon.cc_get_flag(flag0_id)
assert res.active
res = daemon.cc_get_flag(flag1_id)
assert res.active == (BUILD_RATIO_ACTIVE_THRESHOLD_PERCENT == 0)
# get the last game update nonce
res = daemon.get_info()
height = res.height
res = daemon.get_block_header_by_height(height - 1)
res = daemon.get_transactions([res.block_header.miner_tx_hash], get_cc_data = True)
assert len(res.txs) == 1
first_nonce = res.txs[0].cc_nonce
daemon.pop_blocks(1)
state2 = self.get_state()
state0_clean = self.remove_crop_temperature(self.remove_height(state0))
state2_clean = self.remove_crop_temperature(self.remove_height(state2))
assert state0_clean == state2_clean, self.get_diff(state0_clean, state2_clean)
# should be the same after the game update again, since the random seed was not popped
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
state3 = self.get_state()
# get the new game update nonce
res = daemon.get_info()
height = res.height
res = daemon.get_block_header_by_height(height - 1)
res = daemon.get_transactions([res.block_header.miner_tx_hash], get_cc_data = True)
assert len(res.txs) == 1
second_nonce = res.txs[0].cc_nonce
# remove first nonce from state1 and second nonce from state3
found_nonce = False
for i in range(len(state1['used_nonces'])):
if state1['used_nonces'][i].nonce == first_nonce:
del state1['used_nonces'][i]
found_nonce = True
break
assert found_nonce
found_nonce = False
for i in range(len(state2['used_nonces'])):
if state3['used_nonces'][i].nonce == second_nonce:
del state3['used_nonces'][i]
found_nonce = True
break
assert found_nonce
state1_clean = self.remove_crop_temperature(self.remove_height(state1))
state3_clean = self.remove_crop_temperature(self.remove_height(state3))
assert state1_clean == state3_clean, self.get_diff(state1_clean, state3_clean)
# destroy all meat to avoid messing with balance comparisons
res = daemon.cc_get_account(account_id)
meat = 0
if 'item_balances' in res:
for e in res['item_balances']:
if e.type == ITEM_FOOD_MEAT:
meat += e.amount
if meat > 0:
self.wallet[2].cc_destroy_items(ITEM_FOOD_MEAT, meat)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
def get_state(self):
daemon = self.daemon
return get_daemon_state(daemon)
def copy(self, state):
return copy.deepcopy(state)
def remove_height(self, state):
new_state = self.copy(state)
del new_state['info.height']
return new_state
def remove_used_nonces(self, state):
new_state = self.copy(state)
del new_state['used_nonces']
return new_state
def remove_crop_temperature(self, state):
new_state = self.copy(state)
for key in new_state.keys():
if key.startswith('flag_') and 'crop_temperature' in new_state[key]:
del new_state[key]['crop_temperature']
return new_state
def get_diff(self, state0, state1):
s = ""
s += "state0: " + str(state0) + '\n'
s += "state1: " + str(state1) + '\n'
if state0.keys() != state1.keys():
s += 'Keys are different:\n'
s += 'state0 keys: ' + str(state0.keys()) + '\n'
s += 'state1 keys: ' + str(state1.keys()) + '\n'
else:
for key in state0.keys():
if state0[key] != state1[key]:
s += 'Data at key ' + str(key) + ' are different:\n'
s += 'state0[' + str(key) + ']: ' + str(state0[key]) + '\n'
s += 'state1[' + str(key) + ']: ' + str(state1[key]) + '\n'
return s
def test_revert_cmd(self, daemon, f, redo = True, mined = True):
state0 = self.get_state()
self.wallet[3].refresh()
res = f(self)
assert ('tx_hash' in res and len(res.tx_hash) == 64) or ('tx_hash_list' in res and len(res.tx_hash_list) > 0)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
state1 = self.get_state()
state0_clean = self.remove_crop_temperature(self.remove_height(state0))
state1_clean = self.remove_crop_temperature(self.remove_height(state1))
if mined:
assert state0_clean != state1_clean, self.get_diff(state0_clean, state1_clean)
else:
assert state0_clean == state1_clean, self.get_diff(state0_clean, state1_clean)
daemon.pop_blocks(1)
daemon.flush_txpool()
state2 = self.get_state()
assert state0 == state2, self.get_diff(state0, state2)
self.wallet[3].rescan_blockchain()
if redo:
res = f(self)
assert ('tx_hash' in res and len(res.tx_hash) == 64) or ('tx_hash_list' in res and len(res.tx_hash_list) > 0)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
state3 = self.get_state()
state1_clean = self.remove_crop_temperature(self.remove_used_nonces(state1))
state3_clean = self.remove_crop_temperature(self.remove_used_nonces(state3))
assert state1_clean == state3_clean, self.get_diff(state1_clean, state3_clean)
self.wallet[3].refresh()
def test_revert(self):
daemon = self.daemon
print("Testing reverting commands")
daemon.flush_txpool()
# sanity
state0 = self.get_state()
state1 = self.get_state()
assert state0 == state1, str(state0) + ', ' + str(state1)
# create account
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_deposit(amount = 450000000000, name = 'revert-test', inviting_account = 4))
self.deposits += 450000000000
# deposit on an existing account
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_deposit(amount = 450000000000))
self.deposits += 450000000000
# transfer
res = self.wallet[2].cc_get_info()
public_key = res.cc_pkey
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_transfer(public_key = public_key, amount = 5000000000))
# buy items
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 8 * 1500}, {'type': ITEM_STONE + 1, 'amount': 8 * 350}, {'type': ITEM_STONE + 2, 'amount': 8 * 25}, {'type': ITEM_WOOD, 'amount': 8 * 2400}, {'type': ITEM_LABOUR, 'amount': 8 * 66000}]))
# buy land
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_buy_land(FIRST_CITY_X + 100, FIRST_CITY_Y - 100, FIRST_CITY_X + 100 + 30 - 1, FIRST_CITY_Y - 100 + 30 - 1))
# building settings
res = self.wallet[3].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
assert len(res.flags) == 1
flag_id = res.flags[-1]
assert flag_id > 0
res = daemon.cc_get_flag(flag_id)
construction_height = res.construction_height
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_building_settings(flag_id, ROLE_RESIDENTIAL1, 150, construction_height, name = 'some test building'))
# build
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_build(flag_id, 0, 0, 2, 2, block_data = [3, ITEM_STONE, 0, ITEM_STONE, 0], encoded = True))
# trade
res = daemon.get_info()
height = res.height
res = self.wallet[3].cc_trade_item(False, 1, 5, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 100)
nonce = res.cc_nonce
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_trade_item(True, 1, 5, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 100), mined = False)
res = self.wallet[3].cc_trade_item(True, 1, 5, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 1000)
nonce = res.cc_nonce
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_trade_item(False, 1, 5, 80, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 100), mined = False)
res = self.wallet[3].cc_trade_flag(False, flag_id, 5000, 0, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 1000)
nonce = res.cc_nonce
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_trade_flag(True, flag_id, 5000, 0, 0, 0, 0, height + MIN_TRADE_EXPIRATION + 100), mined = False)
daemon.flush_txpool()
# assign_items
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_assign_items(flag_id, [{'type': ITEM_STONE, 'amount': 2}]))
# rename
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_rename_flag(flag_id, old_name = 'some test building', new_name = 'test-name'))
# demolish
res = daemon.cc_get_flag(flag_id, get_packed_tiles = True)
flag_width = res.x1 - res.x0 + 1
flag_height = res.y1 - res.y0 + 1
flag_role = res.role
flag_economic_power = res.economic_power
flag_repair = res.repair
flag_construction_height = res.construction_height
flag_last_service_height = res.last_service_height
flag_service_price = res.service_price if 'service_price' in res else 0
flag_name = res.name
flag_ignore = res.ignore
flag_active = res.active
flag_budget = res.budget
assert 'packed_tiles' in res
flag_tiles = res.packed_tiles
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_demolish(flag_id, role = flag_role, economic_power = flag_economic_power, repair = flag_repair, construction_height = flag_construction_height, last_service_height = flag_last_service_height, service_price = flag_service_price, name = flag_name, ignore = flag_ignore, active = flag_active, crop = res.crop, sow_height = res.sow_height, num_missed_ticks = res.num_missed_ticks, budget = flag_budget, tiles = flag_tiles))
# research
res = daemon.cc_get_discoveries()
assert res.discoveries[DISCOVERY_BUCKET_BRIGADE].name == "Bucket brigade"
difficulty = res.discoveries[DISCOVERY_BUCKET_BRIGADE].difficulty
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_research(DISCOVERY_BUCKET_BRIGADE, difficulty * 5))
# new item
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_new_item(8, "Test item for testing revert", False, False, 0, "primary desc", "secondary desc"))
# dividend (monetary and item)
res = daemon.cc_get_custom_items()
assert len(res.items_) > 0
custom_item_id = res.items_[-2].id # -2 for the restricted items hack
assert res.items_[-2].amount == 8 # -2 for the restricted items hack
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_dividend(True, custom_item_id, 0, 100, False))
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_dividend(True, custom_item_id, ITEM_STONE, 16, False))
# new event badge
res = self.wallet[0].cc_get_info()
account_id = res.account_id
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_event_badge(0, "Test badge for testing revert", "Event badge desc", "", [{"account_id": account_id, "from_level": 0, "to_level": 2}]))
# assign event badge
from_timestamp = 0
res = daemon.cc_get_account(account_id)
for e in res.badges if 'badges' in res else []:
if e.type == 4096:
assert e.level == 2
from_timestamp = e.timestamp
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_event_badge(4096, "", "", "", [{"account_id": account_id, "from_level": 2, "to_level": 3, "from_timestamp": from_timestamp}]))
# destroy items
res = daemon.cc_get_custom_items()
item_id = res.items_[-2].id # -2 for the restricted items hack
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_destroy_items(item_id, 2))
# define attribute
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_define_attribute('Test attribute', 'foo'))
# increase attribute
self.test_revert_cmd(daemon, lambda self: self.wallet[0].cc_increase_attribute(1, 1))
# dice roll
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_dice_roll("x", 10, 20))
# hunt
res = daemon.cc_get_city(0)
moose = res.moose
self.test_revert_cmd(daemon, lambda self: self.wallet[2].cc_hunt(target = 0, city = 0), redo = False)
bears = res.bears
self.test_revert_cmd(daemon, lambda self: self.wallet[2].cc_hunt(target = 1, city = 0), redo = False)
# destroy_flag
res = self.wallet[3].cc_buy_land(FIRST_CITY_X + 500, FIRST_CITY_Y, FIRST_CITY_X + 500 + 8 - 1, FIRST_CITY_Y + 8 - 1)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = self.wallet[3].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
flag_id = res.flags[-1]
res = daemon.cc_get_flag(flag_id)
palette = res.palette if 'palette' in res else []
crop = res.crop
sow_height = res.sow_height
vegetables_nutrients = res.vegetables_nutrients
grain_nutrients = res.grain_nutrients
construction_height = res.construction_height
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_destroy_flag(flag_id, 0, FIRST_CITY_X + 500, FIRST_CITY_Y, FIRST_CITY_X + 500 + 8 - 1, FIRST_CITY_Y + 8 - 1, construction_height, crop, sow_height, vegetables_nutrients, grain_nutrients, palette))
def test_custom_items(self):
daemon = self.daemon
print("Testing custom items")
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
account_id = [None, None, None]
account_public_key = [None, None, None]
expected_balances = [None, None, None]
expected_item_balances = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
account_public_key[i] = res.cc_pkey
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
res = daemon.cc_get_custom_items()
existing_items = 0 if 'items_' not in res else len(res.items_)
ok = False
try: self.wallet[2].cc_new_item(0, "name", False, False, 0, "primary desc", "secondary desc")
except: ok = True
assert ok
ok = False
try: self.wallet[2].cc_new_item(100, "", False, False, 0, "primary desc", "secondary desc")
except: ok = True
assert ok
ok = False
try: self.wallet[2].cc_new_item(100, "name", False, False, 0, "primary desc", "secondary desc", hash = "1212121212121212")
except: ok = True
assert ok
ok = False
try: self.wallet[2].cc_new_item(100, "name", False, False, 0, "primary desc", "secondary desc", gold = 9999999999999)
except: ok = True
assert ok
ok = False
try: self.wallet[2].cc_new_item(184467440738, "name", False, False, 0, "primary desc", "secondary desc", gold = 100000000)
except: ok = True
assert ok
ok = False
try: self.wallet[2].cc_new_item(100, "name", True, False, 0, "primary desc", "secondary desc", gold = 100000000)
except: ok = True
assert ok
res = daemon.cc_get_custom_items()
assert (0 if 'items_' not in res else len(res.items_)) == existing_items
res = self.wallet[2].cc_new_item(100, "Test item 1", False, False, 0, "primary desc", "secondary desc")
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.get_height()
height = res.height
res = daemon.cc_get_custom_items()
assert len(res.items_) == existing_items + 1
item = res.items_[-1]
assert item.id == ITEM_FIRST_USER + existing_items
assert item.creator == account_id[2]
assert item.creation_height == height - 1
assert item.amount == 100
assert item.name == "Test item 1"
assert item.pdesc == "primary desc"
assert item.sdesc == "secondary desc"
assert not item.is_group
assert item.group == 0
assert item.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
assert item.hash == ""
assert item.gold == 0
expected_balances[2] -= NEW_ITEM_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items, 100)
# no fractional gold below 1000
self.assert_exception(lambda: self.wallet[2].cc_new_item(30, "Test item 2", False, False, 0, "primary desc", "secondary desc", hash = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd", gold = 1500))
res = self.wallet[2].cc_new_item(30, "Test item 2", False, False, 0, "primary desc", "secondary desc", hash = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd", gold = 100000000)
expected_balances[2] -= 30 * (100000000 * (1000 + CUSTOM_ITEM_GOLD_GILDING_FEE_PER_THOUSAND) // 1000)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
assert len(res.items_) == existing_items + 2
item = res.items_[-1]
assert item.id == ITEM_FIRST_USER + existing_items + 1
assert item.creator == account_id[2]
assert item.creation_height == height - 1 + 1
assert item.amount == 30
assert item.name == "Test item 2"
assert item.pdesc == "primary desc"
assert item.sdesc == "secondary desc"
assert not item.is_group
assert item.group == 0
assert item.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
assert item.hash == "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"
assert item.gold == 100000000
expected_balances[2] -= NEW_ITEM_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 1, 30)
res = daemon.cc_is_custom_item_data("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd")
assert res.id == ITEM_FIRST_USER + existing_items + 1
assert not res.ignore
res = daemon.cc_is_custom_item_data("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabce")
assert res.id == 0
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
res = daemon.cc_get_item_count([ITEM_FIRST_USER + existing_items, ITEM_FIRST_USER + existing_items + 1])
assert res.counts == [100, 30]
print('Testing dividends')
# first give some to others
res = self.wallet[2].cc_give(account_public_key[0], [{'type': ITEM_FIRST_USER + existing_items + 1, 'amount': 3}])
expected_balances[2] -= res.fee
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 1, -3)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_FIRST_USER + existing_items + 1, 3)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_give(account_public_key[1], [{'type': ITEM_FIRST_USER + existing_items + 1, 'amount': 2}])
expected_balances[2] -= res.fee
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 1, -2)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_FIRST_USER + existing_items + 1, 2)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# can't be the same item
self.assert_exception(lambda: self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, ITEM_FIRST_USER + existing_items + 1, 1, False))
# invalid dividend item
self.assert_exception(lambda: self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, ITEM_FIRST_USER + 1919, 100, False))
# group custom item
self.assert_exception(lambda: self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, ITEM_FIRST_USER, 100, False))
# no items
self.assert_exception(lambda: self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, ITEM_FIRST_USER + existing_items + 1, 0, False))
# too many items
self.assert_exception(lambda: self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, ITEM_FIRST_USER + existing_items + 1, 111111111111, False))
# too much money
self.assert_exception(lambda: self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, 0, 1111111111111111))
# money dividend
res = self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, 0, 100 * 30, False)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[2] -= 100 * 5
expected_balances[0] += 100 * 3
expected_balances[1] += 100 * 2
# item dividend
res = self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, ITEM_LABOUR, 5 * 30 + 29, False)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_LABOUR, -5 * 5)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_LABOUR, 5 * 3)
expected_item_balances[1] = self.add_item(expected_item_balances[1], ITEM_LABOUR, 5 * 2)
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
# test dividend split/exclude
# supply: 30, ownership: wallet0: 3, wallet1: 2, wallet2: 25
# split 30 among unowned, we spend 30, others gain 18 and 12
res = self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, 0, 30, True)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[2] -= 30
expected_balances[0] += 18
expected_balances[1] += 12
# split 30 among all, we spend 5, others gain 3 and 2
res = self.wallet[2].cc_dividend(True, ITEM_FIRST_USER + existing_items + 1, 0, 30, False)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[2] -= 5
expected_balances[0] += 3
expected_balances[1] += 2
# pay 30 to unowned, we spend 150, others gain 90 and 60
res = self.wallet[2].cc_dividend(False, ITEM_FIRST_USER + existing_items + 1, 0, 30, True)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[2] -= 150
expected_balances[0] += 90
expected_balances[1] += 60
# pay 30 to all, we spend 150, others gain 90 and 60
res = self.wallet[2].cc_dividend(False, ITEM_FIRST_USER + existing_items + 1, 0, 30, False)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
expected_balances[2] -= 150
expected_balances[0] += 90
expected_balances[1] += 60
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
print('Testing item destruction')
res = daemon.cc_get_item_ownership(ITEM_FIRST_USER + existing_items + 1)
user_item_supply = res.supply
assert len(res.ownership) == 3
assert res.ownership[0].account == account_id[0]
assert res.ownership[0].amount == 3
assert res.ownership[1].account == account_id[1]
assert res.ownership[1].amount == 2
assert res.ownership[2].account == account_id[2]
assert res.ownership[2].amount == 25
assert res.supply == 30
res = daemon.cc_get_item_ownership(ITEM_WOOD)
wood_supply = res.supply
assert len(res.ownership) >= 1
for i in range(len(res.ownership)):
if res.ownership[i].account == account_id[2]:
wood_owned = res.ownership[i].amount
assert wood_owned > 0
assert wood_owned <= wood_supply
# testing destruction
res = self.wallet[0].cc_destroy_items(ITEM_FIRST_USER + existing_items + 1, 1)
expected_balances[0] -= res.fee
expected_balances[0] += 1 * 100000000 * (1000 - CUSTOM_ITEM_GOLD_RECOVERY_FEE_PER_THOUSAND) // 1000
res = self.wallet[2].cc_destroy_items(ITEM_FIRST_USER + existing_items + 1, 3)
expected_balances[2] -= res.fee
expected_balances[2] += 3 * 100000000 * (1000 - CUSTOM_ITEM_GOLD_RECOVERY_FEE_PER_THOUSAND) // 1000
res = self.wallet[2].cc_destroy_items(ITEM_WOOD, 2)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 3)
expected_item_balances[0] = self.add_item(expected_item_balances[0], ITEM_FIRST_USER + existing_items + 1, -1)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 1, -3)
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_WOOD, -2)
item_counts[ITEM_WOOD] -= 2
# check total amount
res = daemon.cc_get_custom_items()
items = [x for x in res.items_ if x['id'] == ITEM_FIRST_USER + existing_items + 1]
assert len(items) == 1
assert items[0].amount == 30 - 3 - 1
res = daemon.cc_get_item_ownership(ITEM_FIRST_USER + existing_items + 1)
user_item_supply = res.supply
assert len(res.ownership) == 3
assert res.ownership[0].account == account_id[0]
assert res.ownership[0].amount == 2
assert res.ownership[1].account == account_id[1]
assert res.ownership[1].amount == 2
assert res.ownership[2].account == account_id[2]
assert res.ownership[2].amount == 22
assert res.supply == 26
res = daemon.cc_get_item_ownership(ITEM_WOOD)
assert len(res.ownership) >= 1
for i in range(len(res.ownership)):
if res.ownership[i].account == account_id[2]:
assert res.ownership[i].amount == wood_owned - 2
assert res.supply == wood_supply - 2
# these can't
ok = False
try: self.wallet[2].cc_destroy_items(ITEM_FIRST_USER + existing_items + 100, 1)
except: ok = True
assert ok
ok = False
try: self.wallet[2].cc_destroy_items(ITEM_FIRST_USER + existing_items + 1, 3000000)
except: ok = True
assert ok
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
res = daemon.cc_get_item_count([ITEM_FIRST_USER + existing_items, ITEM_FIRST_USER + existing_items + 1])
assert res.counts == [100, 30 - 4]
print('Testing item groups')
res = daemon.cc_get_custom_items()
for e in res.items_:
assert not e.is_group or e.id == COINS_ITEM_GROUP or e.id == MORTGAGE_ITEM_GROUP or e.id == RESTRICTED_ITEM_GROUP
# we have a settlement and a year coin at start
if e.id <= COIN_ITEM_SETTLEMENT + 1:
assert e.group == 0 or e.id == COIN_ITEM_SETTLEMENT or e.id == COIN_ITEM_SETTLEMENT + 1
# group with non zero amount
self.assert_exception(lambda: self.wallet[2].cc_new_item(30, "Group", True, False, 0, "primary desc", "secondary desc"))
# group with group to a non custom item
self.assert_exception(lambda: self.wallet[2].cc_new_item(0, "Group", True, False, ITEM_WOOD, "primary desc", "secondary desc"))
res = self.wallet[2].cc_new_item(0, "Group", True, False, 0, "primary desc", "secondary desc")
expected_balances[2] -= res.fee
expected_balances[2] -= NEW_ITEM_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = res.items_[-1]
assert e.id >= ITEM_FIRST_USER and e.id <= ITEM_LAST_USER
assert e.is_group
assert not e.is_public
assert e.group == 0
assert e.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
group_id = e.id
res = self.wallet[2].cc_new_item(1, "Group member 1", False, False, group_id, "primary desc", "secondary desc")
expected_balances[2] -= res.fee
expected_balances[2] -= NEW_ITEM_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 3, 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = res.items_[-1]
assert e.id >= ITEM_FIRST_USER and e.id <= ITEM_LAST_USER
assert not e.is_group
assert not e.is_public
assert e.group == group_id
group_member_1_id = e.id
assert e.id == ITEM_FIRST_USER + existing_items + 3
res = self.wallet[2].cc_new_item(1, "Group member 2", False, False, group_id, "primary desc", "secondary desc")
expected_balances[2] -= res.fee
expected_balances[2] -= NEW_ITEM_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 4, 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = res.items_[-1]
assert e.id >= ITEM_FIRST_USER and e.id <= ITEM_LAST_USER
assert not e.is_group
assert not e.is_public
assert e.group == group_id
assert e.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
group_member_2_id = e.id
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
res = daemon.cc_get_item_count(list(range(ITEM_FIRST_USER + existing_items, ITEM_FIRST_USER + existing_items + 4 + 1)))
assert res.counts == [100, 30 - 4, 2, 1, 1]
res = self.wallet[2].cc_new_item(0, "Subgroup", True, False, group_id, "primary desc", "secondary desc")
expected_balances[2] -= res.fee
expected_balances[2] -= NEW_ITEM_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = res.items_[-1]
assert e.id >= ITEM_FIRST_USER and e.id <= ITEM_LAST_USER
assert e.is_group
assert not e.is_public
assert e.group == group_id
assert e.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
subgroup_id = e.id
res = self.wallet[2].cc_new_item(4, "Subgroup member 1", False, False, subgroup_id, "primary desc", "secondary desc")
expected_balances[2] -= res.fee
expected_balances[2] -= NEW_ITEM_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 6, 4)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = res.items_[-1]
assert e.id >= ITEM_FIRST_USER and e.id <= ITEM_LAST_USER
assert not e.is_group
assert not e.is_public
assert e.group == subgroup_id
assert e.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
subgroup_member_1_id = e.id
res = self.wallet[2].cc_new_item(8, "Subgroup member 2", False, False, subgroup_id, "primary desc", "secondary desc")
expected_balances[2] -= res.fee
expected_balances[2] -= NEW_ITEM_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 7, 8)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = res.items_[-1]
assert e.id >= ITEM_FIRST_USER and e.id <= ITEM_LAST_USER
assert not e.is_group
assert not e.is_public
assert e.group == subgroup_id
assert e.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
subgroup_member_2_id = e.id
# wrong owner
self.assert_exception(lambda: self.wallet[1].cc_new_item(1, "Invalid", False, False, group_id, "primary desc", "secondary desc"))
# groups is not a group
self.assert_exception(lambda: self.wallet[2].cc_new_item(1, "Invalid", False, False, group_member_1_id, "primary desc", "secondary desc"))
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
res = daemon.cc_get_item_count(list(range(ITEM_FIRST_USER + existing_items, ITEM_FIRST_USER + existing_items + 7 + 1)))
assert res.counts == [100, 30 - 4, 14, 1, 1, 12, 4, 8]
# others can add to public groups
res = self.wallet[2].cc_new_item(0, "Public group", True, True, group_id, "primary desc", "secondary desc")
expected_balances[2] -= res.fee
expected_balances[2] -= NEW_ITEM_FEE
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = res.items_[-1]
assert e.id >= ITEM_FIRST_USER and e.id <= ITEM_LAST_USER
assert e.is_group
assert e.is_public
assert e.group == group_id
assert e.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
public_group_id = e.id
res = self.wallet[1].cc_new_item(4, "In a public group", False, False, public_group_id, "primary desc", "secondary desc")
expected_balances[1] -= res.fee
expected_balances[1] -= NEW_ITEM_FEE
expected_item_balances[1] = self.add_item(expected_item_balances[1], public_group_id + 1, 4)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = res.items_[-1]
assert e.id >= ITEM_FIRST_USER and e.id <= ITEM_LAST_USER
assert not e.is_group
assert not e.is_public
assert e.user_data == [0] * NUM_CUSTOM_ITEM_USER_DATA
assert e.group == public_group_id
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
res = daemon.cc_get_item_count(list(range(ITEM_FIRST_USER + existing_items, ITEM_FIRST_USER + existing_items + 7 + 3)))
assert res.counts == [100, 30 - 4, 18, 1, 1, 12, 4, 8, 4, 4]
print('Testing updating item')
# invalid
self.assert_exception(lambda: self.wallet[2].cc_update_item(0, "", ""))
# non custom
self.assert_exception(lambda: self.wallet[2].cc_update_item(ITEM_WOOD, "", ""))
# not owned
self.assert_exception(lambda: self.wallet[1].cc_update_item(ITEM_FIRST_USER + existing_items + 1, "", ""))
# wrong previous desc
self.assert_exception(lambda: self.wallet[2].cc_update_item(ITEM_FIRST_USER + existing_items + 1, "secondary desc - not", ""))
# wrong previous desc
self.assert_exception(lambda: self.wallet[2].cc_update_item(ITEM_FIRST_USER + existing_items + 1, "primary desc", ""))
# that one should work
self.wallet[2].cc_update_item(ITEM_FIRST_USER + existing_items + 1, "secondary desc", "new secondary desc")
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
e = [x for x in res.items_ if x.id == ITEM_FIRST_USER + existing_items + 1][0]
assert e.id == ITEM_FIRST_USER + existing_items + 1
assert e.sdesc == "new secondary desc"
def test_badges(self):
daemon = self.daemon
print("Testing badges")
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
item_counts = res.counts
account_id = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
self.wallet[3].cc_event_badge(0, "Some test badge", "Event badge desc", "", [{"account_id": account_id[0], "from_level": 0, "to_level": 2}, {"account_id": account_id[2], "from_level": 0, "to_level": 1}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.get_height()
height = res.height
res = daemon.cc_get_badge([])
badge = res.badges[-1]
assert badge.id > 0
assert badge.name == "Some test badge"
assert badge.description == "Event badge desc"
badge_id = badge.id
res = daemon.cc_get_account(account_id[0])
assert res.badges[-1].type == badge_id
assert res.badges[-1].level == 2
assert res.badges[-1].timestamp == height - 1
res = daemon.cc_get_account(account_id[2])
assert res.badges[-1].type == badge_id
assert res.badges[-1].level == 1
assert res.badges[-1].timestamp == height - 1
from_timestamp = [0, 0, 0]
for i in range(3):
res = daemon.cc_get_account(account_id[i])
for e in res.badges if 'badges' in res else []:
if e.type == badge_id:
from_timestamp[i] = e.timestamp
self.wallet[3].cc_event_badge(badge_id, "", "", "", [{"account_id": account_id[0], "from_level": 2, "to_level": 4, "from_timestamp": from_timestamp[0]}, {"account_id": account_id[1], "from_level": 0, "to_level": 2, "from_timestamp": from_timestamp[1]}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id[0])
assert res.badges[-1].type == badge_id
assert res.badges[-1].level == 4
assert res.badges[-1].timestamp == height
res = daemon.cc_get_account(account_id[2])
assert res.badges[-1].type == badge_id
assert res.badges[-1].level == 1
assert res.badges[-1].timestamp == height - 1
res = daemon.cc_get_account(account_id[1])
assert res.badges[-1].type == badge_id
assert res.badges[-1].level == 2
assert res.badges[-1].timestamp == height
# invalid: does not change
ok = False
try: self.wallet[3].cc_event_badge(badge_id, "", "", "", [{"account_id": account_id[0], "from_level": 4, "to_level": 4, "from_timestamp": from_timestamp[0]}])
except: ok = True
assert ok
# invalid: wrong from
ok = False
try: self.wallet[3].cc_event_badge(badge_id, "", "", "", [{"account_id": account_id[0], "from_level": 3, "to_level": 5, "from_timestamp": from_timestamp[0]}])
except: ok = True
assert ok
# invalid: goes down
ok = False
try: self.wallet[3].cc_event_badge(badge_id, "", "", "", [{"account_id": account_id[0], "from_level": 4, "to_level": 3, "from_timestamp": from_timestamp[0]}])
except: ok = True
assert ok
# invalid: goes too high
ok = False
try: self.wallet[3].cc_event_badge(badge_id, "", "", "", [{"account_id": account_id[0], "from_level": 4, "to_level": 6, "from_timestamp": from_timestamp[0]}])
except: ok = True
assert ok
# invalid: both id and name
ok = False
try: self.wallet[3].cc_event_badge(badge_id, "not empty", "", "", [{"account_id": account_id[0], "from_level": 4, "to_level": 5, "from_timestamp": from_timestamp[0]}])
except: ok = True
assert ok
# invalid: not an event badge
ok = False
try: self.wallet[3].cc_event_badge(1, "", "", "", [{"account_id": account_id[0], "from_level": 4, "to_level": 5, "from_timestamp": from_timestamp[0]}])
except: ok = True
assert ok
# invalid: unallocated event badge
ok = False
try: self.wallet[3].cc_event_badge(4096 + 1024, "", "", "", [{"account_id": account_id[0], "from_level": 4, "to_level": 5, "from_timestamp": from_timestamp[0]}])
except: ok = True
assert ok
# valid: new event badge, no recipients
self.wallet[3].cc_event_badge(0, "Badge with no recipients", "desc", "", [])
# invalid: bad from timestamp
ok = False
try: self.wallet[3].cc_event_badge(badge_id, "", "", "", [{"account_id": account_id[0], "from_level": 4, "to_level": 5, "from_timestamp": 999999}])
except: ok = True
assert ok
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
assert res.counts == item_counts
def test_resize_flag(self):
daemon = self.daemon
print("Testing resizing flags")
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X + 200, FIRST_CITY_Y + 500, FIRST_CITY_X + 200 + 12 - 1, FIRST_CITY_Y + 500 + 12 - 1)
res = self.wallet[2].cc_buy_land(FIRST_CITY_X + 200, FIRST_CITY_Y + 500, FIRST_CITY_X + 200 + 12 - 1, FIRST_CITY_Y + 500 + 12 - 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
assert 'flags' in res and len(res.flags) > 0
flag_id = res.flags[-1]
assert flag_id > 0
res = daemon.cc_get_flag(flag_id)
x0 = res.x0
y0 = res.y0
x1 = res.x1
y1 = res.y1
w = x1 - x0 + 1
h = y1 - y0 + 1
tmpx0 = x0
tmpy0 = y0
tmpx1 = x1
tmpy1 = y1
# increase to the right
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0, tmpy0, tmpx1 + 1, tmpy1)
self.wallet[2].cc_resize_flag(flag_id, 0, 0, 1, 0, res.cost)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id)
assert res.x0 == x0 and res.y0 == y0 and res.x1 == x1 + 1 and res.y1 == y1
tmpx1 += 1
# increase to the left
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0 - 1, tmpy0, tmpx1, tmpy1)
self.wallet[2].cc_resize_flag(flag_id, -1, 0, 0, 0, res.cost)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id)
assert res.x0 == x0 - 1 and res.y0 == y0 and res.x1 == x1 + 1 and res.y1 == y1
tmpx0 -= 1
# increase to the top
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0, tmpy0 - 1, tmpx1, tmpy1)
self.wallet[2].cc_resize_flag(flag_id, 0, -1, 0, 0, res.cost)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id)
assert res.x0 == x0 - 1 and res.y0 == y0 - 1 and res.x1 == x1 + 1 and res.y1 == y1
tmpy0 -= 1
# increase to the bottom
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0, tmpy0, tmpx1, tmpy1 + 1)
self.wallet[2].cc_resize_flag(flag_id, 0, 0, 0, 1, res.cost)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id)
assert res.x0 == x0 - 1 and res.y0 == y0 - 1 and res.x1 == x1 + 1 and res.y1 == y1 + 1
tmpy1 += 1
# increase all
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0 - 2, tmpy0 - 1, tmpx1 + 3, tmpy1 + 2)
self.wallet[2].cc_resize_flag(flag_id, -2, -1, 3, 2, res.cost)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id)
assert res.x0 == x0 - 3 and res.y0 == y0 - 2 and res.x1 == x1 + 4 and res.y1 == y1 + 3
tmpx0 -= 2
tmpy0 -= 1
tmpx1 += 3
tmpy1 += 2
# increase back to start
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0 + 3, tmpy0 + 2, tmpx1 - 4, tmpy1 - 3)
self.wallet[2].cc_resize_flag(flag_id, 3, 2, -4, -3, res.cost)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id)
assert res.x0 == x0 and res.y0 == y0 and res.x1 == x1 and res.y1 == y1
tmpx0 += 3
tmpy0 += 2
tmpx1 -= 4
tmpy1 -= 3
assert tmpx0 == x0
assert tmpy0 == y0
assert tmpx1 == x1
assert tmpy1 == y1
# invalid: increase width past 256
ok = False
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0 - 128, tmpy0, tmpx1 + 127, tmpy1)
try: self.wallet[2].cc_resize_flag(flag_id, -128, 0, 127, 0, res.cost)
except: ok = True
assert ok
# invalid: increase height past 256
ok = False
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0, tmpy0 - 128, tmpx1, tmpy1 + 127)
try: self.wallet[2].cc_resize_flag(flag_id, 0, -128, 0, 127, res.cost)
except: ok = True
assert ok
# invalid: move with no intersection
ok = False
res = daemon.cc_get_flag_resizing_cost(0, tmpx0, tmpy0, tmpx1, tmpy1, tmpx0 + 127, tmpy0 + 127, tmpx1 + 127, tmpy1 + 127)
try: self.wallet[2].cc_resize_flag(flag_id, 127, 127, 127, 127, res.cost)
except: ok = True
assert ok
def test_names(self):
daemon = self.daemon
print("Testing valid names")
for name in ["foo", "a" * MAX_CC_NAME_LENGTH, "a b", "a1", "a'."]:
res = self.wallet[2].cc_deposit(amount = 100000000, name = name)
self.deposits += 100000000
txid = res.tx_hash_list[0]
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
assert res.name == name, res
daemon.pop_blocks(1)
daemon.flush_txpool(txid)
self.wallet[2].rescan_blockchain()
self.deposits -= 100000000
print("Testing invalid names")
for name in ["", "a\0", " ", "a ", " a", "1", "a&", "\xff\xff", "a" * (MAX_CC_NAME_LENGTH + 1), u"いちりゅう", "\0f", u"いちりゅういちりゅういちりゅういちりゅういちりゅう"]:
ok = False
try:
res = self.wallet[2].cc_deposit(amount = 100000000, name = name)
except:
ok = True
assert ok
print("Testing conflicting names")
res = self.wallet[2].cc_deposit(amount = 100000000, name = 'Foo')
self.deposits += 100000000
txid = res.tx_hash_list[0]
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].refresh()
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
assert res.name == "Foo", res
for name in ["foo", "foO", "FOO"]:
ok = False
try:
res = self.wallet[3].cc_deposit(amount = 100000000, name = name)
except:
ok = True
assert ok
daemon.pop_blocks(1)
daemon.flush_txpool(txid)
self.wallet[2].rescan_blockchain()
self.deposits -= 100000000
def test_script_variables(self):
daemon = self.daemon
print("Testing script variables")
res = daemon.cc_get_script_variables()
assert 'script_variables' not in res or len(res.script_variables) == 0
res = daemon.cc_get_script_variable("test")
assert res.value == 0
self.assert_exception(lambda: self.wallet[1].cc_set_script_variable('other', 1, 42))
res = self.wallet[3].cc_set_script_variable('other', 0, 42)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_script_variable("test")
assert res.value == 0
res = daemon.cc_get_script_variable("other")
assert res.value == 42
res = daemon.cc_get_script_variables()
assert len(res.script_variables) == 1
assert res.script_variables[0].name == "other"
assert res.script_variables[0].value == 42
res = self.wallet[3].cc_set_script_variable('test', 0, 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_script_variables()
assert len(res.script_variables) == 2
res = daemon.cc_get_script_variable("test")
assert res.value == 1
res = daemon.cc_get_script_variable("other")
assert res.value == 42
res = self.wallet[3].cc_set_script_variable('test', 1, 0)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_script_variables()
assert len(res.script_variables) == 1
res = daemon.cc_get_script_variable("other")
assert res.value == 42
res = daemon.cc_get_script_variable("test")
assert res.value == 0
res = self.wallet[3].cc_set_script_variable('other', 42, 18)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_script_variables()
assert len(res.script_variables) == 1
res = daemon.cc_get_script_variable("other")
assert res.value == 18
res = self.wallet[3].cc_set_script_variable('other', 18, 0)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_script_variables()
assert 'script_variables' not in res or len(res.script_variables) == 0
res = daemon.cc_get_script_variable("other")
assert res.value == 0
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
assert 'script_variables' not in res or len(res.script_variables) == 0
def test_attributes(self):
daemon = self.daemon
print("Testing attributes")
res = daemon.cc_get_attributes()
assert len(res.attributes) == 1 # from revert test
ok = False
try: res = self.wallet[3].cc_define_attribute('', 'desc')
except: ok = True
assert ok
description = 'The quality of that which is evanescent (duh)'
res = self.wallet[3].cc_define_attribute('Evanescence', description)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_attributes()
assert len(res.attributes) == 2
assert res.attributes[1].name == 'Evanescence'
assert res.attributes[1].description == description
ok = False
try: res = self.wallet[3].cc_define_attribute('Evanescence', description)
except: ok = True
assert ok
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
for e in res.attributes if 'attributes' in res else []:
assert e.value == 0
self.wallet[2].cc_increase_attribute(2, 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
ok = False
try: res = self.wallet[3].cc_increase_attribute(3, 1)
except: ok = True
assert ok
ok = False
try: res = self.wallet[3].cc_increase_attribute(2, 5)
except: ok = True
assert ok
ok = False
try: res = self.wallet[3].cc_increase_attribute(2, 0xffffffff)
except: ok = True
assert ok
res = daemon.cc_get_account(account_id)
for e in res.attributes if 'attributes' in res else []:
assert e.value == 1 if e.type == 2 else 0
def test_dice_roll(self):
daemon= self.daemon
print("Testing dice roll")
self.assert_exception(lambda: self.wallet[3].cc_dice_roll('foo!\n', 6, 10, 0))
self.assert_exception(lambda: self.wallet[3].cc_dice_roll('foo!', 11, 10, 0))
self.assert_exception(lambda: self.wallet[3].cc_dice_roll('foo!', 6, 10, 111))
self.assert_exception(lambda: self.wallet[3].cc_dice_roll('foo!', 6, 10, 1, 100))
res = self.wallet[3].cc_dice_roll('foo!', 6, 10, 1, 1)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
def test_hunt(self):
daemon = self.daemon
print("Testing hunt")
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
moose_killed = res.moose_killed
bears_killed = res.bears_killed
starting_meat = 0
for x in res.item_balances:
if x['type'] == ITEM_FOOD_MEAT:
starting_meat = x['amount']
res = daemon.cc_get_city(0)
starting_moose = res.moose
starting_bears = res.bears
# bad city
ok = False
try: self.wallet[2].cc_hunt(target = 0, city = 9)
except: ok = True
assert ok
# bad target
ok = False
try: self.wallet[2].cc_hunt(target = 2, city = 0)
except: ok = True
assert ok
self.wallet[2].cc_hunt(target = 0, city = 0)
# can do several at a time
self.wallet[2].cc_hunt(target = 1, city = 0)
self.wallet[2].cc_hunt(target = 1, city = 0)
self.wallet[2].cc_hunt(target = 1, city = 0)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert res.moose_killed > moose_killed
moose_killed_now = res.moose_killed - moose_killed
moose_killed = res.moose_killed
assert bears_killed < res.bears_killed
meat = 0
for x in res.item_balances:
if x['type'] == ITEM_FOOD_MEAT:
meat = x['amount']
assert meat > starting_meat
starting_meat = meat
res = daemon.cc_get_city(0)
self.wallet[2].cc_hunt(target = 1, city = 0)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert moose_killed == res.moose_killed
assert res.bears_killed > bears_killed
bears_killed_now = res.bears_killed - bears_killed
bears_killed = res.bears_killed
meat = 0
for x in res.item_balances:
if x['type'] == ITEM_FOOD_MEAT:
meat = x['amount']
assert meat > starting_meat
res = daemon.cc_get_city(0)
new_moose = res.moose
assert new_moose == starting_moose - moose_killed_now
new_bears = res.bears
assert new_bears == starting_bears - bears_killed_now
# mine enough to pass one update
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
# check regeneration
res = daemon.cc_get_city(0)
if res.special_event != SPECIAL_EVENT_MOOSE_DISEASE:
assert res.moose > new_moose
assert res.bears > new_bears
res = daemon.cc_get_city(0)
old_moose = res.moose
self.wallet[2].cc_hunt(target = 0, city = 0)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_city(0)
new_moose = res.moose
assert new_moose < old_moose
def test_invitation(self):
daemon = self.daemon
print("Testing invitations")
res = self.wallet[2].cc_create_invitation(amount = 250000000)
invitation = res.invitation
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
ok = False
try: res = self.wallet[4].cc_get_info()
except: ok = True
assert ok
# can't redeem if we already have an account
ok = False
try: self.wallet[0].cc_redeem_account(invitation, "invited account 2")
except: ok = True
assert ok
# bad name
ok = False
try: self.wallet[4].cc_redeem_account(invitation, "")
except: ok = True
assert ok
self.wallet[4].cc_redeem_account(invitation, "invited account")
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
self.wallet[4].refresh()
res = self.wallet[4].cc_get_info()
account_id = res.account_id
assert account_id != 0
res = self.wallet[4].get_address()
recipient = res.address
res = daemon.cc_get_account(account_id)
assert res.balance == 250000000
res = self.wallet[4].get_balance()
assert res.cc_balance == 250000000
# pop, the account disappears
daemon.pop_blocks(1)
ok = False
try: res = daemon.cc_get_account(account_id)
except: ok = True
assert ok
self.wallet[4].refresh()
ok = False
try: res = self.wallet[4].cc_get_info()
except: ok = True
assert ok
# can do it again if popped
daemon.flush_txpool()
self.wallet[4].cc_redeem_account(invitation, "invited account after pop")
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
self.wallet[4].refresh()
res = self.wallet[4].cc_get_info()
account_id = res.account_id
assert account_id != 0
res = daemon.cc_get_account(account_id)
assert res.balance == 250000000
res = self.wallet[4].get_balance()
assert res.cc_balance == 250000000
# can't do it twice
ok = False
try: self.wallet[4].cc_redeem_account(invitation, "invited account 2")
except: ok = True
assert ok
# nor from another account
ok = False
try: self.wallet[3].cc_redeem_account(invitation, "invited account 2")
except: ok = True
assert ok
# pop and try with invitation with a recipient
daemon.pop_blocks(1)
daemon.flush_txpool()
self.assert_exception(lambda: self.wallet[2].cc_create_invitation(amount = 250000000, recipient = 'invalid'))
res = self.wallet[2].cc_create_invitation(amount = 250000000, recipient = recipient)
invitation = res.invitation
assert len(invitation) > 0
# it's not used yet
res = daemon.cc_is_invitation_used(invitation)
assert not res.used
assert res.expiration == 0
assert not res.expired
assert res.balance_ok
# only the intended recipient can redeem
self.assert_exception(lambda: self.wallet[3].cc_redeem_account(invitation, "invited account with recipient"))
res = self.wallet[4].cc_redeem_account(invitation, "invited account with recipient")
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# it's used now
res = daemon.cc_is_invitation_used(invitation)
assert res.used
self.wallet[4].refresh()
res = self.wallet[4].cc_get_info()
account_id = res.account_id
assert account_id != 0
res = daemon.cc_get_account(account_id)
assert res.balance == 250000000
res = daemon.get_info()
height = res.height
res = self.wallet[2].cc_create_invitation(amount = 250000000, expiration = height + 2)
expiring_invitation = res.invitation
# it's not used yet
res = daemon.cc_is_invitation_used(expiring_invitation)
assert not res.used
assert res.expiration == height + 2
assert not res.expired
assert res.balance_ok
# mine two blocks and it's expired
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 2)
res = daemon.cc_is_invitation_used(expiring_invitation)
assert not res.used
assert res.expiration == height + 2
assert res.expired
assert res.balance_ok
res = self.wallet[4].get_balance()
assert res.cc_balance == 250000000
def test_chat(self):
daemon = self.daemon
print("Testing chat")
# empty line
ok = False
try: self.wallet[2].cc_chat("")
except: ok = True
assert ok
# bad color
ok = False
try: self.wallet[2].cc_chat("foo", color = 200)
except: ok = True
assert ok
# locked color
ok = False
try: self.wallet[2].cc_chat("foo", color = 10)
except: ok = True
assert ok
res = self.wallet[2].cc_chat("foo", color = 1)
txid = res.tx_hash
assert len(txid) == 64
res = daemon.get_transaction_pool_hashes()
assert txid in res.tx_hashes
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 5)
# not mined
res = daemon.get_transaction_pool_hashes()
assert txid in res.tx_hashes
res = self.wallet[2].cc_chat("foo2")
txid2 = res.tx_hash
assert len(txid2) == 64
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 5)
# not mined
res = daemon.get_transaction_pool_hashes()
assert txid in res.tx_hashes
assert txid2 in res.tx_hashes
daemon.flush_txpool([txid, txid2])
res = daemon.get_transaction_pool_hashes()
assert 'tx_hashes' not in res or len(tx_hashes) == 0
def test_ignore(self):
daemon = self.daemon
print("Testing ignore")
# wrong type
ok = False
try: self.wallet[3].cc_ignore(type = 5, id = [4], ignore = True)
except: ok = True
assert ok
# wrong account
ok = False
try: self.wallet[3].cc_ignore(type = IGNORE_ACCOUNT, id = [444], ignore = True)
except: ok = True
assert ok
# wrong city
ok = False
try: self.wallet[3].cc_ignore(type = IGNORE_CITY, id = [444], ignore = True)
except: ok = True
assert ok
# wrong flag
ok = False
try: self.wallet[3].cc_ignore(type = IGNORE_FLAG, id = [444], ignore = True)
except: ok = True
assert ok
# wrong item
ok = False
try: self.wallet[3].cc_ignore(type = IGNORE_ITEM, id = [444], ignore = True)
except: ok = True
assert ok
# wrong sender
ok = False
try: self.wallet[2].cc_ignore(type = IGNORE_ACCOUNT, id = [4], ignore = True)
except: ok = True
assert ok
# duplicate id
ok = False
try: self.wallet[3].cc_ignore(type = IGNORE_ACCOUNT, id = [4, 4], ignore = True)
except: ok = True
assert ok
res = self.wallet[3].cc_ignore(type = IGNORE_ACCOUNT, id = [4], ignore = True)
res = daemon.cc_get_account(4)
assert not res.ignore
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(4)
assert res.ignore
res = self.wallet[3].cc_ignore(type = IGNORE_ACCOUNT, id = [4], ignore = False)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(4)
assert not res.ignore
# does not change state
ok = False
try: self.wallet[3].cc_ignore(type = IGNORE_ACCOUNT, id = [4], ignore = False)
except: ok = True
assert ok
# flag
flag_id = 2
res = daemon.cc_get_flag(flag_id)
assert not res.ignore
res = self.wallet[3].cc_ignore(type = IGNORE_FLAG, id = [flag_id], ignore = True)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_flag(flag_id)
assert res.ignore
res = self.wallet[3].cc_ignore(type = IGNORE_FLAG, id = [flag_id], ignore = False)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_flag(flag_id)
assert not res.ignore
# city
res = daemon.cc_get_city(0)
assert not res.ignore
res = self.wallet[3].cc_ignore(type = IGNORE_CITY, id = [0], ignore = True)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_city(0)
assert res.ignore
res = self.wallet[3].cc_ignore(type = IGNORE_CITY, id = [0], ignore = False)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_city(0)
assert not res.ignore
# custom item
res = daemon.cc_get_custom_items()
assert len(res.items_) > 0
item_id = res.items_[0].id
assert not res.items_[0].ignore
res = self.wallet[3].cc_ignore(type = IGNORE_ITEM, id = [item_id], ignore = True)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_custom_items()
assert res.items_[0].id == item_id
assert res.items_[0].ignore
res = self.wallet[3].cc_ignore(type = IGNORE_ITEM, id = [item_id], ignore = False)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_custom_items()
assert res.items_[0].id == item_id
assert not res.items_[0].ignore
def test_minting(self):
daemon = self.daemon
print("Testing minting")
# get past the point where the genesis coin can still be minted
res = daemon.get_height()
height = res.height
if height + 2 <= COIN_MINTING_WINDOW:
blocks = COIN_MINTING_WINDOW - height + 2 # to be sure
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', blocks)
expected_balances = [None, None, None]
expected_item_balances = [None, None, None]
account_id = [None, None, None]
for i in range(3):
self.wallet[i].refresh()
res = self.wallet[i].cc_get_info()
account_id[i] = res.account_id
res = daemon.cc_get_account(account_id[i])
expected_balances[i] = res.balance
expected_item_balances[i] = []
if 'item_balances' in res:
for e in res.item_balances:
expected_item_balances[i] = self.add_item(expected_item_balances[i], e.type, e.amount)
# there should be a preexisting coin, without any instances
res = daemon.cc_get_custom_items([COINS_ITEM_GROUP, COIN_ITEM_SETTLEMENT])
assert len(res.items_) == 2
assert res.items_[0].id == COINS_ITEM_GROUP
assert res.items_[0].group == 0
assert res.items_[0].is_group
assert res.items_[0].amount == 0
assert res.items_[0].creator == GAME_ACCOUNT
assert res.items_[0].creation_height == 0
assert res.items_[1].id == COIN_ITEM_SETTLEMENT
assert res.items_[1].group == COINS_ITEM_GROUP
assert not res.items_[1].is_group
assert res.items_[1].amount == 0
assert res.items_[1].creator == GAME_ACCOUNT
assert res.items_[1].creation_height == 0
res = daemon.cc_get_custom_items()
n_existing_custom_items = len(res.items_)
# but it's too late to mint any
self.assert_exception(lambda: self.wallet[2].cc_mint(COIN_ITEM_SETTLEMENT, 1))
# or smelt any either
self.assert_exception(lambda: self.wallet[2].cc_smelt(COIN_ITEM_SETTLEMENT, 1))
# can't mint the group either
self.assert_exception(lambda: self.wallet[2].cc_mint(COINS_ITEM_GROUP, 1))
# create a fresh one, and a new coin item
res = self.wallet[3].cc_new_item(0, "Test coin", False, False, COINS_ITEM_GROUP, "primary desc", "secondary desc")
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 2)
res = self.wallet[3].cc_new_item(1, "Test non-coin", False, False, 0, "primary desc", "secondary desc")
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 2)
res = daemon.cc_get_custom_items()
assert n_existing_custom_items + 2 == len(res.items_)
test_coin = res.items_[-2 - 1] # restr
assert test_coin.group == COINS_ITEM_GROUP
test_non_coin = res.items_[-1 - 1] # restr
assert test_non_coin.group == 0
# we can mint the coin, but not the coin group
res = self.wallet[2].cc_mint(test_coin.id, 3)
gold_content = get_gold_content(COIN_TYPE(test_coin.user_data))
assert gold_content > 0
expected_balances[2] -= res.fee + COIN_MINTING_FEE * 3 + gold_content * 100000000 * 3
expected_item_balances[2] = self.add_item(expected_item_balances[2], test_coin.id, 3)
self.assert_exception(lambda: self.wallet[2].cc_mint(test_non_coin.id, 1))
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# can't destroy the coin, it can only be smelted
ok = False
try: self.wallet[2].cc_destroy_items(test_coin.id, 1)
except: ok = True
assert ok
# smelt two
res = self.wallet[2].cc_smelt(test_coin.id, 2)
expected_balances[2] -= res.fee
expected_balances[2] += COIN_GOLD_CONTENT * 2 - COIN_SMELTING_FEE * 2
expected_item_balances[2] = self.add_item(expected_item_balances[2], test_coin.id, -2)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# can't smelt 2 more though, we don't have that many
self.assert_exception(lambda: self.wallet[2].cc_smelt(test_coin.id, 2))
# can't smelt the group either
self.assert_exception(lambda: self.wallet[2].cc_smelt(test_non_coin.id, 1))
# wallet 1 can't smelt anything
self.assert_exception(lambda: self.wallet[1].cc_smelt(test_coin.id, 1))
# check balances
for i in range(3):
res = daemon.cc_get_account(account_id[i])
assert res.balance == expected_balances[i]
assert (res.item_balances if 'item_balances' in res else []) == expected_item_balances[i]
# wait a week
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', COIN_MINTING_WINDOW)
# can't mint anymore
self.assert_exception(lambda: self.wallet[2].cc_mint(test_coin.id, 1))
def test_farming(self):
daemon = self.daemon
print("Testing farming")
# get some more money first
self.wallet[2].refresh()
res = self.wallet[2].cc_deposit(amount = 100000000000)
self.deposits += 100000000000
assert len(res.fee_list) == 1
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_get_info()
account_id = res.account_id
# build a new farm
res = daemon.cc_get_new_flag_cost(0, FIRST_CITY_X - 350, FIRST_CITY_Y - 350, FIRST_CITY_X - 350 + 64 - 1, FIRST_CITY_Y - 350 + 64 - 1)
res = self.wallet[2].cc_buy_land(FIRST_CITY_X - 350, FIRST_CITY_Y - 350, FIRST_CITY_X - 350 + 64 - 1, FIRST_CITY_Y - 350 + 64 - 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert 'flags' in res and len(res.flags) > 0
flag_id = res.flags[-1]
assert flag_id
self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 8000}, {'type': ITEM_WOOD, 'amount': 8000}, {'type': ITEM_LABOUR, 'amount': 1}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id)
construction_height = res.construction_height
res = self.wallet[2].cc_building_settings(flag_id, ROLE_AGRICULTURAL, 100, construction_height, name = 'test building')
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_calendar()
if res.vegetables_sowing_season:
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', res.blocks_till_vegetables_sowing_season_end)
res = daemon.cc_get_calendar()
assert not res.vegetables_sowing_season
if res.grain_sowing_season:
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', res.blocks_till_grain_sowing_season_end)
res = daemon.cc_get_calendar()
assert not res.vegetables_sowing_season and not res.grain_sowing_season
res = daemon.cc_get_flag(flag_id)
num_missed_ticks = res.num_missed_ticks
if res.repair < 500000:
# drive by check for repair range
self.assert_exception(lambda: self.wallet[1].cc_repair([flag_id, 0]))
self.assert_exception(lambda: self.wallet[1].cc_repair([flag_id, 1000000 + 1 - res.repair]))
self.wallet[2].cc_repair([flag_id, 25000])
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_NONE))
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES))
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_GRAIN))
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, 255))
self.assert_exception(lambda: self.wallet[2].cc_harvest(flag_id, CROP_NONE, 0, 0, num_missed_ticks))
# get into sowing season
res = daemon.cc_get_calendar()
if not res.vegetables_sowing_season:
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', res.blocks_till_vegetables_sowing_season_start)
res = daemon.cc_get_calendar()
assert res.vegetables_sowing_season
res = daemon.cc_get_flag(flag_id)
if res.repair < 500000:
self.wallet[2].cc_repair([flag_id, 25000])
# now we can sow
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_NONE))
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, 255))
res = self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# but not twice
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES))
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_GRAIN))
# we cannot harvest yet
res = daemon.cc_get_flag(flag_id)
sow_height = res.sow_height
vegetables_nutrients = res.vegetables_nutrients
num_missed_ticks = res.num_missed_ticks
self.assert_exception(lambda: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height, vegetables_nutrients, num_missed_ticks))
# leave sowing season
res = daemon.cc_get_calendar()
if res.vegetables_sowing_season:
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', res.blocks_till_vegetables_sowing_season_end)
res = daemon.cc_get_calendar()
assert not res.vegetables_sowing_season
# sowing ends after harvest starts for vegetables
assert res.vegetables_harvest_season
res = daemon.cc_get_flag(flag_id)
num_missed_ticks = res.num_missed_ticks
if res.repair < 500000:
self.wallet[2].cc_repair([{'flag': flag_id, 'delta_repair': 25000}])
# still cannot sow
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES))
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_GRAIN))
# but we can harvest now
self.assert_exception(lambda: self.wallet[2].cc_harvest(flag_id - 1, CROP_VEGETABLES, sow_height, vegetables_nutrients, num_missed_ticks))
self.assert_exception(lambda: self.wallet[2].cc_harvest(flag_id, CROP_GRAIN, sow_height, vegetables_nutrients, num_missed_ticks))
self.assert_exception(lambda: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height - 1, vegetables_nutrients, num_missed_ticks))
self.assert_exception(lambda: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height + 1, vegetables_nutrients, num_missed_ticks))
self.assert_exception(lambda: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height, vegetables_nutrients - 1, num_missed_ticks))
res = self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height, vegetables_nutrients, num_missed_ticks)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# not twice
self.assert_exception(lambda: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height, vegetables_nutrients, num_missed_ticks))
# and we can't sow right afterwards either (we could sow grain as seasons overlap though)
self.assert_exception(lambda: self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES))
# nutrients should have gone down for vegetables, stayed at 100 for grain
res = daemon.cc_get_flag(flag_id)
assert res.vegetables_nutrients < 100
assert res.grain_nutrients == 100
vegetables_nutrients = res.vegetables_nutrients
# get into sowing season
res = daemon.cc_get_calendar()
if not res.vegetables_sowing_season:
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', res.blocks_till_vegetables_sowing_season_start)
res = daemon.cc_get_calendar()
assert res.vegetables_sowing_season
res = daemon.cc_get_flag(flag_id)
if res.repair < 500000:
self.wallet[2].cc_repair([{'flag': flag_id, 'delta_repair': 25000}])
# vegetables nutrients should have recovered somewhat, but not fully, grain nutrients should not have gone past 100
res = daemon.cc_get_flag(flag_id)
assert res.vegetables_nutrients < 100
assert res.vegetables_nutrients > vegetables_nutrients
assert res.grain_nutrients == 100
# sow
self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# drop all wood so we miss ticks
res = daemon.cc_get_account(account_id)
for e in res.item_balances:
if e.type == ITEM_WOOD or e.type == ITEM_FIREWOOD:
res = self.wallet[2].cc_destroy_items(e.type, e.amount)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# wait past harvest time so the crop gets lost
while True:
res = daemon.cc_get_calendar()
if not res.vegetables_sowing_season and not res.vegetables_harvest_season:
break
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY)
# delete the flag, to check state gets restored on pop
res = daemon.cc_get_flag(flag_id, get_packed_tiles = True)
flag_role = res.role
flag_economic_power = res.economic_power
flag_repair = res.repair
flag_construction_height = res.construction_height
flag_last_service_height = res.last_service_height
flag_service_price = res.service_price if 'service_price' in res else 0
flag_name = res.name
flag_ignore = res.ignore
flag_active = res.active
flag_budget = res.budget
flag_tiles = res.packed_tiles if 'packed_tiles' in res else []
flag_crop = res.crop
flag_sow_height = res.sow_height
flag_num_missed_ticks = res.num_missed_ticks
self.wallet[2].cc_demolish(flag_id, role = flag_role, economic_power = flag_economic_power, repair = flag_repair, construction_height = flag_construction_height, last_service_height = flag_last_service_height, service_price = flag_service_price, name = flag_name, ignore = flag_ignore, active = flag_active, crop = flag_crop, sow_height = flag_sow_height, num_missed_ticks = flag_num_missed_ticks, budget = flag_budget, tiles = flag_tiles)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_flag(flag_id)
x0 = res.x0
y0 = res.y0
x1 = res.x1
y1 = res.y1
palette = res.palette if 'palette' in res else []
construction_height = res.construction_height
crop = res.crop
sow_height = res.sow_height
vegetables_nutrients = res.vegetables_nutrients
grain_nutrients = res.grain_nutrients
self.wallet[2].cc_destroy_flag(flag_id, 0, x0, y0, x1, y1, construction_height, crop, sow_height, vegetables_nutrients, grain_nutrients, palette)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# now go back to where we still have crop, and demolish the building then, all state should be restored on rewind
while True:
res = daemon.cc_get_calendar()
if res.vegetables_harvest_season:
break
daemon.pop_blocks(GAME_UPDATE_FREQUENCY)
daemon.flush_txpool()
res = daemon.cc_get_flag(flag_id, get_packed_tiles = True)
assert res.role == ROLE_AGRICULTURAL
assert res.crop == CROP_VEGETABLES
flag_role = res.role
flag_economic_power = res.economic_power
flag_repair = res.repair
flag_construction_height = res.construction_height
flag_last_service_height = res.last_service_height
flag_service_price = res.service_price if 'service_price' in res else 0
flag_name = res.name
flag_ignore = res.ignore
flag_active = res.active
flag_budget = res.budget
flag_tiles = res.packed_tiles if 'packed_tiles' in res else []
flag_crop = res.crop
flag_sow_height = res.sow_height
flag_num_missed_ticks = res.num_missed_ticks
assert flag_crop == CROP_VEGETABLES
assert flag_num_missed_ticks > 0
self.wallet[2].cc_demolish(flag_id, role = flag_role, economic_power = flag_economic_power, repair = flag_repair, construction_height = flag_construction_height, last_service_height = flag_last_service_height, service_price = flag_service_price, name = flag_name, ignore = flag_ignore, active = flag_active, crop = flag_crop, sow_height = flag_sow_height, num_missed_ticks = flag_num_missed_ticks, budget = flag_budget, tiles = flag_tiles)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# rebuild another on top, which should clear the previous agricultural data
self.wallet[2].cc_buy_items(entries = [{'type': ITEM_WOOD, 'amount': 4500}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_flag(flag_id)
construction_height = res.construction_height
self.wallet[2].cc_building_settings(flag_id, ROLE_AGRICULTURAL, 100, construction_height)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_flag(flag_id, get_packed_tiles = True)
assert res.crop == CROP_NONE
assert res.sow_height == 0
def test_fire(self):
daemon = self.daemon
print("Testing fire")
flag_mil1_x0 = FIRST_CITY_X - 270
flag_mil1_y0 = FIRST_CITY_Y - 220
flag_mil1_x1 = flag_mil1_x0 + 32 - 1
flag_mil1_y1 = flag_mil1_y0 + 32 - 1
flag_mil2_x0 = FIRST_CITY_X - 520
flag_mil2_y0 = FIRST_CITY_Y - 150
flag_mil2_x1 = flag_mil2_x0 + 128 - 1
flag_mil2_y1 = flag_mil2_y0 + 128 - 1
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
flags = res.flags
flag_id = None
flag_repair = 1000000
flag_distance = 99999999
flag_name = ""
flag_city = 0
for flag in flags:
res = daemon.cc_get_flag(flag)
d1 = get_distance(res.x0, res.y0, res.x1, res.y1, flag_mil1_x0, flag_mil1_y0, flag_mil1_x1, flag_mil1_y1)
d2 = get_distance(res.x0, res.y0, res.x1, res.y1, flag_mil2_x0, flag_mil2_y0, flag_mil2_x1, flag_mil2_y1)
if res.role != ROLE_EMPTY and max(d1, d2) < flag_distance:
flag_repair = res.repair
flag_id = flag
flag_name = res.name
flag_city = res.city
flag_distance = max(d1, d2)
assert flag_id != None
assert flag_repair > 0
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 500000}, {'type': ITEM_STONE + 1, 'amount': 13000}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_WOOD, 'amount': 75000}, {'type': ITEM_WOOD + 1, 'amount': 42000}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_LABOUR, 'amount': 4000000}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# build a military
res = self.wallet[2].cc_buy_land(flag_mil1_x0, flag_mil1_y0, flag_mil1_x1, flag_mil1_y1)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account_id)
flag_mil1_id = res.flags[-1]
res = daemon.cc_get_flag(flag_mil1_id)
construction_height = res.construction_height
self.wallet[2].cc_building_settings(flag_mil1_id, ROLE_MILITARY, 100, construction_height = construction_height)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# start a fire (might have to wait two ticks first if there's a current event already)
res = daemon.cc_get_city(flag_city)
if res.special_event != SPECIAL_EVENT_NONE:
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY)
res = daemon.cc_get_city(flag_city)
assert res.special_event == SPECIAL_EVENT_NONE
res = daemon.cc_get_flag(flag_id)
assert res.fire_state == 0
self.wallet[2].cc_rename_flag(flag_id, old_name = flag_name, new_name = 'test:fire')
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 1)
# pass next update
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
res = daemon.cc_get_city(flag_city)
assert res.special_event == SPECIAL_EVENT_FIRE
# get close to next update, so we build a military right before it gets activated
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', GAME_UPDATE_FREQUENCY - 5)
# build a military, it'll be pretty much powerless right after building
res = self.wallet[2].cc_buy_land(flag_mil2_x0, flag_mil2_y0, flag_mil2_x1, flag_mil2_y1)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account_id)
flag_mil2_id = res.flags[-1]
res = daemon.cc_get_flag(flag_mil2_id)
construction_height = res.construction_height
self.wallet[2].cc_building_settings(flag_mil2_id, ROLE_MILITARY, 120, construction_height = construction_height)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_flag(flag_id)
assert res.fire_state == 1
self.wallet[2].cc_rename_flag(flag_id, old_name = 'test:fire', new_name = flag_name)
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 1)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 5)
# try to fight fire with the new building, it'll fail
res = daemon.cc_get_flag(flag_id)
fire_state = res.fire_state
area = (res.x1 - res.x0 + 1) * (res.y1 - res.y0 + 1)
res = daemon.cc_get_flag(flag_mil2_id)
service_price = res.service_price if 'service_price' in res else 0
last_service_height = res.last_service_height
cost = service_price * area
res = self.wallet[2].cc_fight_fire(flag_id, fire_state, flag_mil2_id, last_service_height, cost)
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 1)
res = daemon.cc_get_flag(flag_id)
assert res.fire_state > 0 and res.fire_state < 128
# this one might work
res = daemon.cc_get_flag(flag_id)
fire_state = res.fire_state
area = (res.x1 - res.x0 + 1) * (res.y1 - res.y0 + 1)
res = daemon.cc_get_flag(flag_mil1_id)
service_price = res.service_price if 'service_price' in res else 0
last_service_height = res.last_service_height
cost = service_price * area
res = self.wallet[2].cc_fight_fire(flag_id, fire_state, flag_mil1_id, last_service_height, cost)
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 1)
res = daemon.cc_get_flag(flag_id)
assert res.fire_state > 0
# wait till fire runs out
on_fire = True
for i in range(21):
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
res = daemon.cc_get_city(flag_city)
if res.special_event == SPECIAL_EVENT_NONE:
on_fire = False
break
assert not on_fire
res = daemon.cc_get_flag(flag_id)
assert res.fire_state == 0
def test_derelict(self):
daemon = self.daemon
print("Testing derelict buildings")
res = self.wallet[2].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
flags = res.flags
flag_id = None
flag_repair = 1000000
flag_name = ""
for flag in flags:
res = daemon.cc_get_flag(flag)
if res.role != ROLE_EMPTY and res.repair < flag_repair:
flag_repair = res.repair
flag_id = flag
flag_name = res.name
assert flag_id != None
assert flag_repair > 0
self.wallet[2].cc_rename_flag(flag_id, old_name = flag_name, new_name = 'test:decay')
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 1)
for i in range(20):
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', GAME_UPDATE_FREQUENCY // 2)
res = daemon.cc_get_flag(flag_id)
if res.role == ROLE_EMPTY:
break
res = daemon.get_info()
height = res.height
res = daemon.cc_get_game_events(min_height = height - GAME_UPDATE_FREQUENCY, max_height = height, account = account_id)
found = False
for e in res.events:
if "derelict" in e.event and e.flags == [flag_id]:
found = True
break
assert found
self.wallet[2].cc_rename_flag(flag_id, old_name = 'test:decay', new_name = flag_name)
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 1)
def test_cities(self):
daemon = self.daemon
print("Testing cities")
fast = False
try: fast = int(os.environ['CC_FIXED_COST_CITIES'])
except: pass
# get a lot more money
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 60)
for i in range(8 if fast else 200):
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 100)
self.wallet[1].refresh()
res = self.wallet[1].cc_deposit(amount = 150000000000)
self.deposits += 150000000000
assert len(res.fee_list) == 1
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# build a lot
res = self.wallet[1].cc_get_info()
account_id = res.account_id
self.wallet[1].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 500000 if fast else 5000000}, {'type': ITEM_WOOD, 'amount': 800000 if fast else 25000000}, {'type': ITEM_LABOUR, 'amount': 5000000 if fast else 350000000}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
farms = []
buildings = []
for i in range(1 if fast else 100):
self.wallet[1].cc_buy_land(FIRST_CITY_X + i * 128, FIRST_CITY_Y + 1500, FIRST_CITY_X + i * 128 + 127, FIRST_CITY_Y + 1500 + 127)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert 'flags' in res and len(res.flags) > 0
last_flag = res.flags[-1]
role = [ROLE_AGRICULTURAL, ROLE_CRAFT, ROLE_SAWMILL, ROLE_STONECUTTER, ROLE_WORKFORCE, ROLE_AGRICULTURAL, ROLE_RESIDENTIAL1][i % 7]
if role == ROLE_AGRICULTURAL:
farms.append(last_flag)
buildings.append(last_flag)
res = daemon.cc_get_flag(last_flag)
construction_height = res.construction_height
self.wallet[1].cc_building_settings(last_flag, role, 100, construction_height)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# mine through updates, sowing and harvesting so flags get active and start counting towards city progress
current_crop = CROP_NONE
while not fast:
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
repair = []
for flag in buildings:
res = daemon.cc_get_flag(flag)
if res.repair < 500000:
repair.append({'flag': flag, 'delta_repair': 1000000 - res.repair})
if len(repair) > 0:
self.wallet[1].cc_repair(repair)
res = daemon.cc_get_calendar()
if res.vegetables_sowing_season and current_crop == CROP_NONE:
for flag in farms:
res = daemon.cc_get_flag(flag)
if res.crop == CROP_NONE:
self.wallet[1].cc_sow(flag, CROP_VEGETABLES)
current_crop = CROP_VEGETABLES
elif res.vegetables_harvest_season and (res.grain_sowing_season or res.blocks_till_grain_sowing_season_start <= GAME_UPDATE_FREQUENCY) and current_crop == CROP_VEGETABLES:
for flag in farms:
res = daemon.cc_get_flag(flag)
if res.crop == CROP_VEGETABLES:
vegetables_nutrients = res.vegetables_nutrients
sow_height = res.sow_height
num_missed_ticks = res.num_missed_ticks
self.wallet[1].cc_harvest(flag, CROP_VEGETABLES, sow_height, vegetables_nutrients, num_missed_ticks)
current_crop = CROP_NONE
elif res.grain_sowing_season and current_crop == CROP_NONE:
for flag in farms:
res = daemon.cc_get_flag(flag)
if res.crop == CROP_NONE:
self.wallet[1].cc_sow(flag, CROP_GRAIN)
current_crop = CROP_GRAIN
elif res.grain_harvest_season and res.blocks_till_grain_harvest_season_end <= GAME_UPDATE_FREQUENCY and current_crop == CROP_GRAIN:
for flag in farms:
res = daemon.cc_get_flag(flag)
if res.crop == CROP_GRAIN:
grain_nutrients = res.grain_nutrients
sow_height = res.sow_height
num_missed_ticks = res.num_missed_ticks
self.wallet[1].cc_harvest(flag, CROP_GRAIN, sow_height, grain_nutrients, num_missed_ticks)
current_crop = CROP_NONE
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_cities()
cities = res.cities
assert len(cities) == 1
total_shares = res.total_shares
res = daemon.cc_get_new_city_cost()
cost = res.cost
res = daemon.cc_get_calendar()
res = daemon.cc_get_account(account_id)
res = daemon.cc_get_new_city_cost()
cost = res.cost
self.wallet[1].refresh()
res = self.wallet[1].get_balance()
if cost + 1e8 < res.cc_balance:
break
res = self.wallet[1].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
cosmopolitan_found = False
if 'badges' in res:
for e in res.badges:
if e.type == BADGE_COSMOPOLITAN:
cosmopolitan_found = True
assert not cosmopolitan_found
# create city
res = daemon.cc_get_new_city_cost()
cost = res.cost
self.wallet[1].cc_found_city(1, 42, 'Ur', cost)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_cities()
assert len(res.cities) == 2
assert res.cities[0].city_id == 0
assert res.cities[0].name == "Helsengaard"
assert res.cities[0].mayor == 1
assert res.cities[1].city_id == 1
assert res.cities[1].name == "Ur"
assert res.cities[1].mayor == account_id
assert res.cities[1].ox != res.cities[0].ox
assert res.cities[1].oy != res.cities[0].oy
res = daemon.cc_get_city(1)
assert res.name == 'Ur'
assert res.mayor == account_id
assert res.ox > 0
assert res.oy > 0
assert res.seed == 42
assert res.treasury == 10 or fast
assert res.specializations == 0
assert res.max_level == 0
assert res.moose == NUM_STARTING_MOOSE
assert res.bears == NUM_STARTING_BEARS
ur_ox = res.ox
ur_oy = res.oy
# build in this new city
self.wallet[1].cc_buy_land(ur_ox + i * 128, ur_oy + 1500, ur_ox + i * 128 + 127, ur_oy + 1500 + 127, city = 1)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account_id)
flag = res.flags[-1]
res = daemon.cc_get_flag(flag)
construction_height = res.construction_height
self.wallet[1].cc_building_settings(flag, ROLE_AGRICULTURAL, 100, construction_height)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# get some food to feed buildings
for i in range(4):
res = daemon.cc_get_city(1)
self.wallet[1].cc_hunt(target = 0, city = 1)
self.wallet[1].cc_hunt(target = 1, city = 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# generate some city weight so the next game update gets us some of the block reward
for i in range(7 if fast else 20):
self.wallet[1].cc_buy_land(ur_ox + i * 128, ur_oy + 2500, ur_ox + i * 128 + 127, ur_oy + 2500 + 127, city = 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert 'flags' in res and len(res.flags) > 0
last_flag = res.flags[-1]
role = [ROLE_AGRICULTURAL, ROLE_SAWMILL, ROLE_STONECUTTER, ROLE_WORKFORCE, ROLE_AGRICULTURAL, ROLE_RESIDENTIAL1][i % 6]
res = daemon.cc_get_flag(last_flag)
construction_height = res.construction_height
self.wallet[1].cc_building_settings(last_flag, role, 100, construction_height)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
# wait three game updates for influence to propagate
for i in range(3):
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
res = daemon.cc_get_shares(0)
shares_0 = res.weighted_shares
assert shares_0 > 0
assert sum([x.num_shares for x in res.shares]) > 0
assert sum([x.payout for x in res.shares]) > 0
res = daemon.cc_get_shares(1)
shares_1 = res.weighted_shares
assert shares_1 > 0
assert sum([x.num_shares for x in res.shares]) > 0
assert sum([x.payout for x in res.shares]) > 0
shares_ratio = shares_0 / float(shares_1)
res = daemon.cc_get_city(0)
treasury_balance_0 = res.treasury_balance
res = daemon.cc_get_city(1)
treasury_balance_1 = res.treasury_balance
# commands that share money between cities
self.wallet[1].cc_buy_items(entries = [{'type': ITEM_STONE, 'amount': 500000}])
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_city(0)
delta_treasury_balance_0 = res.treasury_balance - treasury_balance_0
treasury_balance_0 = res.treasury_balance
res = daemon.cc_get_city(1)
delta_treasury_balance_1 = res.treasury_balance - treasury_balance_1
treasury_balance_1 = res.treasury_balance
assert feq(delta_treasury_balance_0 / float(delta_treasury_balance_1), shares_ratio)
res = self.wallet[3].cc_new_item(0, "Other test coin", False, False, COINS_ITEM_GROUP, "primary desc", "secondary desc")
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_custom_items()
test_coin = res.items_[-2] # -2 for restricted items hack
self.wallet[1].cc_mint(test_coin.id, 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_city(0)
delta_treasury_balance_0 = res.treasury_balance - treasury_balance_0
treasury_balance_0 = res.treasury_balance
res = daemon.cc_get_city(1)
delta_treasury_balance_1 = res.treasury_balance - treasury_balance_1
treasury_balance_1 = res.treasury_balance
assert feq(delta_treasury_balance_0 / float(delta_treasury_balance_1), shares_ratio)
self.wallet[1].cc_smelt(test_coin.id, 1)
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_city(0)
delta_treasury_balance_0 = res.treasury_balance - treasury_balance_0
treasury_balance_0 = res.treasury_balance
res = daemon.cc_get_city(1)
delta_treasury_balance_1 = res.treasury_balance - treasury_balance_1
treasury_balance_1 = res.treasury_balance
assert feq(delta_treasury_balance_0 / float(delta_treasury_balance_1), shares_ratio)
res = self.wallet[1].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
cosmopolitan_level = 0
if 'badges' in res:
for e in res.badges:
if e.type == BADGE_COSMOPOLITAN:
cosmopolitan_level = e.level
assert cosmopolitan_level == 1
# A third town
if not fast:
res = daemon.cc_get_new_city_cost()
cost = res.cost
self.wallet[2].cc_found_city(2, 43, 'Ururur', cost)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_cities()
assert len(res.cities) == 3
assert res.cities[0].city_id == 0
assert res.cities[0].name == "Helsengaard"
assert res.cities[0].mayor == 1
assert res.cities[1].city_id == 1
assert res.cities[1].name == "Ur"
assert res.cities[1].mayor == account_id
assert res.cities[1].ox != res.cities[0].ox
assert res.cities[1].oy != res.cities[0].oy
assert res.cities[2].name == "Ururur"
assert res.cities[2].mayor == account_id + 1
assert res.cities[2].ox != res.cities[0].ox and res.cities[2].ox != res.cities[1].ox
assert res.cities[2].oy != res.cities[0].oy and res.cities[2].oy != res.cities[0].oy
res = daemon.cc_get_city(2)
assert res.name == 'Ururur'
assert res.mayor == account_id + 1
assert res.ox > 0
assert res.oy > 0
assert res.seed == 43
assert res.treasury == 10 or fast
assert res.specializations == 0
assert res.max_level == 0
assert res.moose == NUM_STARTING_MOOSE
assert res.bears == NUM_STARTING_BEARS
res = daemon.cc_get_city(0)
delta_treasury_balance_0 = res.treasury_balance - treasury_balance_0
treasury_balance_0 = res.treasury_balance
res = daemon.cc_get_city(1)
delta_treasury_balance_1 = res.treasury_balance - treasury_balance_1
treasury_balance_1 = res.treasury_balance
assert feq(delta_treasury_balance_0 / float(delta_treasury_balance_1), shares_ratio)
# city specializations
print("Testing city specializations")
# out of range
self.assert_exception(lambda: self.wallet[1].cc_add_city_specialization(1, 1000))
self.wallet[1].cc_add_city_specialization(1, 4)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_city(1)
assert res.specializations == 1 << 4
# duplicate
self.assert_exception(lambda: self.wallet[1].cc_add_city_specialization(1, 4))
# can't add too many
rejected = False
try:
for s in [0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]:
self.wallet[1].cc_add_city_specialization(1, s)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
except:
rejected = True
assert rejected
# cities can be traded
print("Testing city trading")
res = daemon.get_info()
height = res.height
# invalid city
self.assert_exception(lambda: self.wallet[1].cc_trade_city(False, 5, 1234567, 0, 0, 0, height + 10000))
# not the mayor
self.assert_exception(lambda: self.wallet[1].cc_trade_city(False, 0, 1234567, 0, 0, 0, height + 10000))
res = self.wallet[1].cc_trade_city(False, 1, 1234567, 0, 0, 0, height + 10000)
assert len(res.tx_hash) == 64
nonce = res.cc_nonce
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = self.wallet[2].cc_trade_city(True, 1, 12345678, 0, 0, 0, height + 10000, match_nonce = nonce, cost = 1234567)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = self.wallet[2].cc_get_info()
account2_id = res.account_id
res = daemon.cc_get_city(1)
assert res.mayor == account2_id
print("Testing settler list")
# lists start out empty
res = daemon.cc_get_city(0)
assert 'allow_settlers' not in res or len(res.allow_settlers) == 0
res = daemon.cc_get_city(1)
assert 'allow_settlers' not in res or len(res.allow_settlers) == 0
# only the mayor may modify the list
res = self.wallet[1].cc_get_info()
account_id = res.account_id
self.assert_exception(lambda: self.wallet[1].cc_allow_settlers([account_id], 1, allow = True))
# we can't remove someone who's not in the list
self.assert_exception(lambda: self.wallet[2].cc_allow_settlers([account_id], 1, allow = False))
self.wallet[2].cc_allow_settlers([account_id], 1, allow = True)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_city(1)
assert res.allow_settlers == [account_id]
# we can't add someone who's already in the list
self.assert_exception(lambda: self.wallet[2].cc_allow_settlers([account_id], 1, allow = True))
# other accounts can now not buy land in the city
res = self.daemon.cc_get_city(1)
city_ox = res.ox
city_oy = res.oy
self.assert_exception(lambda: self.wallet[2].cc_buy_land(city_ox + 1200, city_oy + 60, city_ox + 1200 + 16 - 1, city_oy + 60 + 16 - 1, city = 1))
# but the allowed one can
self.wallet[1].cc_buy_land(city_ox + 1200, city_oy + 80, city_ox + 1200 + 16 - 1, city_oy + 80 + 16 - 1, city = 1)
# we can remove from the list
self.wallet[2].cc_allow_settlers([account_id], 1, allow = False)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# other accounts can now buy land in the city again
self.wallet[1].cc_buy_land(city_ox + 1200, city_oy + 60, city_ox + 1200 + 16 - 1, city_oy + 60 + 16 - 1, city = 1)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
def test_discoveries(self):
daemon = self.daemon
print("Testing discoveries")
res = daemon.cc_get_discoveries()
assert len(res.discoveries) > 0
for d in res.discoveries:
assert d.name != ""
assert d.difficulty > 0
assert d.discoverer == 0
assert d.discovery_height == 0
assert d.budget == 0
assert not d.enabled
# pick the last researchable one which is the single prerequisite of another, research it
discovery_index = None
second_discovery_index = None
for idx in range(len(res.discoveries)):
d = res.discoveries[idx]
if (not 'prerequisites' in d or len(d.prerequisites) == 0) and ('patent_time' in d and d.patent_time > 0):
for idx2 in range(len(res.discoveries)):
d2 = res.discoveries[idx2]
if 'prerequisites' in d2 and d2.prerequisites == [d.discovery]:
discovery_index = idx
second_discovery_index = idx2
if discovery_index != None:
break
assert discovery_index != None
assert second_discovery_index != None
discovery = res.discoveries[discovery_index].discovery
second_discovery = res.discoveries[second_discovery_index].discovery
# can't fund less than the minimum amount
ok = False
try: self.wallets[2].cc_research(discovery, MIN_RESEARCH_AMOUNT - 1)
except: ok = True
assert ok
res = self.wallet[2].cc_get_info()
account_id = res.account_id
budget = 0
while True:
amount = 200000000000
blocks = amount // 1500000000
res = self.wallet[2].cc_deposit(amount = amount)
self.deposits += amount
res = self.wallet[2].cc_research(discovery, amount)
assert len(res.tx_hash) == 64
budget += amount
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
res = daemon.cc_get_discoveries()
d = res.discoveries[discovery_index]
if d.discoverer != 0:
break
assert d.discoverer == account_id
res = daemon.get_info()
assert d.discovery_height >= res.height - 1 - blocks and d.discovery_height < res.height
assert d.research_start_height == 0
assert d.budget == budget
# it should be available for us, but not for others
res = daemon.cc_get_discoveries(account_id)
assert res.discoveries[discovery_index].enabled
res = daemon.cc_get_discoveries(account_id + 1)
assert not res.discoveries[discovery_index].enabled
res = daemon.cc_get_discoveries(0)
assert not res.discoveries[discovery_index].enabled
res = daemon.cc_get_discoveries()
assert not res.discoveries[discovery_index].enabled
# it should have unlocked the original discoverery that had it as prerequisite
res = daemon.get_info()
blockchain_height = res.height
res = daemon.cc_get_discoveries(account_id)
d2 = res.discoveries[second_discovery_index]
assert d2.research_start_height >= blockchain_height - 1 - blocks and d2.research_start_height < blockchain_height
assert d2.discoverer == 0
assert d2.discovery_height == 0
# research a string of unlocking discoveries
for discovery in [DISCOVERY_RESEARCH_EFFICIENCY, DISCOVERY_RESEARCH_PRODUCTIVITY, DISCOVERY_RESEARCH_EXPERTISE, DISCOVERY_RESEARCH_MASTERY]:
while True:
amount = 200000000000
blocks = amount // 3000000000
res = self.wallet[2].refresh()
res = self.wallet[2].cc_deposit(amount = amount)
self.deposits += amount
res = self.wallet[2].cc_research(discovery, amount)
assert len(res.tx_hash) == 64
budget += amount
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
res = daemon.cc_get_discoveries()
discovered = False
for d in res.discoveries:
if d.discovery == discovery:
if d.discoverer:
discovered = True
if discovered:
break
def make_native_script(self, owner, states):
source = "script { name \"test script\" description \"description\" owner " + str(owner) + "\n"
state_idx = 0
for s in states:
source += "state \"state " + str(state_idx) + "\" { text \"state " + str(state_idx) + "\"\n"
choice_idx = 0
for choice in s:
next_state = "" if choice == -1 else ("state " + str(choice))
source += " choice { text \"choice " + str(choice_idx) + "\" next state \"" + next_state + "\"}\n"
choice_idx += 1
state_idx += 1
source += "}\n"
source += "}"
return source
def test_scripts(self):
daemon = self.daemon
print("Testing scripts")
res = daemon.cc_get_scripts()
assert 'scripts' not in res or len(res.scripts) == 0
self.assert_exception(lambda: daemon.cc_get_script(0))
self.assert_exception(lambda: daemon.cc_get_script(1))
res = self.wallet[2].cc_get_info()
account2_id = res.account_id
res = self.wallet[3].cc_get_info()
account3_id = res.account_id
# no end state
self.assert_exception(lambda: self.wallet[3].cc_create_script(source = self.make_native_script(0, [])))
self.assert_exception(lambda: self.wallet[3].cc_create_script(source = self.make_native_script(0, [[0, 1], [2, 0]])))
self.wallet[3].cc_create_script(source = self.make_native_script(0, [[0, 1], [2, 0], [1, -1]]))
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_scripts(include_blob = True)
assert len(res.scripts) == 1
assert res.scripts[0].index == 1
blob = res.scripts[0].blob
res = daemon.cc_get_script(1)
assert res.name == "test script"
assert res.icon == ""
assert res.desc == "description"
assert len(res.blob) > 0
assert res.blob == blob
res = daemon.cc_get_script_state(account = 1, script = 1, state = 0, city = 0)
assert res.ui == ""
assert res.text == "state 0"
assert res.image == ""
assert len(res.choices) == 2
assert res.choices[0].id == 0
assert res.choices[0].text == "choice 0"
assert res.choices[1].id == 1
assert res.choices[1].text == "choice 1"
assert res.choices_ui == ""
res = daemon.cc_get_script_state(account = 1, script = 1, state = 1, city = 0)
assert res.ui == ""
assert res.text == "state 1"
assert res.image == ""
assert len(res.choices) == 2
assert res.choices[0].id == 0
assert res.choices[0].text == "choice 0"
assert res.choices[1].id == 1
assert res.choices[1].text == "choice 1"
assert res.choices_ui == ""
res = daemon.cc_get_script_state(account = 1, script = 1, state = 2, city = 0)
assert res.ui == ""
assert res.text == "state 2"
assert res.image == ""
assert len(res.choices) == 2
assert res.choices[0].id == 0
assert res.choices[0].text == "choice 0"
assert res.choices[1].id == 1
assert res.choices[1].text == "choice 1"
assert res.choices_ui == ""
# invalid city
self.assert_exception(lambda: self.wallet[2].cc_start_script(1, city = 11))
res = daemon.cc_get_account(account2_id)
assert 'reserve' not in res
assert res.script == 0
assert res.script_state == 0
assert res.script_city == 0
res = self.wallet[2].cc_start_script(1, city = 0)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' not in res
assert res.script == 1
assert res.script_state == 0
assert res.script_city == 0
assert res.script_owner == GAME_ACCOUNT
self.assert_exception(lambda: self.wallet[2].cc_script_choice(0, state = 0, city = 0, owner = GAME_ACCOUNT, choice = 0))
self.assert_exception(lambda: self.wallet[2].cc_script_choice(2, state = 0, city = 0, owner = GAME_ACCOUNT, choice = 0))
self.assert_exception(lambda: self.wallet[2].cc_script_choice(1, state = 1, city = 0, owner = GAME_ACCOUNT, choice = 0))
self.assert_exception(lambda: self.wallet[2].cc_script_choice(1, state = -1, city = 0, owner = GAME_ACCOUNT, choice = 0))
self.assert_exception(lambda: self.wallet[2].cc_script_choice(1, state = 0, city = 11, owner = GAME_ACCOUNT, choice = 0))
self.assert_exception(lambda: self.wallet[2].cc_script_choice(1, state = 0, city = 0, owner = GAME_ACCOUNT, choice = -1))
self.assert_exception(lambda: self.wallet[2].cc_script_choice(1, state = 0, city = 0, owner = GAME_ACCOUNT, choice = 11))
self.assert_exception(lambda: self.wallet[2].cc_script_choice(1, state = 0, city = 0, owner = account3_id, choice = 0))
res = self.wallet[2].cc_script_choice(1, state = 0, city = 0, owner = GAME_ACCOUNT, choice = 0)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' not in res
assert res.script == 1
assert res.script_state == 0
assert res.script_city == 0
assert res.script_owner == GAME_ACCOUNT
res = self.wallet[2].cc_script_choice(1, state = 0, city = 0, owner = GAME_ACCOUNT, choice = 1)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' not in res
assert res.script == 1
assert res.script_state == 1
assert res.script_city == 0
assert res.script_owner == GAME_ACCOUNT
res = self.wallet[2].cc_script_choice(1, state = 1, city = 0, owner = GAME_ACCOUNT, choice = 1)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' not in res
assert res.script == 1
assert res.script_state == 0
assert res.script_city == 0
assert res.script_owner == GAME_ACCOUNT
res = self.wallet[2].cc_script_choice(1, state = 0, city = 0, owner = GAME_ACCOUNT, choice = 1)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' not in res
assert res.script == 1
assert res.script_state == 1
assert res.script_city == 0
assert res.script_owner == GAME_ACCOUNT
res = self.wallet[2].cc_script_choice(1, state = 1, city = 0, owner = GAME_ACCOUNT, choice = 0)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' not in res
assert res.script == 1
assert res.script_state == 2
assert res.script_city == 0
assert res.script_owner == GAME_ACCOUNT
res = self.wallet[2].cc_script_choice(1, state = 2, city = 0, owner = GAME_ACCOUNT, choice = 1)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' not in res
assert res.script == 0
assert res.script_state == 0
assert res.script_city == 0
assert res.script_owner == 0
self.assert_exception(lambda: self.wallet[2].cc_script_choice(1, state = 0, city = 0, owner = GAME_ACCOUNT, choice = 0))
print('testing looping script')
script = """
script {
name "looping state test"
precondition global "allow" == 1
reserves {
owner gold 0.002
player gold 0.001
}
state "0" {
choice { text "0" next state "0" actions { set local "tc" 0 award gold 0.00000001 + random 16} }
choice { text "1" next state "0" actions { set local "tc" 1 pay gold 0.00000002 set player "x" player "x" + 1} }
choice { text "2" next state "0" actions { set local "tc" 2 } }
choice { text "second" next state "1" actions { set global "loop-done" 2 + global "loop-done" } }
}
state "1" {
choice { text "0" next state "2" enabled local "ready" != 0 }
choice { text "1" next state "1" actions { set local "ready" 8 } }
}
state "2" {
choice { text "0" next state ""}
}
}
"""
self.wallet[3].cc_create_script(source = script)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_scripts(include_blob = True)
assert len(res.scripts) > 1
script_id = res.scripts[-1].index
# it should not be available yet
res = daemon.cc_get_scripts(first_script = script_id, last_script = script_id, account = account2_id)
assert len(res.scripts) == 1
assert not res.scripts[0].available
self.assert_exception(lambda: self.wallet[2].cc_start_script(script_id, city = 0))
res = self.wallet[3].cc_set_script_variable('allow', 0, 1)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# it should now be available
res = daemon.cc_get_scripts(first_script = script_id, last_script = script_id, account = account2_id)
assert len(res.scripts) == 1
assert res.scripts[0].available
# can't start if disabled
self.wallet[3].cc_enable_script(script_id, False)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
self.assert_exception(lambda: self.wallet[2].cc_start_script(script_id, city = 0))
self.wallet[3].cc_enable_script(script_id, True)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# start the script
res = self.wallet[2].cc_start_script(script_id, city = 0)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' in res
assert res.script == script_id
assert res.script_state == 0
assert res.script_city == 0
assert res.script_owner == GAME_ACCOUNT
for i in [ 0, 1, 0, 2, 2, 1, 2, 2, 1, 0, 1, 0, 1, 1, 1, 2, 1, 0, 2, 1, 3]:
res = self.wallet[2].cc_script_choice(script_id, state = 0, city = 0, owner = GAME_ACCOUNT, choice = i)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert 'reserve' in res
assert res.script == script_id
assert res.script_city == 0
assert res.script_owner == GAME_ACCOUNT
if i == 3:
assert res.script_state == 1
assert len(res.script_local_variables) == 1
assert res.script_local_variables[0] == { 'name': 'tc', 'value': 1} # last before we changed state
else:
assert res.script_state == 0
if i == 0:
assert 'script_local_variables' not in res
else:
assert len(res.script_local_variables) == 1
assert res.script_local_variables[0] == { 'name': 'tc', 'value': i}
res = daemon.cc_get_script_variable('loop-done')
assert res.value == 2
res = daemon.cc_get_account(account2_id)
x = [x for x in res.script_variables if x.name == 'x'][0]
assert x.name == 'x'
assert x.value == 9
# we're in the second state now, we can't choose choice 0 until we chose choice 1
self.assert_exception(lambda: self.wallet[2].cc_script_choice(script_id, state = 1, city = 0, owner = GAME_ACCOUNT, choice = 0))
res = self.wallet[2].cc_script_choice(script_id, state = 1, city = 0, owner = GAME_ACCOUNT, choice = 1)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = self.wallet[2].cc_script_choice(script_id, state = 1, city = 0, owner = GAME_ACCOUNT, choice = 0)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_account(account2_id)
assert res.script_state == 2
def check_gold_consistency(self):
daemon = self.daemon
print("Checking gold consistency")
# get all txes from the DB, get the ones with commands, accumulate fees, let the total be F
# get all the deposits/withdraws, sum it all, let the total be D
# sum up balances + coins and custom items of all accounts, let the total be B
# sum up all game subsity, let the total be S
# then D + S = B + F
game_fees = 0
game_txes = 0
res = daemon.get_info()
height = res.height
res = daemon.get_block_headers_range(0, height - 1, False)
headers = res.headers
for h in headers:
if h.num_txes > 0:
res = daemon.get_block(hash = h.hash, fill_pow_hash = False, include_blob = False, include_json = False)
res = daemon.get_transactions(res.tx_hashes, decode_as_json = False, prune = True, get_cc_data = True)
for tx in res.txs:
game_txes += 1
game_fees += tx.cc_fee
gold_balance_available = 0
gold_balance_reserved = 0
gold_balance_in_coins = 0
gold_balance_in_custom_items = 0
for i in range(1, 16):
try:
res = daemon.cc_get_account(i)
gold_balance_available += res.balance
if 'reserve' in res:
for e in res.reserve:
gold_balance_reserved += e['balance']
for e in res.item_balances:
if e['type'] >= ITEM_FIRST_USER and e['type'] <= ITEM_LAST_USER:
res2 = daemon.cc_get_custom_items([e['type']])
assert len(res2.items_) == 1
if res2.items_[0].group == COINS_ITEM_GROUP and not res2.items_[0].is_group:
gold_content = get_gold_content(COIN_TYPE(res2.items_[0].user_data))
gold_balance_in_coins += e['amount'] * gold_content * 100000000
else:
gold_balance_in_custom_items += e['amount'] * res2.items_[0].gold
except:
pass
game_subsidy = 0
res = daemon.get_info()
height = res.height
res = daemon.get_coinbase_tx_sum(0, height)
game_subsidy = res.game_subsidy
D = self.deposits
S = game_subsidy
B = gold_balance_available + gold_balance_reserved + gold_balance_in_coins + gold_balance_in_custom_items
F = game_fees
if D + S != B + F:
print('Game fees for ' + str(game_txes) + ' txes: ' + str(game_fees / 1e8))
print('Deposit/withdraw balance: ' + str(self.deposits / 1e8))
print('Gold in available balance: ' + str(gold_balance_available / 1e8))
print('Gold in reserved balance: ' + str(gold_balance_reserved / 1e8))
print('Gold in coins balance: ' + str(gold_balance_in_coins / 1e8))
print('Gold in custom items balance: ' + str(gold_balance_in_custom_items / 1e8))
print('Game subsidy: ' + str(game_subsidy / 1e8))
print('D: ' + str(D / 1e8))
print('S: ' + str(S / 1e8))
print('B: ' + str(B / 1e8))
print('F: ' + str(F / 1e8))
print('D + S: ' + str((D + S) / 1e8))
print('B + F: ' + str((B + F) / 1e8))
assert D + S == B + F
def check_event_balances(self):
daemon = self.daemon
print("Checking event balances")
res = daemon.get_height()
height = res.height
for i in [0, 1, 2, 3, 4]:
try:
res = self.wallet[i].cc_get_info()
account_id = res.account_id
res = daemon.cc_get_account(account_id)
balance = res.balance
if 'reserve' in res:
for e in res.reserve:
balance += e.balance
except:
continue
res = daemon.cc_get_game_events(min_height = 0, max_height = height, account = account_id)
event_balance = 0
for e in res.events:
event_balance += e.balance
event_balance -= e.tx_fee
if balance != event_balance:
print('Event balance does not match actual balance for account ' + str(account_id) + ' (wallet ' + str(i) + ')')
print('balance: ' + str(balance))
print('event_balance: ' + str(event_balance))
assert balance == event_balance
def check_item_count_consistency(self):
daemon = self.daemon
print("Checking item count consistency")
item_balances = []
for account_id in range(256):
try:
res = daemon.cc_get_account(account_id)
if 'item_balances' in res:
for e in res.item_balances:
item_balances = self.add_item(item_balances, e['type'], e['amount'])
except:
pass
item_count = []
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
if 'counts' in res:
for idx in range(len(res.counts)):
if res.counts[idx] > 0:
item_count = self.add_item(item_count, idx, res.counts[idx])
res = daemon.cc_get_custom_items()
items = res.items_ if 'items_' in res else []
res = daemon.cc_get_item_count([x + ITEM_FIRST_USER for x in range(256)])
if 'counts' in res:
for idx in range(len(res.counts)):
if res.counts[idx] > 0:
is_group = False
for e in items:
if e.id == idx + ITEM_FIRST_USER:
is_group = e.is_group
break
if not is_group:
item_count = self.add_item(item_count, idx + ITEM_FIRST_USER, res.counts[idx])
item_supply = []
for item in itertools.chain(range(280), range(ITEM_FIRST_FOOD, ITEM_FIRST_FOOD + 32), range(ITEM_FIRST_PATENT, ITEM_FIRST_PATENT + 256), range(ITEM_FIRST_GEMSTONE, ITEM_FIRST_GEMSTONE + 16), [x + ITEM_FIRST_USER for x in range(256)]):
res = daemon.cc_get_item_ownership(item)
if 'ownership' in res:
total = 0
for e in res.ownership:
assert e.amount > 0
total += e.amount
assert total == res.supply
item_supply = self.add_item(item_supply, item, total)
assert item_balances == item_count, "item_balances: expected " + str(item_balances) + ", got " + str(item_count) + ", supply " + str(item_supply)
assert item_supply == item_count, "item_supply: expected " + str(item_supply) + ", got " + str(item_count) + ", balances " + str(item_balances)
def test_reorg(self):
daemon = self.daemon
second_daemon = Daemon(idx = 3)
third_daemon = Daemon(idx = 4)
print('Testing reorg')
res = daemon.get_info()
height = res.height
blocks = (GAME_UPDATE_FREQUENCY * 8000 - height) % GAME_UPDATE_FREQUENCY + 1
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
assert len(self.reorg_root_hash) == 64
res = daemon.get_info()
assert len(res.top_block_hash) == 64
assert res.top_block_hash != self.reorg_root_hash
original_top_block = res.top_block_hash
res = daemon.getblockheaderbyheight(self.reorg_root_height + 1)
first_orphaned_block_hash = res.block_header.hash
assert len(first_orphaned_block_hash) == 64
assert not res.block_header.orphan_status
assert res.block_header.height == self.reorg_root_height + 1
# ensure synced
print('Syncing second and third daemons')
daemon.out_peers(8)
daemon.in_peers(8)
second_daemon.out_peers(8)
second_daemon.in_peers(8)
third_daemon.out_peers(8)
third_daemon.in_peers(8)
loops = 100
while True:
res0 = daemon.get_info()
res1 = second_daemon.get_info()
res2 = third_daemon.get_info()
if res0.top_block_hash == res1.top_block_hash and res0.top_block_hash == res2.top_block_hash:
break
time.sleep(30)
loops -= 1
assert loops >= 0
res = daemon.get_info()
cumulative_difficulty = res.cumulative_difficulty
# disconnect third daemon so it keeps the original chain
third_daemon.out_peers(0)
third_daemon.in_peers(0)
# temporarily use second daemon
self.wallet[2].set_daemon('127.0.0.1:18183')
self.wallet[2].rescan_blockchain()
# mine
print('Mining on second daemon')
n_mined = 0
for i in range(1, GAME_UPDATE_FREQUENCY // 20 + 5):
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_LABOUR, 'amount': HUNT_LABOUR_COST}])
res = second_daemon.generateblocks('TF1MMEY5v2dN49XKNCKCdBqmTMM6GdZZA5UBbDgTewaUd3c2jDazN5yKrG1BBHX3UyPqKD9hrh3DpPTDmWiCmsuRpePT1MTaPxm', 1)
res = daemon.cc_get_city(0)
population = res.moose if int(i % 2) == 0 else res.bears
self.wallet[2].cc_hunt(target = int(i % 2), city = 0)
n_mined += 20
res = second_daemon.generateblocks('TF1MMEY5v2dN49XKNCKCdBqmTMM6GdZZA5UBbDgTewaUd3c2jDazN5yKrG1BBHX3UyPqKD9hrh3DpPTDmWiCmsuRpePT1MTaPxm', 19)
# back to previous daemon
self.wallet[2].set_daemon('127.0.0.1:18182')
self.wallet[2].rescan_blockchain()
# reconnect daemons 1 and 2
daemon.out_peers(8)
daemon.in_peers(8)
second_daemon.out_peers(8)
second_daemon.in_peers(8)
# wait till the first daemon is synced
print('Waiting for sync on first daemon')
while True:
res = daemon.get_info()
if res.cumulative_difficulty > cumulative_difficulty:
break
time.sleep(10)
# disconnect daemons again
daemon.out_peers(0)
daemon.in_peers(0)
second_daemon.out_peers(0)
second_daemon.in_peers(0)
# temporarily use third daemon
self.wallet[2].set_daemon('127.0.0.1:18184')
self.wallet[2].rescan_blockchain()
# mine on the third daemon, which still has the original chain
print('Mining another chain on third daemon')
n_mined_third = 0
while n_mined_third < n_mined:
res = self.wallet[2].cc_buy_items(entries = [{'type': ITEM_LABOUR, 'amount': HUNT_LABOUR_COST}])
res = second_daemon.generateblocks('TF1MMEY5v2dN49XKNCKCdBqmTMM6GdZZA5UBbDgTewaUd3c2jDazN5yKrG1BBHX3UyPqKD9hrh3DpPTDmWiCmsuRpePT1MTaPxm', 1)
res = third_daemon.cc_get_city(0)
population = res.moose if int(i % 2) == 0 else res.bears
self.wallet[2].cc_hunt(target = int(i % 2), city = 0)
res = third_daemon.generateblocks('TF1MMEY5v2dN49XKNCKCdBqmTMM6GdZZA5UBbDgTewaUd3c2jDazN5yKrG1BBHX3UyPqKD9hrh3DpPTDmWiCmsuRpePT1MTaPxm', 99)
n_mined_third += 100
res = third_daemon.generateblocks('TF1MMEY5v2dN49XKNCKCdBqmTMM6GdZZA5UBbDgTewaUd3c2jDazN5yKrG1BBHX3UyPqKD9hrh3DpPTDmWiCmsuRpePT1MTaPxm', 100)
res = third_daemon.get_info()
cumulative_difficulty = res.cumulative_difficulty
# back to previous daemon
self.wallet[2].set_daemon('127.0.0.1:18182')
self.wallet[2].rescan_blockchain()
state0 = get_daemon_state(third_daemon)
# reconnect all daemons
daemon.out_peers(8)
daemon.in_peers(8)
second_daemon.out_peers(8)
second_daemon.in_peers(8)
third_daemon.out_peers(8)
third_daemon.in_peers(8)
# wait while the reorg happens
print('Waiting for reorg on first daemon')
while True:
res = daemon.get_info()
if res.cumulative_difficulty == cumulative_difficulty:
break
time.sleep(10)
print('Back on the original chain')
# disconnect all daemons
daemon.out_peers(0)
daemon.in_peers(0)
second_daemon.out_peers(0)
second_daemon.in_peers(0)
third_daemon.out_peers(0)
third_daemon.in_peers(0)
state1 = get_daemon_state(daemon)
state0_clean = self.remove_crop_temperature(self.remove_height(state0))
state1_clean = self.remove_crop_temperature(self.remove_height(state1))
assert state0_clean == state1_clean, self.get_diff(state0_clean, state1_clean)
class Guard:
def __enter__(self):
for i in [2, 3, 4]:
Daemon(idx = i).out_peers(0)
Daemon(idx = i).in_peers(0)
for i in [0, 1, 2, 3, 4]:
Wallet(idx = i).auto_refresh(False)
def __exit__(self, exc_type, exc_value, traceback):
for i in [2, 3, 4]:
Daemon(idx = i).out_peers(8)
Daemon(idx = i).in_peers(8)
for i in [0, 1, 2, 3, 4]:
Wallet(idx = i).set_daemon('127.0.0.1:18180')
Wallet(idx = i).auto_refresh(True)
if __name__ == '__main__':
with Guard() as guard:
CCTest().run_test()