townforge/tests/functional_tests/cc.py
2020-09-14 13:30:07 +00:00

3757 lines
164 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
"""Test CC RPC
"""
from framework.daemon import Daemon
from framework.wallet import Wallet
FIRST_CITY_X = 0
FIRST_CITY_Y = 0
ITEM_STONE = 1
ITEM_MEDIUM_STONE = 2
ITEM_WOOD = 4
MATERIAL_VARIANT_PLOUGHED_FIELD = 32
MATERIAL_LOCKED_PINE = 26
ITEM_LABOUR = 256
ITEM_FIRST_PATENT = 1024
ITEM_FIRST_FOOD = ITEM_FIRST_PATENT + 4096
ITEM_FOOD_VEGETABLES = ITEM_FIRST_FOOD
ITEM_FOOD_GRAIN = ITEM_FIRST_FOOD + 1
ITEM_FOOD_MEAT = ITEM_FIRST_FOOD + 2
NUM_PREDEFINED_ITEMS = ITEM_FIRST_FOOD + 1024
ITEM_FIRST_USER = NUM_PREDEFINED_ITEMS
ITEM_LAST_USER = 65536 + 1048576 - 1
ORIGIN_RESOURCE_RADIUS = 500
ROLE_AGRICULTURAL = 1
ROLE_CRAFT = 2
ROLE_RESIDENTIAL1 = 5
ROLE_SAWMILL = 11
ROLE_STONECUTTER = 10
MAX_DISTANCE_PERCENTAGE = 1600
MAX_CC_NAME_LENGTH = 64
account_creation_fee = 100000000
GAME_UPDATE_FREQUENCY = 360
BUILD_RATIO_ACTIVE_THRESHOLD_PERCENT = 0
MIN_TRADE_EXPIRATION = (86400 / 60)
MIN_RESEARCH_AMOUNT = 10000000
LABOUR_LAST_RESORT_PRICE = 40000
STONE_LAST_RESORT_PRICE = 600000
NEW_ITEM_FEE = 100000000
DISCOVERY_RESEARCH_EFFICIENCY = 12
DISCOVERY_RESEARCH_PRODUCTIVITY = 13
DISCOVERY_RESEARCH_EXPERTISE = 14
DISCOVERY_RESEARCH_MASTERY = 15
DISCOVERY_BUCKET_BRIGADE = 31
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 = 200
GAME_ACCOUNT = 2
COINS_ITEM_GROUP = ITEM_FIRST_USER
COIN_ITEM_SETTLEMENT = COINS_ITEM_GROUP + 1
COIN_MINTING_FEE = 10000000
COIN_SMELTING_FEE = 1000000
COIN_GOLD_CONTENT = 2500000000
COIN_MINTING_WINDOW = (7 * 86400 / 60)
NONCE_CANCELLATION_FEE = 10000000
collectible_coin_type_players = 1
collectible_coin_type_city = 2
collectible_coin_type_yearly = 3
collectible_coin_type_event = 4
def assert_exception(f):
ok = False
try:
f()
except:
ok = True
assert ok
def RESOURCE_AVAILABILITY_SCALE(x):
return int((x) * 4)
def COIN_TYPE(x):
return x >> 56
def get_gold_content(coin_type):
if coin_type == collectible_coin_type_players:
return 20
if coin_type == collectible_coin_type_city:
return 10
if coin_type == collectible_coin_type_yearly:
return 5
if coin_type == collectible_coin_type_event:
return 25
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
class CCTest():
def run_test(self):
self.reset()
self.create()
state0 = self.get_state()
self.mine()
self.recordreorg_root()
self.check_no_items()
self.test_get_new_nonces()
self.test_cc_account_creation()
self.test_names()
self.test_cc_transfers()
self.test_buy_land()
self.test_buy_items()
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.test_game_update()
self.test_revert()
self.test_custom_items()
self.test_badges()
self.test_resize_flag()
self.test_attributes()
self.test_hunt()
self.test_invitation()
self.test_chat()
self.test_ignore()
self.test_minting()
self.test_farming()
self.test_cities()
self.test_reorg()
self.reset_and_check_states()
state1 = self.get_state()
assert state0 == state1, self.get_diff(state0, state1)
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 = []
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)
def generate_blocks(self, address, nblocks, skip_update = False):
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)
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', 15], # wallet[0]
['TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 15], # wallet[1]
['TF1MMCHHDFKYSQ1DkLWskTxU2fC7jtZLwcATXHP3uvJo4dV757dtGwUE9ZL6SE9DJhxrvzThAsG4drVmAPHzEvh6TCq19AbzTo4', 330], # wallet[3]
['TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 500], # 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 at game start')
res = daemon.cc_get_item_count([x for x in range(NUM_PREDEFINED_ITEMS)])
if 'count' in res:
for x in res.count:
assert x == 0
def test_get_new_nonces(self):
daemon = self.daemon
print("Testing getting new nonces")
assert_exception(lambda self: 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')
assert len(res.fee_list) == 1
fee0 = res.fee_list[0]
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')
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)
balance[1] += 25000000 - res.fee_list[0]
cc_balance[1] -= 25000000
res = self.wallet[2].cc_deposit(amount = 500000000, name = 'wallet 2')
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])
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)
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)
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)
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)
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)
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(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(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(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(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(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 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(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, a.amount)
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_build(self):
daemon = self.daemon
print("Testing building")
flag_id = 2
# 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)
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)
except: ok = True
assert ok
# can't set 0 economic power
ok = False
try: self.wallet[2].cc_building_settings(flag_id, 1, 0)
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)
except: ok = True
assert ok
# ok
res = self.wallet[2].cc_building_settings(flag_id, 1, 100, 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, 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):
expected_tiles = []
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, 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):
expected_tiles = []
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_WOOD], encoded = False, build_height = 2)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_flag(flag_id, 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):
expected_tiles = []
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_WOOD]
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, 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
assert_exception(lambda self: 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}]))
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.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
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
# 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 == 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 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.mined
assert trade.amount == 2
trade = res.bids[0]
assert trade.nonce == bid_1_2_80_a_nonce
assert trade.mined
assert trade.amount == 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 == 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 == 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 == 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.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 == 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.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.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
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
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)
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
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
# 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")
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]
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
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)
# 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, 80, 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(match = nonce_0, matches = [{'nonce': nonce_1, 'amount': 6}])
except: ok = True
assert ok
ok = False
try: res = self.wallet[2].cc_match(match = nonce_1, matches = [{'nonce': nonce_0, 'amount': 6}])
except: ok = True
assert ok
# match 0 amount
ok = False
try: res = self.wallet[2].cc_match(match = nonce_0, matches = [{'nonce': nonce_1, 'amount': 0}])
except: ok = True
assert ok
ok = False
try: res = self.wallet[2].cc_match(match = nonce_1, matches = [{'nonce': nonce_0, 'amount': 0}])
except: ok = True
assert ok
# match one
res = self.wallet[2].cc_match(match = nonce_0, matches = [{'nonce': nonce_1_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 * 80
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
# 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(match = nonce_1_1, cost = 2 * 80, matches = [{'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 * 80
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
# 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(match = nonce_0, matches = [{'nonce': nonce_1, 'amount': 2}])
except: ok = True
assert ok
ok = False
try: res = self.wallet[2].cc_match(match = nonce_1, matches = [{'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()
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_nonce(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
assert_exception(lambda self: self.wallet[1].cc_cancel_nonce(nonce))
res = self.wallet[0].cc_cancel_nonce(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_nonce(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
# the trade tx is mined, but further matches can be prevented
assert_exception(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
assert_exception(lambda self: self.wallet[0].cc_cancel_nonce(nonce))
# nor ourselves
assert_exception(lambda self: self.wallet[2].cc_cancel_nonce(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")
res = daemon.cc_get_new_flag_cost(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(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': 2943}, {'type': ITEM_WOOD, 'amount': 2150}, {'type': ITEM_LABOUR, 'amount': 50000}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_building_settings(flag0_id, ROLE_AGRICULTURAL, 100)
assert len(res.tx_hash) == 64
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_building_settings(flag1_id, ROLE_AGRICULTURAL, 100)
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()
# 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)
# 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)
def get_state(self):
daemon = self.daemon
state = {}
res = daemon.get_info()
state['info.height'] = res.height
res = daemon.cc_get_cities()
state['cities'] = res
num_cities = len(res.cities)
for i in range(num_cities):
try:
res = daemon.cc_get_city(i)
state['city_' + str(i)] = res
res = daemon.cc_get_special_events(i)
state['city_' + str(i) + '_special_events'] = res
except:
pass
try:
res = daemon.get_shares(i)
state['city_' + str(i) + '_shares'] = res
except:
pass
res = daemon.cc_get_accounts()
state['accounts'] = res.accounts
accounts = [x.id for x in res.accounts]
for i in accounts:
res = daemon.cc_get_account(i)
state['account_' + str(i)] = res
res = daemon.cc_get_flags()
state['flags'] = res.flags if 'flags' in res else None
flags = [x.id for x in res.flags] if 'flags' in res else []
for i in flags:
res = daemon.cc_get_flag(i)
state['flag_' + str(i)] = res
res = daemon.cc_get_last_update_events()
state['events'] = res
state['events']['top_hash'] = None
# do not include orders, those include txpool state so not conensus
res = daemon.cc_get_discoveries()
state['discoveries'] = res
res = daemon.cc_get_custom_items()
state['custom_items'] = res
try:
res = daemon.cc_get_badge([])
state['event_badges'] = res
except:
pass
res = daemon.cc_get_attributes()
state['attributes'] = res
res = daemon.cc_get_bonuses(accounts)
state['bonuses'] = res.bonuses
res = daemon.cc_get_badge_totals()
state['badge_totals'] = res.entries
ids = [x for x in range(NUM_PREDEFINED_ITEMS)]
res = daemon.cc_get_item_count(ids)
state['item_counts'] = res.count if 'count' in res else None
res = daemon.cc_get_used_nonces()
state['used_nonces'] = res.nonces if 'nonces' in res else None
return state
def remove_height(self, state):
new_state = state.copy()
del new_state['info.height']
return new_state
def remove_used_nonces(self, state):
new_state = state.copy()
del new_state['used_nonces']
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()
if mined:
assert self.remove_height(state0) != self.remove_height(state1), self.get_diff(state0, state1)
else:
assert self.remove_height(state0) == self.remove_height(state1), self.get_diff(state0, state1)
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()
assert self.remove_used_nonces(state1) == self.remove_used_nonces(state3), self.get_diff(self.remove_used_nonces(state1), self.remove_used_nonces(state3))
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))
# deposit on an existing account
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_deposit(amount = 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 * 1500}, {'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 + 16 - 1, FIRST_CITY_Y - 100 + 16 - 1, discounted = True))
# 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
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_building_settings(flag_id, ROLE_RESIDENTIAL1, 200, 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_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_name = res.name
flag_active = res.active
flag_budget = res.budget
assert 'tiles' in res
flag_tiles = res.tiles
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_demolish(flag_id, width = flag_width, height = flag_height, role = flag_role, economic_power = flag_economic_power, repair = flag_repair, construction_height = flag_construction_height, name = flag_name, active = flag_active, 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_[-1].id
assert res.items_[-1].amount == 8
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_dividend(custom_item_id, 0, 100))
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_dividend(custom_item_id, ITEM_STONE, 16))
# new event badge
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_event_badge(0, "Test badge for testing revert", "Event badge desc", [{"account_id": 4, "from_level": 0, "to_level": 2}]))
# assign event badge
from_timestamp = 0
res = daemon.cc_get_account(4)
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": 4, "from_level": 2, "to_level": 3, "from_timestamp": from_timestamp}]))
# destroy items
res = daemon.cc_get_custom_items()
item_id = res.items_[-1].id
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[3].cc_hunt(target = 0, city = 0, population = moose), redo = False)
bears = res.bears
self.test_revert_cmd(daemon, lambda self: self.wallet[3].cc_hunt(target = 1, city = 0, population = bears), 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]
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))
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
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
expected_balances[2] -= NEW_ITEM_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items, 100)
res = self.wallet[2].cc_new_item(30, "Test item 2", False, False, 0, "primary desc", "secondary desc")
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
expected_balances[2] -= NEW_ITEM_FEE
expected_item_balances[2] = self.add_item(expected_item_balances[2], ITEM_FIRST_USER + existing_items + 1, 30)
# 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)
# money dividend
res = self.wallet[2].cc_dividend(ITEM_FIRST_USER + existing_items + 1, 0, 100 * 30)
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(ITEM_FIRST_USER + existing_items + 1, ITEM_LABOUR, 5 * 30 + 29)
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
print('Testing item destruction')
# testing destruction
res = self.wallet[2].cc_destroy_items(ITEM_FIRST_USER + existing_items + 1, 3)
expected_balances[2] -= res.fee
res = self.wallet[2].cc_destroy_items(ITEM_WOOD, 2)
expected_balances[2] -= res.fee
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 2)
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
# 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 - 3]
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
# 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
assert_exception(lambda self: self.wallet[2].cc_new_item(30, "Group", True, False, 0, "primary desc", "secondary desc"))
# group with group to a non custom item
assert_exception(lambda self: 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
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
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(range(ITEM_FIRST_USER + existing_items, ITEM_FIRST_USER + existing_items + 4 + 1))
assert res.counts == [100, 30 - 3, 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
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
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
subgroup_member_2_id = e.id
# wrong owner
assert_exception(lambda self: self.wallet[1].cc_new_item(1, "Invalid", False, False, group_id, "primary desc", "secondary desc"))
# groups is not a group
assert_exception(lambda self: 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(range(ITEM_FIRST_USER + existing_items, ITEM_FIRST_USER + existing_items + 7 + 1))
assert res.counts == [100, 30 - 3, 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
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
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(range(ITEM_FIRST_USER + existing_items, ITEM_FIRST_USER + existing_items + 7 + 3))
assert res.counts == [100, 30 - 3, 18, 1, 1, 12, 4, 8, 4, 4]
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(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(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(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(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(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(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(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(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(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(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)
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()
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')
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()
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
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_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)
population = res.moose
starting_moose = res.moose
starting_bears = res.bears
# bad population
ok = False
try: self.wallet[2].cc_hunt(target = 0, city = 0, population = population + 1)
except: ok = True
assert ok
# bad city
ok = False
try: self.wallet[2].cc_hunt(target = 0, city = 9, population = population)
except: ok = True
assert ok
# bad target
ok = False
try: self.wallet[2].cc_hunt(target = 2, city = 0, population = population)
except: ok = True
assert ok
self.wallet[2].cc_hunt(target = 0, city = 0, population = population)
# only one at a time
ok = False
try: self.wallet[2].cc_hunt(target = 1, city = 0, population = population)
except: ok = True
assert ok
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = daemon.cc_get_account(account_id)
assert 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)
population = res.bears
self.wallet[2].cc_hunt(target = 1, city = 0, population = population)
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 = 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
new_bears = res.bears
assert new_bears == starting_bears - bears_killed
# 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)
assert res.moose > new_moose
assert res.bears > new_bears
# with lower population
res = daemon.cc_get_city(0)
old_moose = res.moose
self.wallet[2].cc_hunt(target = 0, city = 0, population = 100)
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 = 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
def test_chat(self):
daemon = self.daemon
print("Testing chat")
# bad color
ok = False
try: self.wallet[2].cc_chat("foo", color = 200)
except: ok = True
assert ok
res = self.wallet[2].cc_chat("foo", color = 2)
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
res = daemon.cc_get_flag(1)
assert not res.ignore
res = self.wallet[3].cc_ignore(type = IGNORE_FLAG, id = [1], ignore = True)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_flag(1)
assert res.ignore
res = self.wallet[3].cc_ignore(type = IGNORE_FLAG, id = [1], ignore = False)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
res = daemon.cc_get_flag(1)
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")
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
assert_exception(lambda self: self.wallet[2].cc_mint(COIN_ITEM_SETTLEMENT, 1))
# or smelt any either
assert_exception(lambda self: self.wallet[2].cc_smelt(COIN_ITEM_SETTLEMENT, 1))
# can't mint the group either
assert_exception(lambda self: 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]
assert test_coin.group == COINS_ITEM_GROUP
test_non_coin = res.items_[-1]
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)
assert_exception(lambda self: 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]
# 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
assert_exception(lambda self: self.wallet[2].cc_smelt(test_coin.id, 2))
# can't smelt the group either
assert_exception(lambda self: self.wallet[2].cc_smelt(test_non_coin.id, 1))
# wallet 1 can't smelt anything
assert_exception(lambda self: 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 three days
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', COIN_MINTING_WINDOW)
# can't mint anymore
assert_exception(lambda self: 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)
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(FIRST_CITY_X - 350, FIRST_CITY_Y - 350, FIRST_CITY_X - 350 + 60 - 1, FIRST_CITY_Y - 350 + 60 - 1)
res = self.wallet[2].cc_buy_land(FIRST_CITY_X - 350, FIRST_CITY_Y - 350, FIRST_CITY_X - 350 + 60 - 1, FIRST_CITY_Y - 350 + 60 - 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': 1800}, {'type': ITEM_WOOD, 'amount': 1}, {'type': ITEM_LABOUR, 'amount': 1}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
res = self.wallet[2].cc_building_settings(flag_id, ROLE_AGRICULTURAL, 100, 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
res = daemon.cc_get_flag(flag_id)
if res.repair < 500000:
self.wallet[1].cc_repair([flag_id, 1000000 - res.repair])
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_NONE))
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES))
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_GRAIN))
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, 255))
assert_exception(lambda self: self.wallet[2].cc_harvest(flag_id, CROP_NONE, 0, 0))
# 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[1].cc_repair([flag_id, 1000000 - res.repair])
# now we can sow
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_NONE))
assert_exception(lambda self: 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
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES))
assert_exception(lambda self: 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
assert_exception(lambda self: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height, vegetables_nutrients))
# 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)
if res.repair < 500000:
self.wallet[1].cc_repair([flag_id, 1000000 - res.repair])
# still cannot sow
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES))
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_GRAIN))
# but we can harvest now
assert_exception(lambda self: self.wallet[2].cc_harvest(flag_id - 1, CROP_VEGETABLES, sow_height, vegetables_nutrients))
assert_exception(lambda self: self.wallet[2].cc_harvest(flag_id, CROP_GRAIN, sow_height, vegetables_nutrients))
assert_exception(lambda self: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height - 1, vegetables_nutrients))
assert_exception(lambda self: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height + 1, vegetables_nutrients))
assert_exception(lambda self: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height, vegetables_nutrients - 1))
res = self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height, vegetables_nutrients)
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1)
# not twice
assert_exception(lambda self: self.wallet[2].cc_harvest(flag_id, CROP_VEGETABLES, sow_height, vegetables_nutrients))
# and we can't sow right afterwards either
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_VEGETABLES))
assert_exception(lambda self: self.wallet[2].cc_sow(flag_id, CROP_GRAIN))
# 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[1].cc_repair([flag_id, 1000000 - res.repair])
# 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
def test_cities(self):
daemon = self.daemon
print("Testing cities")
# get a lot more money
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 60)
for i in range(20):
self.generate_blocks('TF1MM5mG6EQkz899pz3uFDR6D2EUowvZWw75hcE6TETrodxHXSKFK6u3SRtEQzJ6epc5HD85dEgYF7xhgBtoFjMHKNFAEuGg1Lk', 100)
self.wallet[1].refresh()
res = self.wallet[1].cc_deposit(amount = 250000000000)
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': 1500000}, {'type': ITEM_WOOD, 'amount': 15000000}, {'type': ITEM_LABOUR, 'amount': 50000000}])
self.generate_blocks('TF1MM8HqWBathu8hS5mwMNHm1da3cZCzg2rkqWLKCxpUarKtPszP3MjiocrJeLvph4AghgYu1AXonCmckfEuyE8Q2FFm8jNdiz3', 1)
farms = []
buildings = []
for i in range(25):
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_RESIDENTIAL1][i % 5]
if role == ROLE_AGRICULTURAL:
farms.append(last_flag)
buildings.append(last_flag)
self.wallet[1].cc_building_settings(last_flag, role, 100)
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 True:
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, 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
self.wallet[1].cc_harvest(flag, CROP_VEGETABLES, sow_height, vegetables_nutrients)
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
self.wallet[1].cc_harvest(flag, CROP_GRAIN, sow_height, grain_nutrients)
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
# 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', blocks)
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 == 9
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)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
res = daemon.cc_get_account(account_id)
flag = res.flags[-1]
self.wallet[1].cc_building_settings(flag, ROLE_AGRICULTURAL, 100)
self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', blocks)
def test_discoveries(self):
daemon = self.daemon
print("Testing discoveries")
res = daemon.cc_get_discoveries()
assert (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 = MIN_RESEARCH_AMOUNT * 100
res = self.wallet[2].cc_research(discovery, amount)
assert len(res.tx_hash) == 64
budget += amount
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1, True)
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
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
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 = MIN_RESEARCH_AMOUNT * 1000
res = self.wallet[2].cc_research(discovery, amount)
assert len(res.tx_hash) == 64
budget += amount
res = self.generate_blocks('TF1MMBg4zx18SnZC6oswCRB7DzdVeUKce5NdCMSWUHNY4wNvNhzmFj4WqZY2bFj8R1REAWR3qAH5zD7sjXyHz3tVayzHSswqymx', 1, True)
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 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)
state0 = self.get_state()
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(10)
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)
# mine
print('Mining on second daemon')
n_mined = 0
while True:
n_mined += 100
res = second_daemon.generateblocks('TF1MMEY5v2dN49XKNCKCdBqmTMM6GdZZA5UBbDgTewaUd3c2jDazN5yKrG1BBHX3UyPqKD9hrh3DpPTDmWiCmsuRpePT1MTaPxm', 1)
res = second_daemon.get_info()
if res.cumulative_difficulty > cumulative_difficulty:
break
print('Mined %u blocks for a reorg' % n_mined)
# reconnect daemons 1 and 2
daemon.out_peers(8)
daemon.in_peers(8)
second_daemon.out_peers(8)
second_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)
# disconnect daemons again
daemon.out_peers(0)
daemon.in_peers(0)
second_daemon.out_peers(0)
second_daemon.in_peers(0)
# mine on the third daemon, which still has the original chain
res = third_daemon.generateblocks('TF1MMEY5v2dN49XKNCKCdBqmTMM6GdZZA5UBbDgTewaUd3c2jDazN5yKrG1BBHX3UyPqKD9hrh3DpPTDmWiCmsuRpePT1MTaPxm', n_mined + 1)
res = third_daemon.get_info()
cumulative_difficulty = res.cumulative_difficulty
# 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 = self.get_state()
assert self.remove_height(state0) == self.remove_height(state1), self.get_diff(state0, state1)
class Guard:
def __enter__(self):
for i in [2, 3, 4]:
Daemon(idx = i).out_peers(0)
Daemon(idx = i).in_peers(0)
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')
if __name__ == '__main__':
with Guard() as guard:
CCTest().run_test()