def test_tx_status(): nodes = start_cluster(4, 0, 1, None, [["epoch_length", 1000], ["block_producer_kickout_threshold", 80], ["transaction_validity_period", 10000]], {}) signer_key = nodes[0].signer_key status = nodes[0].get_status() block_hash = status['sync_info']['latest_block_hash'] encoded_block_hash = base58.b58decode(block_hash.encode('ascii')) payment_tx = transaction.sign_payment_tx( signer_key, 'test1', 100, 1, encoded_block_hash) submit_tx_and_check(nodes, 0, payment_tx) deploy_contract_tx = transaction.sign_deploy_contract_tx( signer_key, load_test_contract(), 2, encoded_block_hash) submit_tx_and_check(nodes, 0, deploy_contract_tx) function_call_tx = transaction.sign_function_call_tx( signer_key, signer_key.account_id, 'write_key_value', struct.pack('<QQ', 42, 24), 300000000000000, 0, 3, encoded_block_hash) submit_tx_and_check(nodes, 0, deploy_contract_tx)
def send_moar_txs(self, last_block_hash, num, use_routing): last_balances = [x for x in self.expected_balances] for i in range(num): while True: from_ = random.randint(0, self.num_nodes - 1) if self.nodes[from_] is not None: break to = random.randint(0, self.num_nodes - 2) if to >= from_: to += 1 amt = random.randint(0, 500) if self.expected_balances[from_] >= amt: logger.info("Sending a tx from %s to %s for %s" % (from_, to, amt)) tx = sign_payment_tx( self.nodes[from_].signer_key, 'test%s' % to, amt, self.next_nonce, base58.b58decode(last_block_hash.encode('utf8'))) if use_routing: self.nodes[0].send_tx(tx) else: self.nodes[self.act_to_val[from_]].send_tx(tx) self.expected_balances[from_] -= amt self.expected_balances[to] += amt self.next_nonce += 1
def send_transfer(source_account, dest_index, base_block_hash): alice = source_account bob = load_testing_account_id(dest_index) alice_nonce = get_nonce_for_pk(alice.account_id, alice.pk) tranfer_amount = 100 tx = sign_payment_tx(alice, bob, tranfer_amount, alice_nonce + 1, base_block_hash) send_tx(tx)
def send_transfer_tx(self, dest_account_id, transfer_amount=100, base_block_hash=None): self.prep_tx() tx = sign_payment_tx(self.key, dest_account_id, transfer_amount, self.nonce, base_block_hash or self.base_block_hash) return self.send_tx(tx)
def test_tx_status(nodes, *, nonce_offset: int = 0): signer_key = nodes[0].signer_key encoded_block_hash = nodes[0].get_latest_block().hash_bytes payment_tx = transaction.sign_payment_tx(signer_key, 'test1', 100, nonce_offset + 1, encoded_block_hash) submit_tx_and_check(nodes, 0, payment_tx) deploy_contract_tx = transaction.sign_deploy_contract_tx( signer_key, load_test_contract(), nonce_offset + 2, encoded_block_hash) submit_tx_and_check(nodes, 0, deploy_contract_tx) function_call_tx = transaction.sign_function_call_tx( signer_key, signer_key.account_id, 'write_key_value', struct.pack('<QQ', 42, 24), 300000000000000, 0, nonce_offset + 3, encoded_block_hash) submit_tx_and_check(nodes, 0, deploy_contract_tx)
def test_finality(self): nodes = start_cluster(4, 1, 1, None, [["min_gas_price", 0], ["epoch_length", 100]], {}) time.sleep(3) # kill one validating node so that no block can be finalized nodes[2].kill() time.sleep(1) acc0_balance = int(nodes[0].get_account('test0')['result']['amount']) acc1_balance = int(nodes[0].get_account('test1')['result']['amount']) token_transfer = 10 latest_block_hash = nodes[0].get_latest_block().hash_bytes tx = sign_payment_tx(nodes[0].signer_key, 'test1', token_transfer, 1, latest_block_hash) logger.info("About to send payment") logger.info(nodes[0].send_tx_and_wait(tx, timeout=200)) logger.info("Done") # wait for doomslug finality time.sleep(5) for i in range(2): acc_id = 'test0' if i == 0 else 'test1' acc_no_finality = nodes[0].get_account(acc_id) acc_doomslug_finality = nodes[0].get_account(acc_id, "near-final") acc_nfg_finality = nodes[0].get_account(acc_id, "final") if i == 0: self.assertEqual(int(acc_no_finality['result']['amount']), acc0_balance - token_transfer) self.assertEqual(int(acc_doomslug_finality['result']['amount']), acc0_balance - token_transfer) self.assertEqual(int(acc_nfg_finality['result']['amount']), acc0_balance - token_transfer) else: self.assertEqual(int(acc_no_finality['result']['amount']), acc1_balance + token_transfer) self.assertEqual(int(acc_doomslug_finality['result']['amount']), acc1_balance + token_transfer) self.assertEqual(int(acc_nfg_finality['result']['amount']), acc1_balance + token_transfer)
def main(): node_root = "/tmp/near/backward" if os.path.exists(node_root): shutil.rmtree(node_root) subprocess.check_output('mkdir -p /tmp/near', shell=True) branch = branches.latest_rc_branch() near_root, (stable_branch, current_branch) = branches.prepare_ab_test(branch) # Setup local network. subprocess.call([ "%snear-%s" % (near_root, stable_branch), "--home=%s" % node_root, "testnet", "--v", "2", "--prefix", "test" ]) # Run both binaries at the same time. config = { "local": True, 'near_root': near_root, 'binary_name': "near-%s" % stable_branch } stable_node = cluster.spin_up_node(config, near_root, os.path.join(node_root, "test0"), 0, None, None) config["binary_name"] = "near-%s" % current_branch current_node = cluster.spin_up_node(config, near_root, os.path.join(node_root, "test1"), 1, stable_node.node_key.pk, stable_node.addr()) # Check it all works. BLOCKS = 100 TIMEOUT = 150 max_height = -1 started = time.time() # Create account, transfer tokens, deploy contract, invoke function call status = stable_node.get_status() block_hash = base58.b58decode( status['sync_info']['latest_block_hash'].encode('utf-8')) new_account_id = 'test_account' new_signer_key = cluster.Key(new_account_id, stable_node.signer_key.pk, stable_node.signer_key.sk) create_account_tx = sign_create_account_with_full_access_key_and_balance_tx( stable_node.signer_key, new_account_id, new_signer_key, 10**24, 1, block_hash) res = stable_node.send_tx_and_wait(create_account_tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res transfer_tx = sign_payment_tx(stable_node.signer_key, new_account_id, 10**25, 2, block_hash) res = stable_node.send_tx_and_wait(transfer_tx, timeout=20) assert 'error' not in res, res status = stable_node.get_status() block_height = status['sync_info']['latest_block_height'] nonce = block_height * 1_000_000 - 1 tx = sign_deploy_contract_tx(new_signer_key, load_test_contract(), nonce, block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res tx = sign_function_call_tx(new_signer_key, new_account_id, 'write_random_value', [], 10**13, 0, nonce + 1, block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res data = json.dumps([{ "create": { "account_id": "near_2", "method_name": "call_promise", "arguments": [], "amount": "0", "gas": 30000000000000, }, "id": 0 }, { "then": { "promise_index": 0, "account_id": "near_3", "method_name": "call_promise", "arguments": [], "amount": "0", "gas": 30000000000000, }, "id": 1 }]) tx = sign_function_call_tx(new_signer_key, new_account_id, 'call_promise', bytes(data, 'utf-8'), 90000000000000, 0, nonce + 2, block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res while max_height < BLOCKS: assert time.time() - started < TIMEOUT status = current_node.get_status() cur_height = status['sync_info']['latest_block_height'] if cur_height > max_height: max_height = cur_height time.sleep(1)
[["min_gas_price", 0], ["epoch_length", 10], ["block_producer_kickout_threshold", 70]], {2: { "tracked_shards": [0, 1, 2, 3] }}) time.sleep(3) for i in range(4): nonce = 1 status = nodes[0].get_status() latest_block_hash = status['sync_info']['latest_block_hash'] for j in range(4): if i != j: tx = sign_payment_tx( nodes[i].signer_key, 'test%s' % j, 100, nonce, base58.b58decode(latest_block_hash.encode('utf8'))) nonce += 1 print("sending transaction from test%d to test%d" % (i, j)) result = nodes[-1].send_tx_and_wait(tx, timeout=25) if 'error' in result: assert False, result time.sleep(2) for i in range(4): height1 = nodes[-1].get_status()['sync_info']['latest_block_height'] height2 = nodes[-2].get_status()['sync_info']['latest_block_height'] height = min(height1, height2) def get_account(node, acc, height):
def monkey_transactions(stopped, error, nodes, nonces): def get_balances(): acts = [ nodes[-1].get_account("test%s" % i)['result'] for i in range(len(nodes)) ] return [int(x['amount']) + int(x['locked']) for x in acts] expected_balances = get_balances() min_balances = [x - MAX_STAKE for x in expected_balances] total_supply = (sum(expected_balances)) print("TOTAL SUPPLY: %s" % total_supply) last_iter_switch = time.time() mode = 0 # 0 = send more tx, 1 = wait for balances tx_count = 0 last_tx_set = [] rolling_tolerance = tx_tolerance # do not stop when waiting for balances while stopped.value == 0 or mode == 1: validator_ids = get_validator_ids(nodes) if time.time() - last_iter_switch > balances_timeout: if mode == 0: print("%s TRANSACTIONS SENT. WAITING FOR BALANCES" % tx_count) mode = 1 else: print("BALANCES NEVER CAUGHT UP, CHECKING UNFINISHED TRANSACTIONS") snapshot_expected_balances = [x for x in expected_balances] def revert_txs(): nonlocal expected_balances good = 0 bad = 0 for tx in last_tx_set: rcpts = nodes[-1].json_rpc('tx', [tx[3]])['result']['receipts'] if rcpts == []: bad += 1 expected_balances[tx[1]] += tx[4] expected_balances[tx[2]] -= tx[4] else: good += 1 return (good, bad) good, bad = revert_txs() if expected_balances == get_balances(): # reverting helped print("REVERTING HELPED, TX EXECUTED: %s, TX LOST: %s" % (good, bad)) bad_ratio = bad / (good + bad) if bad_ratio > rolling_tolerance: rolling_tolerance -= bad_ratio - rolling_tolerance if rolling_tolerance < 0: assert False else: rolling_tolerance = tx_tolerance min_balances = [x - MAX_STAKE for x in expected_balances] tx_count = 0 mode = 0 last_tx_set = [] else: # still no match, fail print("REVERTING DIDN'T HELP, TX EXECUTED: %s, TX LOST: %s" % (good, bad)) for step in range(10): # trace balances for 20 seconds to see if they are catching up print(get_balances()) time.sleep(2) expected_balances = snapshot_expected_balances good, bad = revert_txs() print("The latest and greatest stats on successful/failed: %s/%s" % (good, bad)) assert False, "Balances didn't update in time. Expected: %s, received: %s" % (expected_balances, get_balances()) last_iter_switch = time.time() if mode == 0: from_ = random.randint(0, len(nodes) - 1) while min_balances[from_] < 0: from_ = random.randint(0, len(nodes) - 1) to = random.randint(0, len(nodes) - 1) while from_ == to: to = random.randint(0, len(nodes) - 1) amt = random.randint(0, min_balances[from_]) nonce_val, nonce_lock = nonces[from_] hash_, _ = get_recent_hash(nodes[-1]) with nonce_lock: tx = sign_payment_tx(nodes[from_].signer_key, 'test%s' % to, amt, nonce_val.value, base58.b58decode(hash_.encode('utf8'))) for validator_id in validator_ids: try: tx_hash = nodes[validator_id].send_tx(tx)['result'] except (requests.exceptions.ReadTimeout, requests.exceptions.ConnectionError): if not network_issues_expected and not nodes[validator_id].mess_with: raise last_tx_set.append((tx, from_, to, tx_hash, amt)) nonce_val.value = nonce_val.value + 1 expected_balances[from_] -= amt expected_balances[to] += amt min_balances[from_] -= amt tx_count += 1 else: if get_balances() == expected_balances: print("BALANCES CAUGHT UP, BACK TO SPAMMING TXS") min_balances = [x - MAX_STAKE for x in expected_balances] tx_count = 0 mode = 0 rolling_tolerance = tx_tolerance last_tx_set = [] if mode == 1: time.sleep(1) elif mode == 0: time.sleep(0.1)
def monkey_transactions(stopped, error, nodes, nonces): def get_balances(): acts = [ nodes[-1].get_account("test%s" % i)['result'] for i in range(len(nodes)) ] return [int(x['amount']) + int(x['locked']) for x in acts] expected_balances = get_balances() min_balances = [x - MAX_STAKE for x in expected_balances] total_supply = (sum(expected_balances)) print("TOTAL SUPPLY: %s" % total_supply) last_iter_switch = time.time() mode = 0 # 0 = send more tx, 1 = wait for balances tx_count = 0 last_tx_set = [] while stopped.value == 0: validator_ids = get_validator_ids(nodes) if time.time() - last_iter_switch > BALANCES_TIMEOUT: if mode == 0: print("%s TRANSACTIONS SENT. WAITING FOR BALANCES" % tx_count) mode = 1 else: print( "BALANCES NEVER CAUGHT UP, CHECKING UNFINISHED TRANSACTIONS" ) good = 0 bad = 0 for tx in last_tx_set: rcpts = nodes[-1].json_rpc('tx', [tx[3]])['result']['receipts'] if rcpts == []: bad += 1 expected_balances[tx[1]] += tx[4] expected_balances[tx[2]] -= tx[4] else: good += 1 if expected_balances == get_balances(): # reverting helped print("REVERTING HELPED, TX EXECUTED: %s, TX LOST: %s" % (good, bad)) assert bad * 3 <= good min_balances = [x - MAX_STAKE for x in expected_balances] tx_count = 0 mode = 0 last_tx_set = [] else: # still no match, fail print( "REVERTING DIDN'T HELP, TX EXECUTED: %s, TX LOST: %s" % (good, bad)) for tx in last_tx_set: print("\nTX %s statuses:\n%s\n\n" % (tx[3], '\n'.join([ str(nodes[-1].json_rpc( 'tx', [tx[3]])['result']['receipts']) for node in nodes if node is not None ]))) assert False, "Balances didn't update in time. Expected: %s, received: %s" % ( expected_balances, get_balances()) last_iter_switch = time.time() if mode == 0: from_ = random.randint(0, len(nodes) - 1) while min_balances[from_] < 0: from_ = random.randint(0, len(nodes) - 1) to = random.randint(0, len(nodes) - 1) while from_ == to: to = random.randint(0, len(nodes) - 1) amt = random.randint(0, min_balances[from_]) nonce_val, nonce_lock = nonces[from_] hash_, _ = get_recent_hash(nodes[-1]) with nonce_lock: tx = sign_payment_tx(nodes[from_].signer_key, 'test%s' % to, amt, nonce_val.value, base58.b58decode(hash_.encode('utf8'))) for validator_id in validator_ids: tx_hash = nodes[validator_id].send_tx(tx)['result'] last_tx_set.append((tx, from_, to, tx_hash, amt)) nonce_val.value = nonce_val.value + 1 expected_balances[from_] -= amt expected_balances[to] += amt min_balances[from_] -= amt tx_count += 1 else: if get_balances() == expected_balances: print("BALANCES CAUGHT UP, BACK TO SPAMMING TXS") min_balances = [x - MAX_STAKE for x in expected_balances] tx_count = 0 mode = 0 last_tx_set = [] if mode == 1: time.sleep(1) elif mode == 0: time.sleep(0.1) shutdown_started = time.time() while time.time() - shutdown_started < BALANCES_TIMEOUT: if expected_balances == get_balances(): return assert False, "Balances didn't update in time. Expected: %s, received: %s" % ( expected_balances, get_balances())
def main(): node_root = "/tmp/near/upgradable" if os.path.exists(node_root): shutil.rmtree(node_root) subprocess.check_output('mkdir -p /tmp/near', shell=True) # TODO(#3285): use proper branch near_root, (stable_branch, current_branch) = branches.prepare_ab_test("1.13.0") # Setup local network. print([ "%snear-%s" % (near_root, stable_branch), "--home=%s" % node_root, "testnet", "--v", "4", "--prefix", "test" ]) subprocess.call([ "%snear-%s" % (near_root, stable_branch), "--home=%s" % node_root, "testnet", "--v", "4", "--prefix", "test" ]) genesis_config_changes = [ ("epoch_length", 20), ("num_block_producer_seats", 10), ("num_block_producer_seats_per_shard", [10]), ("block_producer_kickout_threshold", 80), ("chunk_producer_kickout_threshold", 80), ("chain_id", "testnet") ] node_dirs = [os.path.join(node_root, 'test%d' % i) for i in range(4)] for i, node_dir in enumerate(node_dirs): cluster.apply_genesis_changes(node_dir, genesis_config_changes) # Start 3 stable nodes and one current node. config = { "local": True, 'near_root': near_root, 'binary_name': "near-%s" % stable_branch } nodes = [cluster.spin_up_node( config, near_root, node_dirs[0], 0, None, None)] for i in range(1, 3): nodes.append(cluster.spin_up_node( config, near_root, node_dirs[i], i, nodes[0].node_key.pk, nodes[0].addr())) config["binary_name"] = "near-%s" % current_branch nodes.append(cluster.spin_up_node( config, near_root, node_dirs[3], 3, nodes[0].node_key.pk, nodes[0].addr())) time.sleep(2) # deploy a contract status = nodes[0].get_status() hash = status['sync_info']['latest_block_hash'] tx = sign_deploy_contract_tx( nodes[0].signer_key, load_binary_file( '../runtime/near-vm-runner/tests/res/test_contract_rs.wasm'), 1, base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res # write some random value tx = sign_function_call_tx(nodes[0].signer_key, nodes[0].signer_key.account_id, 'write_random_value', [], 10**13, 0, 2, base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res # hex_account_id = (b"I'm hex!" * 4).hex() hex_account_id = '49276d206865782149276d206865782149276d206865782149276d2068657821' tx = sign_payment_tx(key=nodes[0].signer_key, to=hex_account_id, amount=10 ** 25, nonce=3, blockHash=base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) # Account doesn't exist assert 'error' not in res, res assert 'Failure' in res['result']['status'], res # No account res = nodes[0].get_account(hex_account_id) assert 'error' in res, res wait_for_blocks_or_timeout(nodes[0], 20, 120) # Restart stable nodes into new version. for i in range(3): nodes[i].kill() nodes[i].binary_name = config['binary_name'] nodes[i].start(nodes[0].node_key.pk, nodes[0].addr()) wait_for_blocks_or_timeout(nodes[3], 60, 120) status0 = nodes[0].get_status() status3 = nodes[3].get_status() protocol_version = status0['protocol_version'] latest_protocol_version = status3["latest_protocol_version"] assert protocol_version == latest_protocol_version, \ "Latest protocol version %d should match active protocol version %d" % (latest_protocol_version, protocol_version) hash = status0['sync_info']['latest_block_hash'] # write some random value again tx = sign_function_call_tx(nodes[0].signer_key, nodes[0].signer_key.account_id, 'write_random_value', [], 10**13, 0, 4, base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res tx = sign_payment_tx(key=nodes[0].signer_key, to=hex_account_id, amount=10 ** 25, nonce=5, blockHash=base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) # Successfully created a new account on transfer to hex assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res hex_account_balance = int(nodes[0].get_account(hex_account_id)['result']['amount']) assert hex_account_balance == 10 ** 25
nodes = start_cluster(3, 1, 1, None, [["min_gas_price", 0], ["epoch_length", 100]], {}) time.sleep(3) # kill one validating node so that no block can be finalized nodes[2].kill() time.sleep(1) acc0_balance = int(nodes[0].get_account('test0')['result']['amount']) acc1_balance = int(nodes[0].get_account('test1')['result']['amount']) token_transfer = 10 status = nodes[0].get_status() latest_block_hash = status['sync_info']['latest_block_hash'] tx = sign_payment_tx(nodes[0].signer_key, 'test1', token_transfer, 1, base58.b58decode(latest_block_hash.encode('utf8'))) logger.info(nodes[0].send_tx_and_wait(tx, timeout=20)) # wait for doomslug finality time.sleep(5) for i in range(2): acc_id = 'test0' if i == 0 else 'test1' acc_no_finality = nodes[0].get_account(acc_id) acc_doomslug_finality = nodes[0].get_account(acc_id, "near-final") acc_nfg_finality = nodes[0].get_account(acc_id, "final") if i == 0: assert int(acc_no_finality['result'] ['amount']) == acc0_balance - token_transfer assert int(acc_doomslug_finality['result'] ['amount']) == acc0_balance - token_transfer assert int(acc_nfg_finality['result']['amount']) == acc0_balance
} } }) time.sleep(3) started = time.time() old_balances = [ int(nodes[-1].get_account("test%s" % x)['result']['amount']) for x in [0, 1, 2] ] logger.info(f"BALANCES BEFORE {old_balances}") hash_ = nodes[1].get_latest_block().hash time.sleep(5) tx = sign_payment_tx(nodes[0].signer_key, 'test1', 100, 1, base58.b58decode(hash_.encode('utf8'))) logger.info(nodes[-2].send_tx_and_wait(tx, timeout=20)) new_balances = [ int(nodes[-1].get_account("test%s" % x)['result']['amount']) for x in [0, 1, 2] ] logger.info(f"BALANCES AFTER {new_balances}") old_balances[0] -= 100 old_balances[1] += 100 assert old_balances == new_balances
nodes, accounts = connect_to_mocknet(None) print() # Test balance transfers initial_balances = [int(nodes[0].get_account(account.account_id)['result']['amount']) for account in accounts] nonces = [nodes[0].get_nonce_for_pk(account.account_id, account.pk) for account in accounts] print("INITIAL BALANCES", initial_balances) print("NONCES", nonces) last_block_hash = nodes[0].get_status()['sync_info']['latest_block_hash'] last_block_hash_decoded = base58.b58decode(last_block_hash.encode('utf8')) tx = sign_payment_tx(accounts[0], accounts[1].account_id, 100, nonces[0] + 1, last_block_hash_decoded) nodes[0].send_tx_and_wait(tx, timeout=10) new_balances = [int(nodes[0].get_account(account.account_id)['result']['amount']) for account in accounts] print("NEW BALANCES", new_balances) assert (new_balances[0] + 100) % 1000 == initial_balances[0] % 1000 assert (initial_balances[1] + 100) % 1000 == new_balances[1] % 1000 # Test contract deployment tx = sign_deploy_contract_tx(accounts[2], load_binary_file('../runtime/near-vm-runner/tests/res/test_contract_rs.wasm'), nonces[2] + 1, last_block_hash_decoded) nodes[0].send_tx_and_wait(tx, timeout=20) tx2 = sign_function_call_tx(accounts[2], accounts[2].account_id, 'log_something', [], 100000000000, 100000000000, nonces[2] + 2, last_block_hash_decoded)
def main(): node_root = "/tmp/near/upgradable" if os.path.exists(node_root): shutil.rmtree(node_root) subprocess.check_output('mkdir -p /tmp/near', shell=True) branch = branches.latest_rc_branch() logger.info(f"Latest rc release branch is {branch}") near_root, (stable_branch, current_branch) = branches.prepare_ab_test(branch) # Setup local network. logger.info([ "%snear-%s" % (near_root, stable_branch), "--home=%s" % node_root, "testnet", "--v", "4", "--prefix", "test" ]) subprocess.call([ "%snear-%s" % (near_root, stable_branch), "--home=%s" % node_root, "testnet", "--v", "4", "--prefix", "test" ]) genesis_config_changes = [("epoch_length", 20), ("num_block_producer_seats", 10), ("num_block_producer_seats_per_shard", [10]), ("block_producer_kickout_threshold", 80), ("chunk_producer_kickout_threshold", 80), ("chain_id", "testnet")] node_dirs = [os.path.join(node_root, 'test%d' % i) for i in range(4)] for i, node_dir in enumerate(node_dirs): cluster.apply_genesis_changes(node_dir, genesis_config_changes) # Start 3 stable nodes and one current node. config = { "local": True, 'near_root': near_root, 'binary_name': "near-%s" % stable_branch } nodes = [ cluster.spin_up_node(config, near_root, node_dirs[0], 0, None, None) ] for i in range(1, 3): nodes.append( cluster.spin_up_node(config, near_root, node_dirs[i], i, nodes[0].node_key.pk, nodes[0].addr())) if os.getenv('NAYDUCK'): config["binary_name"] = "near" else: config["binary_name"] = "near-%s" % current_branch nodes.append( cluster.spin_up_node(config, near_root, node_dirs[3], 3, nodes[0].node_key.pk, nodes[0].addr())) time.sleep(2) # deploy a contract status = nodes[0].get_status() hash = status['sync_info']['latest_block_hash'] tx = sign_deploy_contract_tx(nodes[0].signer_key, load_test_contract(), 1, base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res # write some random value tx = sign_function_call_tx(nodes[0].signer_key, nodes[0].signer_key.account_id, 'write_random_value', [], 10**13, 0, 2, base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res wait_for_blocks_or_timeout(nodes[0], 20, 120) # Restart stable nodes into new version. for i in range(3): nodes[i].kill() nodes[i].binary_name = config['binary_name'] nodes[i].start(nodes[0].node_key.pk, nodes[0].addr()) wait_for_blocks_or_timeout(nodes[3], 60, 120) status0 = nodes[0].get_status() status3 = nodes[3].get_status() protocol_version = status0['protocol_version'] latest_protocol_version = status3["latest_protocol_version"] assert protocol_version == latest_protocol_version, \ "Latest protocol version %d should match active protocol version %d" % ( latest_protocol_version, protocol_version) hash = status0['sync_info']['latest_block_hash'] # write some random value again tx = sign_function_call_tx(nodes[0].signer_key, nodes[0].signer_key.account_id, 'write_random_value', [], 10**13, 0, 4, base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res # hex_account_id = (b"I'm hex!" * 4).hex() hex_account_id = '49276d206865782149276d206865782149276d206865782149276d2068657821' tx = sign_payment_tx(key=nodes[0].signer_key, to=hex_account_id, amount=10**25, nonce=5, blockHash=base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) # Successfully created a new account on transfer to hex assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res hex_account_balance = int( nodes[0].get_account(hex_account_id)['result']['amount']) assert hex_account_balance == 10**25 hash = status0['sync_info']['latest_block_hash'] new_account_id = f'new.{nodes[0].signer_key.account_id}' new_signer_key = cluster.Key(new_account_id, nodes[0].signer_key.pk, nodes[0].signer_key.sk) create_account_tx = sign_create_account_with_full_access_key_and_balance_tx( nodes[0].signer_key, new_account_id, new_signer_key, 10**24, 6, base58.b58decode(hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(create_account_tx, timeout=20) # Successfully created a new account assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res hash = status0['sync_info']['latest_block_hash'] status = nodes[0].get_status() block_height = status['sync_info']['latest_block_height'] beneficiary_account_id = '1982374698376abd09265034ef35034756298375462323456294875193563756' tx = sign_delete_account_tx(key=new_signer_key, to=new_account_id, beneficiary=beneficiary_account_id, nonce=block_height * 1_000_000 - 1, block_hash=base58.b58decode( hash.encode('utf8'))) res = nodes[0].send_tx_and_wait(tx, timeout=20) # Successfully deleted an account assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res
def test_upgrade() -> None: """Test that upgrade from ‘stable’ to ‘current’ binary is possible. 1. Start a network with 3 `stable` nodes and 1 `new` node. 2. Start switching `stable` nodes one by one with `new` nodes. 3. Run for three epochs and observe that current protocol version of the network matches `new` nodes. """ executables = get_executables() node_root = utils.get_near_tempdir('upgradable', clean=True) # Setup local network. # TODO(#4372): testnet subcommand deprecated since 1.24. Replace with # localnet after a couple of releases in 2022. cmd = (executables.stable.neard, "--home=%s" % node_root, "testnet", "--v", "4", "--prefix", "test") logger.info(' '.join(str(arg) for arg in cmd)) subprocess.check_call(cmd) genesis_config_changes = [("epoch_length", 20), ("num_block_producer_seats", 10), ("num_block_producer_seats_per_shard", [10]), ("block_producer_kickout_threshold", 80), ("chunk_producer_kickout_threshold", 80), ("chain_id", "testnet")] node_dirs = [os.path.join(node_root, 'test%d' % i) for i in range(4)] for i, node_dir in enumerate(node_dirs): cluster.apply_genesis_changes(node_dir, genesis_config_changes) cluster.apply_config_changes(node_dir, {'tracked_shards': [0]}) # Start 3 stable nodes and one current node. config = executables.stable.node_config() nodes = [ cluster.spin_up_node(config, executables.stable.root, node_dirs[0], 0) ] for i in range(1, 3): nodes.append( cluster.spin_up_node(config, executables.stable.root, node_dirs[i], i, boot_node=nodes[0])) config = executables.current.node_config() nodes.append( cluster.spin_up_node(config, executables.current.root, node_dirs[3], 3, boot_node=nodes[0])) time.sleep(2) # deploy a contract hash = nodes[0].get_latest_block().hash_bytes tx = sign_deploy_contract_tx(nodes[0].signer_key, utils.load_test_contract(), 1, hash) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res # write some random value tx = sign_function_call_tx(nodes[0].signer_key, nodes[0].signer_key.account_id, 'write_random_value', [], 10**13, 0, 2, hash) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res utils.wait_for_blocks(nodes[0], count=20) # Restart stable nodes into new version. for i in range(3): nodes[i].kill() nodes[i].binary_name = config['binary_name'] nodes[i].start(boot_node=nodes[0]) utils.wait_for_blocks(nodes[3], count=60) status0 = nodes[0].get_status() status3 = nodes[3].get_status() protocol_version = status0['protocol_version'] latest_protocol_version = status3["latest_protocol_version"] assert protocol_version == latest_protocol_version, \ "Latest protocol version %d should match active protocol version %d" % ( latest_protocol_version, protocol_version) hash = base58.b58decode( status0['sync_info']['latest_block_hash'].encode('ascii')) # write some random value again tx = sign_function_call_tx(nodes[0].signer_key, nodes[0].signer_key.account_id, 'write_random_value', [], 10**13, 0, 4, hash) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res # hex_account_id = (b"I'm hex!" * 4).hex() hex_account_id = '49276d206865782149276d206865782149276d206865782149276d2068657821' tx = sign_payment_tx(key=nodes[0].signer_key, to=hex_account_id, amount=10**25, nonce=5, blockHash=hash) res = nodes[0].send_tx_and_wait(tx, timeout=20) # Successfully created a new account on transfer to hex assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res hex_account_balance = int( nodes[0].get_account(hex_account_id)['result']['amount']) assert hex_account_balance == 10**25
if is_local_receipt: receipt_id_from_outcomes.remove( res['result']['transaction_outcome']['outcome']['receipt_ids'][0]) assert receipt_id_from_outcomes == receipt_id_from_receipts, f'receipt id from outcomes {receipt_id_from_outcomes}, receipt id from receipts {receipt_id_from_receipts} ' nodes = start_cluster( 4, 0, 1, None, [["epoch_length", 1000], ["block_producer_kickout_threshold", 80], ["transaction_validity_period", 10000]], {}) status = nodes[0].get_status() block_hash = status['sync_info']['latest_block_hash'] print("1") payment_tx = transaction.sign_payment_tx( nodes[0].signer_key, 'test1', 100, 1, base58.b58decode(block_hash.encode('utf8'))) submit_tx_and_check(nodes[0], payment_tx) print("2") deploy_contract_tx = transaction.sign_deploy_contract_tx( nodes[0].signer_key, load_binary_file('../tests/hello.wasm'), 2, base58.b58decode(block_hash.encode('utf8'))) submit_tx_and_check(nodes[0], deploy_contract_tx) print("3") function_call_tx = transaction.sign_function_call_tx( nodes[0].signer_key, nodes[0].signer_key.account_id, 'setKeyValue', json.dumps({ "key": "my_key", "value": "my_value_1"
def main(): node_root = utils.get_near_tempdir('backward', clean=True) executables = branches.prepare_ab_test() # Setup local network. subprocess.check_call([ executables.stable.neard, "--home=%s" % node_root, # TODO(#4372): testnet subcommand deprecated since 1.24. Replace with # localnet after a couple of releases in 2022. "testnet", "--v", "2", "--prefix", "test" ]) # Run both binaries at the same time. config = executables.stable.node_config() stable_node = cluster.spin_up_node(config, executables.stable.root, str(node_root / 'test0'), 0) config = executables.current.node_config() current_node = cluster.spin_up_node(config, executables.current.root, str(node_root / 'test1'), 1, boot_node=stable_node) # Check it all works. BLOCKS = 100 max_height = -1 started = time.time() # Create account, transfer tokens, deploy contract, invoke function call block_hash = stable_node.get_latest_block().hash_bytes new_account_id = 'test_account.test0' new_signer_key = cluster.Key(new_account_id, stable_node.signer_key.pk, stable_node.signer_key.sk) create_account_tx = sign_create_account_with_full_access_key_and_balance_tx( stable_node.signer_key, new_account_id, new_signer_key, 10**24, 1, block_hash) res = stable_node.send_tx_and_wait(create_account_tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res transfer_tx = sign_payment_tx(stable_node.signer_key, new_account_id, 10**25, 2, block_hash) res = stable_node.send_tx_and_wait(transfer_tx, timeout=20) assert 'error' not in res, res block_height = stable_node.get_latest_block().height nonce = block_height * 1_000_000 - 1 tx = sign_deploy_contract_tx(new_signer_key, utils.load_test_contract(), nonce, block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res tx = sign_deploy_contract_tx(stable_node.signer_key, utils.load_test_contract(), 3, block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res tx = sign_function_call_tx(new_signer_key, new_account_id, 'write_random_value', [], 10**13, 0, nonce + 1, block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res data = json.dumps([{ "create": { "account_id": "test_account.test0", "method_name": "call_promise", "arguments": [], "amount": "0", "gas": 30000000000000, }, "id": 0 }, { "then": { "promise_index": 0, "account_id": "test0", "method_name": "call_promise", "arguments": [], "amount": "0", "gas": 30000000000000, }, "id": 1 }]) tx = sign_function_call_tx(stable_node.signer_key, new_account_id, 'call_promise', bytes(data, 'utf-8'), 90000000000000, 0, nonce + 2, block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res assert 'Failure' not in res['result']['status'], res utils.wait_for_blocks(current_node, target=BLOCKS)
def monkey_transactions(stopped, error, nodes, nonces): def get_balances(): acts = [ nodes[-1].get_account("test%s" % i)['result'] for i in range(len(nodes)) ] return [int(x['amount']) + int(x['locked']) for x in acts] expected_balances = get_balances() min_balances = [x - MAX_STAKE for x in expected_balances] total_supply = (sum(expected_balances)) logging.info("TOTAL SUPPLY: %s" % total_supply) last_iter_switch = time.time() mode = 0 # 0 = send more tx, 1 = wait for balances tx_count = 0 last_tx_set = [] rolling_tolerance = tx_tolerance # do not stop when waiting for balances while stopped.value == 0 or mode == 1: validator_ids = get_validator_ids(nodes) if time.time() - last_iter_switch > balances_timeout: if mode == 0: logging.info("%s TRANSACTIONS SENT. WAITING FOR BALANCES" % tx_count) mode = 1 else: logging.info( "BALANCES NEVER CAUGHT UP, CHECKING UNFINISHED TRANSACTIONS" ) def revert_txs(): nonlocal expected_balances good = 0 bad = 0 for tx in last_tx_set: tx_happened = True response = nodes[-1].json_rpc( 'tx', [tx[3], "test%s" % tx[1]], timeout=5) if 'error' in response and 'data' in response[ 'error'] and "doesn't exist" in response['error'][ 'data']: tx_happened = False elif 'result' in response and 'receipts_outcome' in response[ 'result']: tx_happened = len( response['result']['receipts_outcome']) > 0 else: assert False, response if not tx_happened: bad += 1 expected_balances[tx[1]] += tx[4] expected_balances[tx[2]] -= tx[4] else: good += 1 return (good, bad) good, bad = revert_txs() if expected_balances == get_balances(): # reverting helped logging.info("REVERTING HELPED, TX EXECUTED: %s, TX LOST: %s" % (good, bad)) bad_ratio = bad / (good + bad) if bad_ratio > rolling_tolerance: rolling_tolerance -= bad_ratio - rolling_tolerance if rolling_tolerance < 0: assert False else: rolling_tolerance = tx_tolerance min_balances = [x - MAX_STAKE for x in expected_balances] tx_count = 0 mode = 0 last_tx_set = [] else: # still no match, fail logging.info( "REVERTING DIDN'T HELP, TX EXECUTED: %s, TX LOST: %s" % (good, bad)) assert False, "Balances didn't update in time. Expected: %s, received: %s" % ( expected_balances, get_balances()) last_iter_switch = time.time() if mode == 0: from_ = random.randint(0, len(nodes) - 1) while min_balances[from_] < 0: from_ = random.randint(0, len(nodes) - 1) to = random.randint(0, len(nodes) - 1) while from_ == to: to = random.randint(0, len(nodes) - 1) amt = random.randint(0, min_balances[from_]) nonce_val, nonce_lock = nonces[from_] hash_, _ = get_recent_hash(nodes[-1], 15) with nonce_lock: tx = sign_payment_tx(nodes[from_].signer_key, 'test%s' % to, amt, nonce_val.value, base58.b58decode(hash_.encode('utf8'))) # Loop trying to send the tx to all the validators, until at least one receives it tx_hash = None for send_attempt in range(SEND_TX_ATTEMPTS): shuffled_validator_ids = [x for x in validator_ids] random.shuffle(shuffled_validator_ids) for validator_id in shuffled_validator_ids: try: info = nodes[validator_id].send_tx(tx) if 'error' in info and info['error']['data'] == 'IsSyncing': pass elif 'result' in info: tx_hash = info['result'] break else: assert False, info except (requests.exceptions.ReadTimeout, requests.exceptions.ConnectionError): if not network_issues_expected and not nodes[ validator_id].mess_with: raise if tx_hash is not None: break time.sleep(1) else: assert False, "Failed to send the transation after %s attempts" % SEND_TX_ATTEMPTS last_tx_set.append((tx, from_, to, tx_hash, amt)) nonce_val.value = nonce_val.value + 1 expected_balances[from_] -= amt expected_balances[to] += amt min_balances[from_] -= amt tx_count += 1 else: if get_balances() == expected_balances: logging.info("BALANCES CAUGHT UP, BACK TO SPAMMING TXS") min_balances = [x - MAX_STAKE for x in expected_balances] tx_count = 0 mode = 0 rolling_tolerance = tx_tolerance last_tx_set = [] if mode == 1: time.sleep(1) elif mode == 0: time.sleep(0.1)
target_height = 60 while cur_height < target_height: status = nodes[0].get_status() cur_height = status['sync_info']['latest_block_height'] time.sleep(1) genesis_block = nodes[0].json_rpc('block', [0]) genesis_hash = genesis_block['result']['header']['hash'] nodes[1].start(nodes[1].node_key.pk, nodes[1].addr()) tracker = LogTracker(nodes[1]) time.sleep(1) start_time = time.time() node1_height = 0 nonce = 1 while node1_height <= cur_height: if time.time() - start_time > MAX_SYNC_WAIT: assert False, "state sync timed out" if nonce % 5 == 0: status1 = nodes[1].get_status() logger.info(status1) node1_height = status1['sync_info']['latest_block_height'] tx = sign_payment_tx(nodes[0].signer_key, 'test1', 1, nonce, base58.b58decode(genesis_hash.encode('utf8'))) nodes[1].send_tx(tx) nonce += 1 time.sleep(0.05) assert tracker.check('transition to State Sync')
[["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 10], ["chunk_producer_kickout_threshold", 10]], {1: node1_config}) time.sleep(2) nodes[1].kill() logger.info('node1 is killed') cur_height, _ = utils.wait_for_blocks(nodes[0], target=60) genesis_block = nodes[0].json_rpc('block', [0]) genesis_hash = genesis_block['result']['header']['hash'] genesis_hash = base58.b58decode(genesis_hash.encode('ascii')) nodes[1].start(boot_node=nodes[1]) tracker = utils.LogTracker(nodes[1]) time.sleep(1) start_time = time.time() node1_height = 0 nonce = 1 while node1_height <= cur_height: if time.time() - start_time > MAX_SYNC_WAIT: assert False, "state sync timed out" if nonce % 5 == 0: node1_height = nodes[1].get_latest_block(verbose=True).height tx = sign_payment_tx(nodes[0].signer_key, 'test1', 1, nonce, genesis_hash) nodes[1].send_tx(tx) nonce += 1 time.sleep(0.05) assert tracker.check('transition to State Sync')