def test_contract_create(): test_settings = settings["test_contract_create"] (node, (root_dir, external_api, top)) = setup_node_with_tokens(test_settings, "node") internal_api = common.internal_api(node) private_key = keys.new_private() public_key = keys.public_key(private_key) alice_address = keys.address(public_key) test_settings["alice"]["pubkey"] = alice_address send_tokens_to_user("alice", test_settings, external_api, internal_api) encoded_tx, contract_address = get_unsigned_contract_create( alice_address, test_settings["create_contract"], external_api) print("Unsigned encoded transaction: " + encoded_tx) print("Contract address: " + contract_address) unsigned_tx = common.base58_decode(encoded_tx) tx = common.decode_unsigned_tx(unsigned_tx) print("Unsigned decoded transaction: " + str(tx)) # make sure same tx assert_equals(tx['type'], 'contract_create_tx') assert_equals(tx['owner'], common.base58_decode(test_settings["alice"]["pubkey"])) assert_equals(tx['vm_version'], test_settings["create_contract"]["vm_version"]) assert_equals(tx['deposit'], test_settings["create_contract"]["deposit"]) assert_equals(tx['amount'], test_settings["create_contract"]["amount"]) assert_equals(tx['gas'], test_settings["create_contract"]["gas"]) assert_equals(tx['gas_price'], test_settings["create_contract"]["gas_price"]) assert_equals(tx['fee'], test_settings["create_contract"]["fee"]) signed = keys.sign_verify_encode_tx(unsigned_tx, private_key, public_key) print("Signed transaction " + signed) alice_balance0 = common.get_account_balance(external_api, internal_api, pub_key=alice_address).balance tx_object = Tx(tx=signed) external_api.post_tx(tx_object) top = external_api.get_top_block() common.wait_until_height(external_api, top.height + 3) alice_balance = common.get_account_balance(external_api, internal_api, pub_key=alice_address).balance assert_equals( alice_balance0, alice_balance + test_settings["create_contract"]["fee"] + test_settings["create_contract"]["gas_used"] + test_settings["create_contract"]["deposit"] + test_settings["create_contract"]["amount"]) print("Fee was consumed, transaction is part of the chain") cleanup(node, root_dir)
def test_persistence(): """ Test persistence: Bob's downloaded blockchain should persist between restarts. He should only download updates to his blockchain when his node starts. """ test_settings = settings["test_persistence"] # prepare a dir to hold the config and DB files root_dir = tempfile.mkdtemp() p_m_conf = """\ --- chain: persist: true db_path: \"""" + root_dir + """\" mining: autostart: true expected_mine_rate: 100 cuckoo: miner: executable: mean16s-generic extra_args: "-t 5" node_bits: 16 """ p_conf = """\ --- chain: persist: true db_path: \"""" + root_dir + """\" """ persistence_mining_user_config = common.install_user_config(root_dir, "p_m_epoch.yaml", p_m_conf) only_persistence_user_config = common.install_user_config(root_dir, "p_epoch.yaml", p_conf) bob_node = test_settings["nodes"]["bob"] common.start_node(bob_node, persistence_mining_user_config) bob_api = common.external_api(bob_node) # Insert some blocks in Bob's chain blocks_to_mine = test_settings["blocks_to_mine"] common.wait_until_height(bob_api, blocks_to_mine) bob_top = bob_api.get_top() assert_equals(bob_top.height >= blocks_to_mine, True) # Now Bob has at least blocks_to_mine blocks common.stop_node(bob_node) common.start_node(bob_node, only_persistence_user_config) bob_new_top = bob_api.get_top() if(bob_new_top.height > bob_top.height): # Bob's node had mined another block(s) before being stopped bob_block = bob_api.get_block_by_hash(bob_top.hash) # this block is presnet assert_equals(bob_block.height, bob_top.height) else: assert_equals(bob_new_top.height, bob_top.height) assert_equals(bob_top.hash, bob_new_top.hash) common.stop_node(bob_node) shutil.rmtree(root_dir)
def test_spend(): test_settings = settings["test_spend"] (root_dir, node, external_api, top) = setup_node_with_tokens(test_settings, "node") internal_api = common.internal_api(node) private_key = keys.new_private() public_key = keys.public_key(private_key) alice_address = keys.address(public_key) bob_address = test_settings["spend_tx"]["recipient"] test_settings["alice"]["pubkey"] = alice_address send_tokens_to_user("alice", test_settings, internal_api, external_api) spend_data_obj = SpendTx(sender=alice_address, recipient_pubkey=bob_address, amount=test_settings["spend_tx"]["amount"], fee=test_settings["spend_tx"]["fee"]) unsigned_spend_obj = external_api.post_spend(spend_data_obj) unsigned_spend_enc = unsigned_spend_obj.tx tx_hash = unsigned_spend_obj.tx_hash unsigned_tx = common.base58_decode(unsigned_spend_enc) unpacked_tx = common.unpack_tx(unsigned_tx) print("Tx " + str(common.parse_tx(unpacked_tx))) alice_balance0 = common.get_account_balance(internal_api, pub_key=alice_address).balance bob_balance0 = common.get_account_balance(internal_api, pub_key=bob_address).balance signed = keys.sign_verify_encode_tx(unsigned_tx, unpacked_tx, private_key, public_key) print("Signed transaction " + signed) print("Tx hash " + tx_hash) tx_object = Tx(tx=signed) external_api.post_tx(tx_object) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) alice_balance = common.get_account_balance(internal_api, pub_key=alice_address).balance bob_balance = common.get_account_balance(internal_api, pub_key=bob_address).balance tx = external_api.get_tx( tx_hash=tx_hash) # it is there - either in mempool or in a block print("Alice balance now is " + str(alice_balance)) print("Bob balance now is " + str(bob_balance)) print("Balances are updated, transaction has been executed") assert_equals( alice_balance0, alice_balance + test_settings["spend_tx"]["fee"] + test_settings["spend_tx"]["amount"]) assert_equals(bob_balance0, bob_balance - test_settings["spend_tx"]["amount"]) cleanup(node, root_dir)
def miner_send_tokens(address, amount, internal_api, external_api): spend_tx_obj = SpendTx(recipient_pubkey=address, amount=amount, fee=1) # populate account internal_api.post_spend_tx(spend_tx_obj) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3)
def test_persistence(): """ Test persistence: Bob's downloaded blockchain should persist between restarts. He should only download updates to his blockchain when his node starts. """ test_settings = settings["test_persistence"] # prepare a dir to hold the config and DB files root_dir = tempfile.mkdtemp() persistance_mining_sys_config = os.path.join(root_dir, "p_m_sys.config") only_persistance_sys_config = os.path.join(root_dir, "p_sys.config") f = open(persistance_mining_sys_config, "w") # this should be moved to an YAML once we have YAML node configuration f.write('[{aecore, [{db_path, "' + root_dir + '"},' + \ ' {persist, true},' + \ ' {autostart, true},' + \ ' {expected_mine_rate, 100},' + \ ' {aec_pow_cuckoo, {"mean16s-generic", "-t 5", 16}}' + \ ']}].') f.close() f = open(only_persistance_sys_config, "w") # this should be moved to an YAML once we have YAML node configuration f.write('[{aecore, [{db_path, "' + root_dir + '"},' + \ ' {persist, true}' + \ ']}].') f.close() bob_node = test_settings["nodes"]["bob"] common.start_node(bob_node, persistance_mining_sys_config) bob_api = common.external_api(bob_node) # Insert some blocks in Bob's chain blocks_to_mine = test_settings["blocks_to_mine"] common.wait_until_height(bob_api, blocks_to_mine) bob_top = bob_api.get_top() assert_equals(bob_top.height >= blocks_to_mine, True) # Now Bob has at least blocks_to_mine blocks common.stop_node(bob_node) common.start_node(bob_node, only_persistance_sys_config) bob_new_top = bob_api.get_top() if (bob_new_top.height > bob_top.height): # Bob's node had mined another block(s) before being stopped bob_block = bob_api.get_block_by_hash( bob_top.hash) # this block is presnet assert_equals(bob_block.height, bob_top.height) else: assert_equals(bob_new_top.height, bob_top.height) assert_equals(bob_top.hash, bob_new_top.hash) common.stop_node(bob_node) shutil.rmtree(root_dir)
def test_syncing(): """ Test node syncing: Alice should be able to connect to peers on startup and download the blockchain up to the current height. """ test_settings = settings["test_syncing"] root_dir = tempfile.mkdtemp() mining_user_config = make_fast_mining_user_config(root_dir, "mining_epoch.yaml") no_mining_user_config = make_no_mining_user_config(root_dir, "no_mining_epoch.yaml") # start Bob's node bob_node = test_settings["nodes"]["bob"] common.start_node(bob_node, mining_user_config) bob_api = common.external_api(bob_node) # Insert some blocks in Bob's chain blocks_to_mine = test_settings["blocks_to_mine"] print("Bob is mining") common.wait_until_height(bob_api, blocks_to_mine) bob_top = bob_api.get_top_block() assert_equals(bob_top.height >= blocks_to_mine, True) # Now Bob has at least blocks_to_mine blocks print("Bob has mined " + str(bob_top.height) + " blocks") # start Alice's node and let it connect with Bob's # note: Alice doesn't mine blocks alice_node = test_settings["nodes"]["alice"] common.start_node(alice_node, no_mining_user_config) print("Alice is not mining") alice_api = common.external_api(alice_node) common.wait_until_height(alice_api, blocks_to_mine) alice_top = alice_api.get_top_block() assert_equals(alice_top.height >= blocks_to_mine, True) if alice_top.height > bob_top.height: # bob had mined more blocks bob_block = bob_api.get_block_by_hash( alice_top.hash) # this block is presnet assert_equals(bob_block.height, alice_top.height) else: assert_equals(alice_top.height, bob_top.height) assert_equals(alice_top.hash, bob_top.hash) print("Alice's had synced with Bob and now Alice's top has height " + str(alice_top.height)) # stop both nodes common.stop_node(bob_node) common.stop_node(alice_node) shutil.rmtree(root_dir)
def miner_send_tokens(address, amount, internal_api, external_api): spend_tx_obj = SpendTx( recipient_id=address, amount=amount, fee=1, ttl=100, payload="sending tokens") # populate account internal_api.post_spend_tx(spend_tx_obj) top = external_api.get_top_block() common.wait_until_height(external_api, top.height + 3)
def send_tokens_to_user(user, test_settings, internal_api, external_api): spend_tx_obj = SpendTx( recipient_pubkey=test_settings[user]["pubkey"], amount=test_settings[user]["amount"], fee=test_settings[user]["fee"]) # populate Alice's account internal_api.post_spend_tx(spend_tx_obj) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) balance_obj = common.get_account_balance(internal_api, pub_key=test_settings[user]["pubkey"]) print(user.capitalize() + "'s balance is now " + str(balance_obj.balance))
def send_tokens_to_name(name, tokens, sender_address, private_key, external_api): name_entry = external_api.get_name(name) resolved_address = name_entry.pointers[0].id print("Name " + name + " resolved to address " + resolved_address) unsigned_spend = common.base58_decode(\ external_api.post_spend(\ SpendTx(sender_id=sender_address, recipient_id=resolved_address, amount=tokens, fee=1,\ ttl=100, payload="foo")).tx) signed_spend = keys.sign_encode_tx(unsigned_spend, private_key) external_api.post_tx(Tx(tx=signed_spend)) top = external_api.get_top_block() common.wait_until_height(external_api, top.height + 3)
def send_tokens_to_name(name, tokens, sender_address, private_key, external_api): name_entry = external_api.get_name(name) resolved_address = json.loads(name_entry.pointers)['account_pubkey'] print("Name " + name + " resolved to address " + resolved_address) unsigned_spend = common.base58_decode(\ external_api.post_spend(\ SpendTx(sender=sender_address, recipient_pubkey=resolved_address, amount=tokens, fee=1)).tx) signed_spend = keys.sign_encode_tx(unsigned_spend, private_key) external_api.post_tx(Tx(tx=signed_spend)) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3)
def test_contract_create(): test_settings = settings["test_contract_create"] (root_dir, node, external_api, top) = setup_node_with_tokens(test_settings, "node") internal_api = common.internal_api(node) send_tokens_to_user("alice", test_settings, internal_api, external_api) encoded_tx = get_unsigned_contract_create(test_settings["alice"]["pubkey"], test_settings["create_contract"], external_api) print("Unsigned encoded transaction: " + encoded_tx) unsigned_tx = common.base58_decode(encoded_tx) unpacked_tx = common.unpack_tx(unsigned_tx) tx = common.parse_tx(unpacked_tx) print("Unsigned decoded transaction: " + str(tx)) # make sure same tx assert_equals(tx['type'], 'contract_create') assert_equals(tx['owner'], common.base58_decode(test_settings["alice"]["pubkey"])) assert_equals(tx['vm_version'], test_settings["create_contract"]["vm_version"]) assert_equals(tx['deposit'], test_settings["create_contract"]["deposit"]) assert_equals(tx['amount'], test_settings["create_contract"]["amount"]) assert_equals(tx['gas'], test_settings["create_contract"]["gas"]) assert_equals(tx['gas_price'], test_settings["create_contract"]["gas_price"]) assert_equals(tx['fee'], test_settings["create_contract"]["fee"]) code = bytearray.fromhex(test_settings["create_contract"]["code"][2:]) # without 0x assert_equals(tx['code'], code) call_data = bytearray.fromhex(test_settings["create_contract"]["call_data"][2:]) # without 0x assert_equals(tx['call_data'], call_data) signature = bytearray(list(map(int, test_settings["create_contract"]["signature"].split(",")))) signed = common.encode_signed_tx(unpacked_tx, [signature]) print("Signed transaction " + signed) alice_balance0 = common.get_account_balance(internal_api, pub_key=test_settings["alice"]["pubkey"]).balance tx_object = Tx(tx=signed) external_api.post_tx(tx_object) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) alice_balance = common.get_account_balance(internal_api, pub_key=test_settings["alice"]["pubkey"]).balance assert_equals(alice_balance0, alice_balance + test_settings["create_contract"]["fee"]) cleanup(node, root_dir)
def test_contract_on_chain_call_off_chain(): test_settings = settings["test_contract_call"] create_settings = settings["test_contract_create"] beneficiary = common.setup_beneficiary() (node, (root_dir, external_api, top)) = setup_node_with_tokens(test_settings, beneficiary, "node") internal_api = common.internal_api(node) private_key = keys.new_private() public_key = keys.public_key(private_key) alice_address = keys.address(public_key) test_settings["alice"]["pubkey"] = alice_address send_tokens_to_user(beneficiary, "alice", test_settings, external_api, internal_api) ## create contract encoded_tx, encoded_contract_id = get_unsigned_contract_create( alice_address, create_settings["create_contract"], external_api, internal_api) unsigned_tx = common.base58_decode(encoded_tx) signed = keys.sign_verify_encode_tx(unsigned_tx, private_key, public_key) alice_balance0 = common.get_account_balance(external_api, internal_api, pub_key=alice_address).balance tx_object = Tx(tx=signed) external_api.post_transaction(tx_object) top = external_api.get_top_block() common.wait_until_height(external_api, top.height + 3) call_contract = test_settings["contract_call"] call_input = ContractCallInput("sophia-address", encoded_contract_id,\ call_contract["data"]["function"],\ call_contract["data"]["argument"]) result = internal_api.call_contract(call_input) assert_equals( '0x000000000000000000000000000000000000000000000000000000000000002a', result.out) cleanup(node, root_dir)
def setup_node_with_tokens(test_settings, node_name): # prepare a dir to hold the configs and the keys root_dir = tempfile.mkdtemp() # setup the dir with Alice's node mining node = test_settings["nodes"][node_name] sys_config = make_mining_config(root_dir, "sys.config") common.start_node(node, sys_config) api = common.external_api(node) # populate the chain so Alice had mined some blocks and has tokens # to spend blocks_to_mine = test_settings["blocks_to_mine"] common.wait_until_height(api, blocks_to_mine) top = api.get_top() assert_equals(top.height >= blocks_to_mine, True) # Now the node has at least blocks_to_mine blocks mined by Alice return (root_dir, node, api, top)
def register_name(name, address, external_api, private_key): salt = 42 commitment = external_api.get_commitment_hash(name, salt).commitment # preclaim unsigned_preclaim = common.base58_decode(\ external_api.post_name_preclaim(\ NamePreclaimTx(commitment=commitment, fee=1, account=address)).tx) signed_preclaim = keys.sign_encode_tx(unsigned_preclaim, private_key) external_api.post_tx(Tx(tx=signed_preclaim)) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) # claim encoded_name = common.encode_name(name) unsigned_claim = common.base58_decode(\ external_api.post_name_claim(\ NameClaimTx(name=encoded_name, name_salt=salt, fee=1, account=address)).tx) signed_claim = keys.sign_encode_tx(unsigned_claim, private_key) external_api.post_tx(Tx(tx=signed_claim)) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) name_entry0 = external_api.get_name(name) print("Name " + name_entry0.name + " has been claimed and has hash " + name_entry0.name_hash) # set pointers pointers_str = json.dumps({'account_pubkey': address}) unsigned_update = common.base58_decode(\ external_api.post_name_update(\ NameUpdateTx(name_hash=name_entry0.name_hash, name_ttl=600000, ttl=50,\ pointers=pointers_str, fee=1, account=address)).tx) signed_update = keys.sign_encode_tx(unsigned_update, private_key) external_api.post_tx(Tx(tx=signed_update)) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) name_entry = external_api.get_name(name) received_pointers = json.loads(name_entry.pointers) assert_equals(address, received_pointers['account_pubkey'])
def register_name(name, address, external_api, private_key): salt = 42 commitment_id = external_api.get_commitment_hash(name, salt).commitment_id # preclaim unsigned_preclaim = common.base58_decode(\ external_api.post_name_preclaim(\ NamePreclaimTx(commitment_id=commitment_id, fee=1, ttl=100, account_id=address)).tx) signed_preclaim = keys.sign_encode_tx(unsigned_preclaim, private_key) external_api.post_tx(Tx(tx=signed_preclaim)) top = external_api.get_top_block() common.wait_until_height(external_api, top.height + 3) # claim encoded_name = common.encode_name(name) unsigned_claim = common.base58_decode(\ external_api.post_name_claim(\ NameClaimTx(name=encoded_name, name_salt=salt, fee=1, ttl=100, account_id=address)).tx) signed_claim = keys.sign_encode_tx(unsigned_claim, private_key) external_api.post_tx(Tx(tx=signed_claim)) top = external_api.get_top_block() common.wait_until_height(external_api, top.height + 3) name_entry0 = external_api.get_name(name) # set pointers pointers = [ NamePointer(key='account_pubkey', id=address) ] unsigned_update = common.base58_decode(\ external_api.post_name_update(\ NameUpdateTx(name_id=name_entry0.id, name_ttl=6000, client_ttl=50,\ pointers=pointers, fee=1, ttl=100, account_id=address)).tx) signed_update = keys.sign_encode_tx(unsigned_update, private_key) external_api.post_tx(Tx(tx=signed_update)) top = external_api.get_top_block() common.wait_until_height(external_api, top.height + 3) name_entry = external_api.get_name(name) received_pointers = name_entry.pointers[0] assert_equals('account_pubkey', received_pointers.key) assert_equals(address, received_pointers.id)
def test_successful(): # Alice should be able to create a spend transaction to send tokens to # Bob. In a controlled environment, Alice should see her transaction # appear in the next block or two that are added to the blockchain. # # Once that happens, Alice should see her account debited and Bob's # account credited with the same number of tokens. # # The debit/credit should not happen until the transaction is confirmed, # e.g. included in at least one block in the chain. test_settings = settings["test_successful"] coinbase_reward = common.coinbase_reward() (root_dir, alice_node, alice_api, alice_top) = setup_node_with_tokens(test_settings, "alice") alice_internal_api = common.internal_api(alice_node) spend_tx_amt = test_settings["spend_tx"]["amount"] spend_tx_fee = test_settings["spend_tx"]["fee"] alice_balance = common.get_account_balance(alice_internal_api) alice_has_enough_tokens = alice_balance.balance >= spend_tx_amt + spend_tx_fee assert_equals(alice_has_enough_tokens, True) print("Alice initial balance is " + str(alice_balance.balance)) bob_balance0 = common.get_account_balance( alice_internal_api, pub_key=test_settings["spend_tx"]["bob_pubkey"]) # Alice sends some tokens to Bob alice_internal_api = common.internal_api(alice_node) spend_tx_obj = SpendTx( recipient_pubkey=test_settings["spend_tx"]["bob_pubkey"], amount=spend_tx_amt, fee=spend_tx_fee) print("Alice's spend_tx is " + str(spend_tx_obj)) alice_internal_api.post_spend_tx(spend_tx_obj) print("Transaction sent") # ensure Alice balance had not changed alice_balance1 = common.get_account_balance(alice_internal_api) assert_equals(alice_balance.balance, alice_balance1.balance) # ensure Bob balance had not changed bob_balance1 = common.get_account_balance( alice_internal_api, pub_key=test_settings["spend_tx"]["bob_pubkey"]) assert_equals(bob_balance1.balance, bob_balance0.balance) # wait for a block to be mined print("Waiting for a next block to be mined") common.wait_until_height(alice_api, alice_top.height + 1) alice_new_top = alice_api.get_top() alice_new_balance = common.get_account_balance(alice_internal_api) blocks_mined = alice_new_top.height - alice_top.height # Since Alice had mined the block she is receiving the fee and the # coinbase_reward # Alice should have # tokens = old_balance - spent_amt - spend_tx + spent_fee + coinbase_reward expected_balance = alice_balance.balance - spend_tx_amt + coinbase_reward * blocks_mined assert_equals(alice_new_balance.balance, expected_balance) bob_balance = common.get_account_balance( alice_internal_api, pub_key=test_settings["spend_tx"]["bob_pubkey"]) print("Coinbase reward is " + str(coinbase_reward) + ", had mined " + str(blocks_mined) + " blocks") print("Alice's balance (with a coinbase reward) is now " + str(alice_new_balance.balance)) print("Bob's balance is now " + str(bob_balance.balance)) assert_equals(bob_balance.balance, spend_tx_amt) # stop node common.stop_node(alice_node) shutil.rmtree(root_dir)
def test_node_discovery(): """ Test node discovery Assuming Carol's node only knows about Bob upon startup and that Bob's node knows Alice, Carol's node should be able to discover Alice and sync with her node. """ test_settings = settings["test_node_discovery"] alice_node = test_settings["nodes"]["alice"] bob_node = test_settings["nodes"]["bob"] carol_node = test_settings["nodes"]["carol"] alice_peer_url = node_peer_url(alice_node) bob_peer_url = node_peer_url(bob_node) carol_peer_url = node_peer_url(carol_node) # prepare a dir to hold the configs root_dir = tempfile.mkdtemp() # Alice's config: no peers alice_sys_config = make_peers_config(root_dir, "alice.config", alice_peer_url, [], mining=True) print("\nAlice has address " + alice_peer_url + " and no peers") # Bob's config: only peer is Alice bob_sys_config = make_peers_config(root_dir, "bob.config", bob_peer_url, [alice_peer_url], mining=False) print("Bob has address " + bob_peer_url + " and peers [" + alice_peer_url + "]") # Carol's config: only peer is Bob carol_sys_config = make_peers_config(root_dir, "carol.config", carol_peer_url, [bob_peer_url], mining=False) print("Carol has address " + carol_peer_url + " and peers [" + bob_peer_url + "]") # start Alice's node common.start_node(alice_node, alice_sys_config) alice_api = common.external_api(alice_node) # Insert some blocks in Alice's chain blocks_to_mine = test_settings["blocks_to_mine"] common.wait_until_height(alice_api, blocks_to_mine) alice_top = alice_api.get_top() assert_true(alice_top.height >= blocks_to_mine) # Now Alice has at least blocks_to_mine blocks # start the other nodes common.start_node(bob_node, bob_sys_config) common.start_node(carol_node, carol_sys_config) # Check that Carol syncs with Alice's chain carol_api = common.external_api(carol_node) common.wait_until_height(carol_api, alice_top.height) assert_equals( carol_api.get_block_by_hash(alice_top.hash).height, alice_top.height) # Check that Carol discovers Alice as a peer gen_hash = common.genesis_hash(carol_api) ping_obj = Ping( source="http://localhost:1234", genesis_hash=gen_hash, best_hash=gen_hash, difficulty=1, share=32, # expected peer list is small peers=[]) def carol_peers(): ps = [p.encode('utf-8') for p in carol_api.ping(ping_obj).peers] print("Carol now has peers " + str(ps)) return ps wait(lambda: common.is_among_peers(alice_peer_url, carol_peers()), timeout_seconds=120, sleep_seconds=0.25) # cleanup common.stop_node(alice_node) common.stop_node(bob_node) common.stop_node(carol_node) shutil.rmtree(root_dir)
def test_node_discovery(): """ Test node discovery Assuming Carol's node only knows about Bob upon startup and that Bob's node knows Alice, Carol's node should be able to discover Alice and sync with her node. """ test_settings = settings["test_node_discovery"] alice_node = test_settings["nodes"]["alice"] bob_node = test_settings["nodes"]["bob"] carol_node = test_settings["nodes"]["carol"] alice_peer_url = node_peer_url(alice_node) bob_peer_url = node_peer_url(bob_node) carol_peer_url = node_peer_url(carol_node) # prepare a dir to hold the configs root_dir = tempfile.mkdtemp() # Alice's config: no peers alice_sys_config = make_peers_config(root_dir, "alice.config", alice_peer_url, "node1", "3015", '{peers, []},', mining=True) print("\nAlice has address " + alice_peer_url + " and no peers") # Bob's config: only peer is Alice bob_peers = ' {peers, [<<"aenode://pp$HdcpgTX2C1aZ5sjGGysFEuup67K9XiFsWqSPJs4RahEcSyF7X@localhost:3015">>]}, ' bob_sys_config = make_peers_config(root_dir, "bob.config", bob_peer_url, "node2", "3025", bob_peers, mining=False) print("Bob has address " + bob_peer_url + " and peers [" + alice_peer_url + "]") # Carol's config: only peer is Bob carol_peers = ' {peers, [<<"aenode://pp$28uQUgsPcsy7TQwnRxhF8GMKU4ykFLKsgf4TwDwPMNaSCXwWV8@localhost:3025">>]}, ' carol_sys_config = make_peers_config(root_dir, "carol.config", carol_peer_url, "node3", "3035", carol_peers, mining=False) print("Carol has address " + carol_peer_url + " and peers [" + bob_peer_url + "]") # start Alice's node common.start_node(alice_node, alice_sys_config) alice_api = common.external_api(alice_node) # Insert some blocks in Alice's chain blocks_to_mine = test_settings["blocks_to_mine"] common.wait_until_height(alice_api, blocks_to_mine) alice_top = alice_api.get_top() assert_true(alice_top.height >= blocks_to_mine) # Now Alice has at least blocks_to_mine blocks # start the other nodes common.start_node(bob_node, bob_sys_config) common.start_node(carol_node, carol_sys_config) # Check that Carol syncs with Alice's chain carol_api = common.external_api(carol_node) common.wait_until_height(carol_api, alice_top.height) assert_equals( carol_api.get_block_by_hash(alice_top.hash).height, alice_top.height) # Check that Carol discovers Alice as a peer carol_int_api = common.internal_api(carol_node) def carol_peers(): peers = carol_int_api.get_peers().peers print("Peers: " + str(peers)) return peers wait( lambda: 'aenode://pp$HdcpgTX2C1aZ5sjGGysFEuup67K9XiFsWqSPJs4RahEcSyF7X@localhost:3015' in carol_peers(), timeout_seconds=20, sleep_seconds=1) # cleanup common.stop_node(alice_node) common.stop_node(bob_node) common.stop_node(carol_node) shutil.rmtree(root_dir)
def test_not_enough_tokens(): # Bob should not be able to send more tokens than he has # # Let's say Bob has 100 tokens. He should not be able to send more than # 100 tokens to Alice. # # If there's an incoming but unconfirmed deposit into Bob's account then Bob # should not be able to use the incoming tokens until the spend transaction # they are in is confirmed. test_settings = settings["test_not_enough_tokens"] coinbase_reward = common.coinbase_reward() (root_dir, bob_node, bob_api, bob_top) = setup_node_with_tokens(test_settings, "bob") bob_internal_api = common.internal_api(bob_node) def get_bob_balance(height): return common.get_account_balance_at_height(bob_api, bob_internal_api, height) def get_alice_balance(height): k = test_settings["spend_tx"]["alice_pubkey"] return common.get_account_balance_at_height(bob_api, bob_internal_api, height, pub_key=k) spend_tx_amt = test_settings["spend_tx"]["amount"] spend_tx_fee = test_settings["spend_tx"]["fee"] bob_balance = get_bob_balance(bob_top.height) bob_has_not_enough_tokens = bob_balance.balance < spend_tx_amt + spend_tx_fee assert_equals(bob_has_not_enough_tokens, True) print("Bob initial balance is " + str(bob_balance.balance) + " and he will try to spend " + str(spend_tx_amt + spend_tx_fee)) alice_balance0 = get_alice_balance(bob_top.height) # Bob tries to send some tokens to Alice spend_tx_obj = SpendTx( recipient_pubkey=test_settings["spend_tx"]["alice_pubkey"], amount=spend_tx_amt, fee=spend_tx_fee) print("Bob's spend_tx is " + str(spend_tx_obj)) bob_internal_api.post_spend_tx(spend_tx_obj) print("Transaction sent") bob_top_after_tx = bob_api.get_top() print("Waiting for a few blocks to be mined") checked_height = bob_top_after_tx.height + 3 common.wait_until_height(bob_api, checked_height) # ensure Alice balance had not changed alice_balance1 = get_alice_balance(checked_height) print("Alice's balance is now " + str(alice_balance1.balance)) assert_equals(alice_balance1.balance, alice_balance0.balance) # ensure Bob not debited # Since Bob had mined the block he is receiving the coinbase_reward blocks_mined = checked_height - bob_top.height print("Coinbase reward is " + str(coinbase_reward) + ", had mined " + str(blocks_mined) + " blocks") expected_balance = bob_balance.balance + coinbase_reward * blocks_mined bob_new_balance = get_bob_balance(checked_height) print("Bob's balance (with coinbase rewards) is now " + str(bob_new_balance.balance)) assert_equals(bob_new_balance.balance, expected_balance) # stop node common.stop_node(bob_node) shutil.rmtree(root_dir)
def test_node_discovery(): """ Test node discovery Assuming Carol's node only knows about Bob upon startup and that Bob's node knows Alice, Carol's node should be able to discover Alice and sync with her node. """ test_settings = settings["test_node_discovery"] alice_node = test_settings["nodes"]["alice"] bob_node = test_settings["nodes"]["bob"] carol_node = test_settings["nodes"]["carol"] alice_peer_url = node_peer_url(alice_node) bob_peer_url = node_peer_url(bob_node) carol_peer_url = node_peer_url(carol_node) # prepare a dir to hold the configs root_dir = tempfile.mkdtemp() # Alice's config: no peers alice_sys_config = make_peers_config(root_dir, "alice.config", alice_peer_url, [], mining=True) print("\nAlice has address " + alice_peer_url + " and no peers") # Bob's config: only peer is Alice bob_sys_config = make_peers_config(root_dir, "bob.config", bob_peer_url, [alice_peer_url], mining=False) print("Bob has address " + bob_peer_url + " and peers [" + alice_peer_url + "]") # Carol's config: only peer is Bob carol_sys_config = make_peers_config(root_dir, "carol.config", carol_peer_url, [bob_peer_url], mining=False) print("Carol has address " + carol_peer_url + " and peers [" + bob_peer_url + "]") # start Alice's node common.start_node(alice_node, alice_sys_config) alice_api = common.external_api(alice_node) # Insert some blocks in Alice's chain blocks_to_mine = test_settings["blocks_to_mine"] common.wait_until_height(alice_api, blocks_to_mine) alice_top = alice_api.get_top() assert_equals(alice_top.height >= blocks_to_mine, True) # Now Alice has at least blocks_to_mine blocks # start the other nodes common.start_node(bob_node, bob_sys_config) common.start_node(carol_node, carol_sys_config) time.sleep(1) # give some time for the data to propagate carol_api = common.external_api(carol_node) carol_top = carol_api.get_top() gen_hash = common.genesis_hash(carol_api) ping_obj = Ping(source="localhost", genesis_hash=gen_hash, best_hash=carol_top.hash, difficulty=1, share=32, peers=[]) ping = carol_api.ping(ping_obj) carol_peers = ping.peers synced = len(list(filter(lambda peer: peer == alice_peer_url, carol_peers))) == 1 print("Carol now has peers " + str(carol_peers)) assert_equals(synced, True) # for larger peer lists this might be too fragile assert_equals(carol_top.height >= blocks_to_mine, True) if carol_top.height > alice_top.height: # Alice had mined some more blocks alice_block = alice_api.get_block_by_hash( carol_top.hash) # this block is presnet assert_equals(alice_block.height, carol_top.height) else: assert_equals(alice_top.height, carol_top.height) # cleanup common.stop_node(alice_node) common.stop_node(bob_node) common.stop_node(carol_node) shutil.rmtree(root_dir)
def test_node_discovery_from_common_friend(): """ Test node discovery (from common friend peer) Assuming Carol's node only knows about Bob upon startup and that Alice's node knows Bob, Carol's node should be able to discover Alice and sync with her node. """ test_settings = settings["test_node_discovery_from_common_friend"] alice_node = test_settings["nodes"]["alice"] bob_node = test_settings["nodes"]["bob"] carol_node = test_settings["nodes"]["carol"] # prepare a dir to hold the configs root_dir = tempfile.mkdtemp() # Alice's config: only peer is Bob alice_peers = """\ peers: - "aenode://pp$28uQUgsPcsy7TQwnRxhF8GMKU4ykFLKsgf4TwDwPMNaSCXwWV8@localhost:3025" """ alice_user_config = make_peers_user_config(root_dir, "alice_epoch.yaml", "node1", "3015", alice_peers, "true") # Bob's config: no peers bob_peers = "peers: []" bob_user_config = make_peers_user_config(root_dir, "bob_epoch.yaml", "node2", "3025", bob_peers, "false") # Carol's config: only peer is Bob carol_peers = """\ peers: - "aenode://pp$28uQUgsPcsy7TQwnRxhF8GMKU4ykFLKsgf4TwDwPMNaSCXwWV8@localhost:3025" """ carol_user_config = make_peers_user_config(root_dir, "carol_epoch.yaml", "node3", "3035", carol_peers, "false") # start Alice's node common.start_node(alice_node, alice_user_config) alice_api = common.external_api(alice_node) # Insert some blocks in Alice's chain blocks_to_mine = test_settings["blocks_to_mine"] common.wait_until_height(alice_api, blocks_to_mine) alice_top = alice_api.get_top_block() assert_true(alice_top.height >= blocks_to_mine) # Now Alice has at least blocks_to_mine blocks # start the other nodes common.start_node(bob_node, bob_user_config) common.start_node(carol_node, carol_user_config) # Check that Carol syncs with Alice's chain carol_api = common.external_api(carol_node) common.wait_until_height(carol_api, alice_top.height) assert_equals( carol_api.get_block_by_hash(alice_top.hash).height, alice_top.height) # Check that Carol discovers Alice as a peer carol_int_api = common.internal_api(carol_node) wait( lambda: 'aenode://[email protected]:3015' in get_peers(carol_int_api), timeout_seconds=20, sleep_seconds=1) # cleanup common.stop_node(alice_node) common.stop_node(bob_node) common.stop_node(carol_node) shutil.rmtree(root_dir)
def test_contract_call(): test_settings = settings["test_contract_call"] create_settings = settings["test_contract_create"] (root_dir, node, external_api, top) = setup_node_with_tokens(test_settings, "node") internal_api = common.internal_api(node) send_tokens_to_user("alice", test_settings, internal_api, external_api) ## create contract encoded_tx = get_unsigned_contract_create(test_settings["alice"]["pubkey"], create_settings["create_contract"], external_api) unsigned_tx = common.base58_decode(encoded_tx) unpacked_tx = common.unpack_tx(unsigned_tx) signature = bytearray(list(map(int, create_settings["create_contract"]["signature"].split(",")))) signed = common.encode_signed_tx(unpacked_tx,[signature]) alice_balance0 = common.get_account_balance(internal_api, pub_key=test_settings["alice"]["pubkey"]).balance tx_object = Tx(tx=signed) external_api.post_tx(tx_object) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) alice_balance = common.get_account_balance(internal_api, pub_key=test_settings["alice"]["pubkey"]).balance # assert contract created: call_contract = test_settings["contract_call"] assert_equals(alice_balance0, alice_balance + create_settings["create_contract"]["fee"]) call_input = ContractCallInput("ring", create_settings["create_contract"]["code"],\ call_contract["data"]["function"],\ call_contract["data"]["argument"]) result = external_api.call_contract(call_input) contract_call_obj = ContractCallData( caller=test_settings["alice"]["pubkey"], contract=call_contract["contract"], vm_version=call_contract["vm_version"], fee=call_contract["fee"], amount=call_contract["amount"], gas=call_contract["gas"], gas_price=call_contract["gas_price"], call_data=result.out) call_tx_obj = external_api.post_contract_call(contract_call_obj) encoded_call_tx = call_tx_obj.tx print("Unsigned encoded transaction: " + encoded_call_tx) unsigned_call_tx = common.base58_decode(encoded_call_tx) unpacked_call_tx = common.unpack_tx(unsigned_call_tx) tx = common.parse_tx(unpacked_call_tx) print("Unsigned decoded transaction: " + str(tx)) signature = bytearray(list(map(int, test_settings["contract_call"]["signature"].split(",")))) signed = common.encode_signed_tx(unpacked_call_tx,[signature]) print("Signed transaction: " + signed) alice_balance0 = common.get_account_balance(internal_api, pub_key=test_settings["alice"]["pubkey"]).balance tx_object = Tx(tx=signed) external_api.post_tx(tx_object) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) alice_balance = common.get_account_balance(internal_api, pub_key=test_settings["alice"]["pubkey"]).balance print("BALANCE0 " + str(alice_balance0)) print("BALANCE " + str(alice_balance)) # assert contract created: assert_equals(alice_balance0, alice_balance + test_settings["contract_call"]["fee"]) cleanup(node, root_dir)
def test_not_enough_tokens(): # Bob should not be able to send more tokens than he has # # Let's say Bob has 100 tokens. He should not be able to send more than # 100 tokens to Alice. # # If there's an incoming but unconfirmed deposit into Bob's account then Bob # should not be able to use the incoming tokens until the spend transaction # they are in is confirmed. test_settings = settings["test_not_enough_tokens"] beneficiary = common.setup_beneficiary() (node, (root_dir, ext_api, int_api, top)) = setup_node_with_tokens(test_settings, beneficiary, "miner") alice_address = keys.address(keys.public_key(keys.new_private())) bob_private_key = keys.new_private() bob_public_key = keys.public_key(bob_private_key) bob_address = keys.address(bob_public_key) bob = {'privk': bob_private_key, 'enc_pubk': bob_address} # initial balances - amounts that the miner should send them alice_init_balance = test_settings["send_tokens"]["alice"] bob_init_balance = test_settings["send_tokens"]["bob"] # populate accounts with tokens, and validate balances common.send_tokens_to_unchanging_user_and_wait_balance( beneficiary, alice_address, alice_init_balance, 1, ext_api, int_api) common.send_tokens_to_unchanging_user_and_wait_balance( beneficiary, bob_address, bob_init_balance, 1, ext_api, int_api) alice_balance0 = common.get_account_balance(ext_api, alice_address) bob_balance0 = common.get_account_balance(ext_api, bob_address) print("Alice balance is " + str(alice_balance0)) print("Bob balance is " + str(bob_balance0)) assert_equals(alice_balance0, alice_init_balance) assert_equals(bob_balance0, bob_init_balance) # check that Bob is able to send less tokens than he has spend_tx_fee = test_settings["spend_tx"]["fee"] few_tokens_to_send = test_settings["spend_tx"]["small_amount"] print("Bob is about to send " + str(few_tokens_to_send) + " to Alice") common.send_tokens_to_unchanging_user_and_wait_balance( bob, alice_address, few_tokens_to_send, spend_tx_fee, ext_api, int_api) alice_balance1 = common.get_account_balance(ext_api, pub_key=alice_address) bob_balance1 = common.get_account_balance(ext_api, pub_key=bob_address) print("Alice balance is " + str(alice_balance1)) print("Bob balance is " + str(bob_balance1)) assert_equals(alice_balance1, alice_balance0 + few_tokens_to_send) assert_equals(bob_balance1, bob_balance0 - (few_tokens_to_send + spend_tx_fee)) # check that Bob is unable to send less tokens than he has many_tokens_to_send = test_settings["spend_tx"]["large_amount"] print("Bob is about to send " + str(many_tokens_to_send) + " to Alice") common.send_tokens_to_unchanging_user(bob, alice_address, many_tokens_to_send, spend_tx_fee, ext_api, int_api) common.wait_until_height(ext_api, ext_api.get_current_key_block().height + 3) alice_balance2 = common.get_account_balance(ext_api, pub_key=alice_address) bob_balance2 = common.get_account_balance(ext_api, pub_key=bob_address) print("Alice balance is " + str(alice_balance2)) print("Bob balance is " + str(bob_balance2)) assert_equals(alice_balance2, alice_balance1) assert_equals(bob_balance2, bob_balance1) # stop node common.stop_node(node) shutil.rmtree(root_dir)
def test_contract_call(): test_settings = settings["test_contract_call"] create_settings = settings["test_contract_create"] (root_dir, node, external_api, top) = setup_node_with_tokens(test_settings, "node") internal_api = common.internal_api(node) private_key = keys.new_private() public_key = keys.public_key(private_key) alice_address = keys.address(public_key) test_settings["alice"]["pubkey"] = alice_address send_tokens_to_user("alice", test_settings, external_api, internal_api) ## create contract encoded_tx, encoded_contract_address = get_unsigned_contract_create(alice_address, create_settings["create_contract"], external_api) unsigned_tx = common.base58_decode(encoded_tx) signed = keys.sign_verify_encode_tx(unsigned_tx, private_key, public_key) alice_balance0 = common.get_account_balance(external_api, internal_api, pub_key=alice_address).balance tx_object = Tx(tx=signed) external_api.post_tx(tx_object) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) alice_balance = common.get_account_balance(external_api, internal_api, pub_key=alice_address).balance # assert contract created: call_contract = test_settings["contract_call"] assert_equals(alice_balance0, alice_balance + create_settings["create_contract"]["fee"] + create_settings["create_contract"]["gas_used"] + create_settings["create_contract"]["deposit"] + create_settings["create_contract"]["amount"]) call_input = ContractCallInput("sophia", create_settings["create_contract"]["code"],\ call_contract["data"]["function"],\ call_contract["data"]["argument"]) result = external_api.call_contract(call_input) contract_call_obj = ContractCallData( caller=test_settings["alice"]["pubkey"], contract=encoded_contract_address, vm_version=call_contract["vm_version"], fee=call_contract["fee"], ttl=100, amount=call_contract["amount"], gas=call_contract["gas"], gas_price=call_contract["gas_price"], call_data=result.out) call_tx_obj = external_api.post_contract_call(contract_call_obj) encoded_call_tx = call_tx_obj.tx print("Unsigned encoded transaction: " + encoded_call_tx) unsigned_call_tx = common.base58_decode(encoded_call_tx) signed_call = keys.sign_verify_encode_tx(unsigned_call_tx, private_key, public_key) print("Signed transaction: " + signed_call) alice_balance0 = common.get_account_balance(external_api, internal_api, pub_key=alice_address).balance tx_object = Tx(tx=signed_call) external_api.post_tx(tx_object) top = external_api.get_top() common.wait_until_height(external_api, top.height + 3) alice_balance = common.get_account_balance(external_api, internal_api, pub_key=alice_address).balance # The call runs out of gas and all gas is consumed # assert contract called: assert_equals(alice_balance0, alice_balance + test_settings["contract_call"]["fee"] + test_settings["contract_call"]["gas"] + test_settings["contract_call"]["amount"]) print("Fee and gas was consumed, transaction is part of the chain") cleanup(node, root_dir)
def test_persistence(): """ Test persistence: Bob's downloaded blockchain should persist between restarts. He should only download updates to his blockchain when his node starts. """ test_settings = settings["test_persistence"] # prepare a dir to hold the config and DB files root_dir = tempfile.mkdtemp() p_m_conf = """\ --- chain: persist: true db_path: \"""" + root_dir + """\" mining: autostart: true expected_mine_rate: 100 beneficiary: "ak_2QLChDdERfod9QajLkCTsJnYP3RNqZJmAFWQWQZWr99fSrC55h" cuckoo: edge_bits: 15 miners: - executable: mean15-generic extra_args: "" """ p_conf = """\ --- chain: persist: true db_path: \"""" + root_dir + """\" mining: beneficiary: "ak_2QLChDdERfod9QajLkCTsJnYP3RNqZJmAFWQWQZWr99fSrC55h" """ persistence_mining_user_config = common.install_user_config( root_dir, "p_m_aeternity.yaml", p_m_conf) minimal_user_config_with_persistence = common.install_user_config( root_dir, "p_aeternity.yaml", p_conf) bob_node = test_settings["nodes"]["bob"] common.start_node(bob_node, persistence_mining_user_config) bob_api = common.external_api(bob_node) # Insert some blocks in Bob's chain blocks_to_mine = test_settings["blocks_to_mine"] common.wait_until_height(bob_api, blocks_to_mine) bob_top = bob_api.get_current_key_block() assert_equals(bob_top.height >= blocks_to_mine, True) # Now Bob has at least blocks_to_mine blocks common.stop_node(bob_node) common.start_node(bob_node, minimal_user_config_with_persistence) bob_new_top = bob_api.get_current_key_block() if (bob_new_top.height > bob_top.height): # Bob's node had mined another block(s) before being stopped bob_block = bob_api.get_key_block_by_hash( bob_top.hash) # this block is presnet assert_equals(bob_block.height, bob_top.height) else: assert_equals(bob_new_top.height, bob_top.height) assert_equals(bob_top.hash, bob_new_top.hash) common.stop_node(bob_node) shutil.rmtree(root_dir)