def test_call_fee(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.PassesUInt deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, 3) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) assert client_contract.value.call() == 0 callKey = alarm.getLastCallKey.call() assert callKey is not None owner = '0xd3cda913deb6f67967b99d67acdfa1712c293601' assert alarm.getCallFee.call(callKey) == 0 assert alarm.accountBalances.call(owner) == 0 wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) balance = alarm.accountBalances.call(owner) assert balance > 0 assert alarm.getCallFee.call(callKey) == balance
def test_getting_called_at_block(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.NoArgs deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address) wait_for_transaction(rpc_client, txn_hash) assert client_contract.value.call() is False callKey = alarm.getLastCallKey.call() assert callKey is not None assert alarm.getCallGasUsed.call(callKey) == 0 wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(rpc_client, call_txn_hash) call_txn = rpc_client.get_transaction_by_hash(call_txn_hash) assert client_contract.value.call() is True assert alarm.getCallCalledAtBlock.call(callKey) == int(call_txn['blockNumber'], 16)
def test_scheduled_call_execution_without_pool(geth_node, geth_coinbase, rpc_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.SpecifyBlock caller_pool = contracts.CallerPool(alarm.getCallerPoolAddress.call(), rpc_client) deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) target_block = rpc_client.get_block_number() + 45 txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, target_block) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) callKey = alarm.getLastCallKey.call() assert callKey is not None pool_manager = PoolManager(caller_pool) scheduled_call = ScheduledCall(alarm, pool_manager, callKey) assert pool_manager.in_active_pool is False scheduled_call.execute_async() wait_for_block(rpc_client, scheduled_call.target_block + 4, 180) assert pool_manager.in_active_pool is False assert scheduled_call.txn_hash assert scheduled_call.txn_receipt assert scheduled_call.txn assert alarm.checkIfCalled.call(scheduled_call.call_key) assert scheduled_call.was_called assert scheduled_call.target_block <= scheduled_call.called_at_block assert scheduled_call.called_at_block <= scheduled_call.target_block + scheduled_call.grace_period
def test_entering_pool(geth_node, geth_coinbase, rpc_client, deployed_contracts): caller_pool = deployed_contracts.CallerPool assert caller_pool.callerBonds.call(geth_coinbase) == 0 deposit_amount = caller_pool.getMinimumBond.call() * 10 txn_1_hash = caller_pool.depositBond.sendTransaction(value=deposit_amount) wait_for_transaction(rpc_client, txn_1_hash) assert caller_pool.isInAnyPool.call() is False assert caller_pool.canEnterPool.call() is True wait_for_transaction(rpc_client, caller_pool.enterPool.sendTransaction()) # Now queued to be in the next pool. assert caller_pool.getActivePoolKey.call() == 0 assert caller_pool.isInAnyPool.call() is True assert caller_pool.canEnterPool.call() is False next_pool_key = caller_pool.getNextPoolKey.call() assert next_pool_key > 0 assert caller_pool.isInPool.call(next_pool_key) is True wait_for_block(rpc_client, next_pool_key, 180) assert caller_pool.isInAnyPool.call() is True assert caller_pool.canEnterPool.call() is False
def test_extra_call_gas_constant_when_gas_price_lower(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.PassesInt deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, -12345) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) callKey = alarm.getLastCallKey.call() assert callKey is not None base_gas_price = alarm.getCallBaseGasPrice.call(callKey) wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey, gas_price=base_gas_price - 10) call_txn_receipt = wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) assert alarm.checkIfCalled.call(callKey) is True recorded_gas_used = alarm.getCallGasUsed.call(callKey) actual_gas_used = int(call_txn_receipt['gasUsed'], 16) try: assert actual_gas_used == recorded_gas_used except AssertionError: assert actual_gas_used == recorded_gas_used + 64
def test_cost_of_duplicate_call(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.NoArgs deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) assert client_contract.value.call() is False callKey = alarm.getLastCallKey.call() assert callKey is not None assert alarm.checkIfCalled.call(callKey) is False wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey) call_txn_receipt = wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) assert alarm.checkIfCalled.call(callKey) is True call_txn_hash = alarm.doCall.sendTransaction(callKey) call_txn_receipt = wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) assert call_txn_receipt['gasUsed'] == '0x67f0'
def test_caller_payout(geth_node, geth_coinbase, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.PassesUInt deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, 3) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) assert client_contract.value.call() == 0 callKey = alarm.getLastCallKey.call() assert callKey is not None assert alarm.getCallPayout.call(callKey) == 0 assert alarm.accountBalances.call(geth_coinbase) == 0 wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) gas_used = alarm.getCallGasUsed.call(callKey) gas_price = alarm.getCallGasPrice.call(callKey) base_gas_price = alarm.getCallBaseGasPrice.call(callKey) scalar = 100 * base_gas_price / (abs(gas_price - base_gas_price) + base_gas_price) expected_payout = gas_used * gas_price * scalar * 101 / 10000 balance = alarm.accountBalances.call(geth_coinbase) assert balance == expected_payout assert alarm.getCallPayout.call(callKey) == balance
def test_call_fee(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.PassesUInt deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, 3) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) assert client_contract.value.call() == 0 callKey = alarm.getLastCallKey.call() assert callKey is not None before_balance = alarm.accountBalances.call(client_contract._meta.address) wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) after_balance = alarm.accountBalances.call(client_contract._meta.address) fee = alarm.getCallFee.call(callKey) payout = alarm.getCallPayout.call(callKey) assert after_balance == before_balance - payout - fee
def test_scheduled_call_execution_without_pool(geth_node, geth_node_config, deploy_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.SpecifyBlock deposit_amount = get_max_gas(deploy_client) * deploy_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) target_block = deploy_client.get_block_number() + 45 txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, target_block) wait_for_transaction(deploy_client, txn_hash) call_key = alarm.getLastCallKey() assert call_key is not None scheduled_call = ScheduledCall(alarm, call_key) block_sage = scheduled_call.block_sage pool_manager = PoolManager(alarm, block_sage=block_sage) time.sleep(1) assert pool_manager.in_any_pool is False scheduled_call.execute_async() # let the scheduled call do it's thing. assert block_sage.current_block_number < target_block wait_till = scheduled_call.target_block + 10 wait_for_block( deploy_client, wait_till, 2 * block_sage.estimated_time_to_block(wait_till), ) for i in range(5): if scheduled_call.txn_hash: break time.sleep(block_sage.block_time) assert scheduled_call.txn_hash assert scheduled_call.txn_receipt assert scheduled_call.txn assert alarm.checkIfCalled(scheduled_call.call_key) assert scheduled_call.was_called assert scheduled_call.target_block <= scheduled_call.called_at_block assert scheduled_call.called_at_block <= scheduled_call.target_block + scheduled_call.grace_period
def test_scheduled_call_execution_without_pool( geth_node, geth_node_config, deploy_client, deployed_contracts, contracts ): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.SpecifyBlock deposit_amount = get_max_gas(deploy_client) * deploy_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) target_block = deploy_client.get_block_number() + 45 txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, target_block) wait_for_transaction(deploy_client, txn_hash) call_key = alarm.getLastCallKey() assert call_key is not None scheduled_call = ScheduledCall(alarm, call_key) block_sage = scheduled_call.block_sage pool_manager = PoolManager(alarm, block_sage=block_sage) time.sleep(1) assert pool_manager.in_any_pool is False scheduled_call.execute_async() # let the scheduled call do it's thing. assert block_sage.current_block_number < target_block wait_till = scheduled_call.target_block + 10 wait_for_block(deploy_client, wait_till, 2 * block_sage.estimated_time_to_block(wait_till)) for i in range(5): if scheduled_call.txn_hash: break time.sleep(block_sage.block_time) assert scheduled_call.txn_hash assert scheduled_call.txn_receipt assert scheduled_call.txn assert alarm.checkIfCalled(scheduled_call.call_key) assert scheduled_call.was_called assert scheduled_call.target_block <= scheduled_call.called_at_block assert scheduled_call.called_at_block <= scheduled_call.target_block + scheduled_call.grace_period
def test_scheduler(geth_node, geth_node_config, deploy_client, deployed_contracts, contracts): block_sage = BlockSage(deploy_client) alarm = deployed_contracts.Alarm client_contract = deployed_contracts.SpecifyBlock deposit_amount = get_max_gas( deploy_client) * deploy_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) anchor_block = deploy_client.get_block_number() blocks = (1, 4, 4, 8, 30, 40, 50, 60) call_keys = [] for n in blocks: wait_for_transaction( deploy_client, client_contract.scheduleIt.sendTransaction(alarm._meta.address, anchor_block + 100 + n)) last_call_key = alarm.getLastCallKey() assert last_call_key is not None call_keys.append(last_call_key) pool_manager = PoolManager(alarm, block_sage) scheduler = Scheduler(alarm, pool_manager, block_sage=block_sage) scheduler.monitor_async() final_block = anchor_block + 100 + 70 wait_for_block( deploy_client, final_block, 2 * block_sage.estimated_time_to_block(final_block), ) scheduler.stop() block_sage.stop() results = [alarm.checkIfCalled(k) for k in call_keys] assert all(results)
def test_scheduled_call_python_object(geth_node, geth_coinbase, rpc_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm caller_pool = contracts.CallerPool(alarm.getCallerPoolAddress.call(), rpc_client) client_contract = deployed_contracts.PassesUInt deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, 3) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) txn = rpc_client.get_transaction_by_hash(txn_hash) assert client_contract.value.call() == 0 callKey = alarm.getLastCallKey.call() assert callKey is not None owner = '0xd3cda913deb6f67967b99d67acdfa1712c293601' wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) txn_receipt = wait_for_transaction(alarm._meta.rpc_client, alarm.doCall.sendTransaction(callKey)) call_txn = rpc_client.get_transaction_by_hash(txn_receipt['transactionHash']) pool_manager = PoolManager(caller_pool) scheduled_call = ScheduledCall(alarm, pool_manager, callKey) assert scheduled_call.scheduler_account_balance == alarm.accountBalances.call(client_contract._meta.address) assert scheduled_call.target_block == alarm.getCallTargetBlock.call(callKey) assert scheduled_call.scheduled_by == client_contract._meta.address assert scheduled_call.called_at_block == int(txn_receipt['blockNumber'], 16) assert scheduled_call.contract_address == client_contract._meta.address assert scheduled_call.base_gas_price == int(txn['gasPrice'], 16) assert scheduled_call.gas_price == int(call_txn['gasPrice'], 16) try: assert scheduled_call.gas_used == int(txn_receipt['gasUsed'], 16) except AssertionError: assert scheduled_call.gas_used == int(txn_receipt['gasUsed'], 16) + 64 assert scheduled_call.payout == alarm.accountBalances.call(geth_coinbase) == alarm.getCallPayout.call(callKey) assert scheduled_call.fee == alarm.accountBalances.call(owner) == alarm.getCallFee.call(callKey) assert scheduled_call.abi_signature == client_contract.doIt.encoded_abi_function_signature == alarm.getCallABISignature.call(callKey) assert scheduled_call.is_cancelled is False assert scheduled_call.was_called is True assert scheduled_call.was_successful is True assert utils.encode_hex(scheduled_call.data_hash) == 'c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b'
def test_executing_scheduled_call_with_int(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.PassesInt deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, -12345) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) assert client_contract.value.call() == 0 callKey = alarm.getLastCallKey.call() assert callKey is not None wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) assert client_contract.value.call() == -12345
def test_executing_scheduled_call_with_address(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.PassesAddress deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) address = '0xc948453368e5ddc7bc00bb52b5809138217a068d' txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, address) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) assert client_contract.value.call() == '0x0000000000000000000000000000000000000000' callKey = alarm.getLastCallKey.call() assert callKey is not None wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) assert client_contract.value.call() == address
def test_exiting_pool(geth_node, geth_coinbase, rpc_client, deployed_contracts): caller_pool = deployed_contracts.CallerPool assert caller_pool.callerBonds.call(geth_coinbase) == 0 deposit_amount = caller_pool.getMinimumBond.call() * 10 txn_1_hash = caller_pool.depositBond.sendTransaction(value=deposit_amount) wait_for_transaction(rpc_client, txn_1_hash) assert caller_pool.getActivePoolKey.call() == 0 assert caller_pool.getNextPoolKey.call() == 0 assert caller_pool.isInAnyPool.call(geth_coinbase) is False assert caller_pool.canEnterPool.call(geth_coinbase) is True assert caller_pool.canExitPool.call(geth_coinbase) is False wait_for_transaction(rpc_client, caller_pool.enterPool.sendTransaction()) first_pool_key = caller_pool.getNextPoolKey.call() wait_for_block(rpc_client, first_pool_key, 180) assert caller_pool.getActivePoolKey.call() == first_pool_key assert caller_pool.getNextPoolKey.call() == 0 assert caller_pool.isInAnyPool.call(geth_coinbase) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is True assert caller_pool.canEnterPool.call(geth_coinbase) is False assert caller_pool.canExitPool.call(geth_coinbase) is True wait_for_transaction(rpc_client, caller_pool.exitPool.sendTransaction()) second_pool_key = caller_pool.getNextPoolKey.call() assert second_pool_key > first_pool_key assert caller_pool.isInAnyPool.call(geth_coinbase) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is True wait_for_block(rpc_client, second_pool_key, 180) assert caller_pool.getActivePoolKey.call() == second_pool_key assert caller_pool.getNextPoolKey.call() == 0 assert caller_pool.isInAnyPool.call(geth_coinbase) is False assert caller_pool.canEnterPool.call(geth_coinbase) is True assert caller_pool.canExitPool.call(geth_coinbase) is False
def test_infinite_loop_protection(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.InfiniteLoop deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) rpc_client.send_transaction(to=client_contract._meta.address, value=1000000000) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) callKey = alarm.getLastCallKey.call() assert callKey is not None wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 300) call_txn_hash = alarm.doCall.sendTransaction(callKey) call_txn_receipt = wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) call_txn = rpc_client.get_transaction_by_hash(call_txn_hash) assert alarm.checkIfCalled.call(callKey) is True assert alarm.checkIfSuccess.call(callKey) is False assert call_txn_receipt['gasUsed'] == call_txn['gas']
def test_funds_are_locked_during_execution(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.WithdrawsDuringCall wait_for_transaction(rpc_client, client_contract.setAlarm.sendTransaction(alarm._meta.address)) deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction() wait_for_transaction(client_contract._meta.rpc_client, txn_hash) assert client_contract.wasCalled.call() is False callKey = alarm.getLastCallKey.call() assert callKey is not None pre_balance = client_contract.getAlarmBalance.call() assert pre_balance == deposit_amount wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 120) call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) fee = alarm.getCallFee.call(callKey) payout = alarm.getCallPayout.call(callKey) withdrawn_amount = client_contract.withdrawAmount.call() assert all(v > 0 for v in (fee, payout, withdrawn_amount)) post_balance = client_contract.getAlarmBalance.call() # Sanity check that an underflow error didn't occur assert post_balance < pre_balance # During the call, the WithdrawsDuringCall contract tries to withdraw it's # entire account balance. This should only leave the max call cost minus # fees left in the account. assert post_balance == pre_balance - fee - payout - withdrawn_amount assert alarm.checkIfCalled.call(callKey) is True
def test_check_if_call_successful_for_failed_call(geth_node, rpc_client, deployed_contracts): alarm = deployed_contracts.Alarm client_contract = deployed_contracts.InfiniteLoop deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) callKey = alarm.getLastCallKey.call() assert callKey is not None assert alarm.checkIfCalled.call(callKey) is False assert alarm.checkIfSuccess.call(callKey) is False wait_for_block(rpc_client, alarm.getCallTargetBlock.call(callKey), 300) call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) assert alarm.checkIfCalled.call(callKey) is True assert alarm.checkIfSuccess.call(callKey) is False
def test_scheduler(geth_node, geth_node_config, deploy_client, deployed_contracts, contracts): block_sage = BlockSage(deploy_client) alarm = deployed_contracts.Alarm client_contract = deployed_contracts.SpecifyBlock deposit_amount = get_max_gas(deploy_client) * deploy_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) anchor_block = deploy_client.get_block_number() blocks = (1, 4, 4, 8, 30, 40, 50, 60) call_keys = [] for n in blocks: wait_for_transaction(deploy_client, client_contract.scheduleIt.sendTransaction(alarm._meta.address, anchor_block + 100 + n)) last_call_key = alarm.getLastCallKey() assert last_call_key is not None call_keys.append(last_call_key) pool_manager = PoolManager(alarm, block_sage) scheduler = Scheduler(alarm, pool_manager, block_sage=block_sage) scheduler.monitor_async() final_block = anchor_block + 100 + 70 wait_for_block( deploy_client, final_block, 2 * block_sage.estimated_time_to_block(final_block), ) scheduler.stop() block_sage.stop() results = [alarm.checkIfCalled(k) for k in call_keys] assert all(results)
def test_pool_manager(geth_node, geth_coinbase, rpc_client, deployed_contracts): caller_pool = deployed_contracts.CallerPool deposit_amount = caller_pool.getMinimumBond.call() * 10 # Put in our bond wait_for_transaction( rpc_client, caller_pool.depositBond.sendTransaction(value=deposit_amount) ) pool_manager = PoolManager(caller_pool) assert pool_manager.in_any_pool is False pool_manager.monitor_async() wait_for_block(rpc_client, rpc_client.get_block_number() + 5, 180) first_pool_number = pool_manager.next_pool assert first_pool_number > 0 assert pool_manager.in_any_pool is True assert pool_manager.in_next_pool is True wait_for_block(rpc_client, first_pool_number + 5, 180) # Now leave the pool (like we got kicked out) wait_for_transaction(rpc_client, caller_pool.exitPool.sendTransaction()) second_pool_number = pool_manager.next_pool assert second_pool_number > first_pool_number # we should not be in the next pool since we left it. assert pool_manager.in_next_pool is False wait_for_block(rpc_client, second_pool_number + 5, 180) # pool manager should have rejoined now that the next pool is live and we # aren't in a freeze. third_pool_number = pool_manager.next_pool assert third_pool_number > second_pool_number assert pool_manager.in_active_pool is False assert pool_manager.in_next_pool is True
def test_rotating_tree_right(geth_node, rpc_client, deployed_contracts): """ Before right rotation ==================== 1 \ 15 / \ 4 \ / \ \ 3 7 \ 1800 / \ / \ 1700 2000 / \ / \ / \ 1900 2001 After right rotation ==================== 15 / \ / \ / \ 1 1800 \ / \ 4 / \ / \ 1700 2000 3 7 / \ 1900 2001 """ initial_state = { (2001, (None, None)), (1900, (None, None)), (1700, (None, None)), (4, (3, 7)), (3, (None, None)), (7, (None, None)), (1, (None, 15)), (15, (4, 1800)), (1800, (1700, 2000)), (2000, (1900, 2001)), } alarm = deployed_contracts.Alarm client_contract = deployed_contracts.SpecifyBlock anchor_block = rpc_client.get_block_number() blocks = (2000, 1800, 2001, 15, 1700, 1900, 1, 4, 3, 7) call_keys = [] for n in blocks: wait_for_transaction(rpc_client, client_contract.scheduleIt.sendTransaction(alarm._meta.address, anchor_block + 65 + n)) last_call_key = alarm.getLastCallKey.call() assert last_call_key is not None call_keys.append(last_call_key) assert len(set(call_keys)) == len(blocks) calls_to_blocks = dict(zip(call_keys, blocks)) calls_to_blocks[None] = None initial_state = { (2001, (None, None)), (1900, (None, None)), (1700, (None, None)), (4, (3, 7)), (3, (None, None)), (7, (None, None)), (1, (None, 15)), (15, (4, 1800)), (1800, (1700, 2000)), (2000, (1900, 2001)), } assert get_tree_state(alarm, calls_to_blocks) == initial_state wait_for_block(rpc_client, anchor_block + 65 + 16, max_wait=180) wait_for_transaction(rpc_client, alarm.rotateTree.sendTransaction()) expected_state = { (1700, (None, None)), (2001, (None, None)), (3, (None, None)), (1, (None, 4)), (4, (3, 7)), (15, (1, 1800)), (2000, (1900, 2001)), (1900, (None, None)), (7, (None, None)), (1800, (1700, 2000)), } assert get_tree_state(alarm, calls_to_blocks) == expected_state
def test_call_window_divided_between_callers(geth_node, geth_coinbase, rpc_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm caller_pool = contracts.CallerPool(alarm.getCallerPoolAddress.call(), rpc_client) joiner = deployed_contracts.JoinsPool client_contract = deployed_contracts.NoArgs # Put in our deposit with the alarm contract. deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) wait_for_transaction(rpc_client, joiner.setCallerPool.sendTransaction(caller_pool._meta.address)) assert caller_pool.callerBonds.call(geth_coinbase) == 0 deposit_amount = caller_pool.getMinimumBond.call() * 10 # Put in our bond wait_for_transaction( rpc_client, caller_pool.depositBond.sendTransaction(value=deposit_amount) ) # Put the contract's bond in wait_for_transaction( rpc_client, rpc_client.send_transaction(to=joiner._meta.address, value=deposit_amount) ) wait_for_transaction( rpc_client, joiner.deposit.sendTransaction(deposit_amount) ) # Both join the pool wait_for_transaction(rpc_client, joiner.enter.sendTransaction()) wait_for_transaction(rpc_client, caller_pool.enterPool.sendTransaction()) # New pool is formed but not active first_pool_key = caller_pool.getNextPoolKey.call() assert first_pool_key > 0 # Wait for it to become active wait_for_block(rpc_client, first_pool_key, 180) # We should both be in the pool assert caller_pool.getActivePoolKey.call() == first_pool_key assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is True # Schedule the function call. txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) callKey = alarm.getLastCallKey.call() assert callKey is not None target_block = alarm.getCallTargetBlock.call(callKey) grace_period = alarm.getCallGracePeriod.call(callKey) callers = [ caller_pool.getCallerFromPool.call(callKey, target_block, grace_period, target_block + n) for n in range(grace_period) ] caller_a = callers[0] caller_b = callers[4] assert {geth_coinbase, joiner._meta.address, '0x0000000000000000000000000000000000000000'} == set(callers) call_on_block = None for i, caller in enumerate(callers): if i / 4 + 2 > len(callers) / 4: assert caller == '0x0000000000000000000000000000000000000000' elif (i / 4) % 2 == 0: assert caller == caller_a else: assert caller == caller_b if call_on_block is None and i > 4 and caller == geth_coinbase: call_on_block = target_block + i * 4 wait_for_block(rpc_client, call_on_block, 240) assert caller_pool.getNextPoolKey.call() == 0 before_balance = caller_pool.callerBonds.call(joiner._meta.address) assert alarm.checkIfCalled.call(callKey) is False call_txn_hash = alarm.doCall.sendTransaction(callKey) call_txn_receipt = wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) call_txn = rpc_client.get_transaction_by_hash(call_txn_hash) call_block = rpc_client.get_block_by_hash(call_txn_receipt['blockHash']) after_balance = caller_pool.callerBonds.call(joiner._meta.address) assert alarm.checkIfCalled.call(callKey) is True assert after_balance < before_balance minimum_bond = int(call_block['gasLimit'], 16) * int(call_txn['gasPrice'], 16) assert after_balance == before_balance - minimum_bond next_pool_key = caller_pool.getNextPoolKey.call() assert next_pool_key > 0 assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(joiner._meta.address, next_pool_key) is False
def test_call_window_divided_between_callers(geth_node, geth_coinbase, rpc_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm caller_pool = contracts.CallerPool(alarm.getCallerPoolAddress.call(), rpc_client) joiner = deployed_contracts.JoinsPool client_contract = deployed_contracts.NoArgs # Put in our deposit with the alarm contract. deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) wait_for_transaction(rpc_client, joiner.setCallerPool.sendTransaction(caller_pool._meta.address)) assert caller_pool.callerBonds.call(geth_coinbase) == 0 deposit_amount = caller_pool.getMinimumBond.call() * 10 # Put in our bond wait_for_transaction( rpc_client, caller_pool.depositBond.sendTransaction(value=deposit_amount) ) # Put the contract's bond in wait_for_transaction( rpc_client, rpc_client.send_transaction(to=joiner._meta.address, value=deposit_amount) ) wait_for_transaction( rpc_client, joiner.deposit.sendTransaction(deposit_amount) ) # Both join the pool wait_for_transaction(rpc_client, joiner.enter.sendTransaction()) wait_for_transaction(rpc_client, caller_pool.enterPool.sendTransaction()) # New pool is formed but not active first_pool_key = caller_pool.getNextPoolKey.call() assert first_pool_key > 0 # Wait for it to become active wait_for_block(rpc_client, first_pool_key, 180) # We should both be in the pool assert caller_pool.getActivePoolKey.call() == first_pool_key assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is True # Schedule the function call. for _ in range(5): txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) callKey = alarm.getLastCallKey.call() assert callKey is not None target_block = alarm.getCallTargetBlock.call(callKey) grace_period = alarm.getCallGracePeriod.call(callKey) first_caller = caller_pool.getDesignatedCaller.call(callKey, target_block, grace_period, target_block) if first_caller == geth_coinbase: break else: raise ValueError("Was never first caller") wait_for_block(rpc_client, target_block, 240) before_balance = caller_pool.callerBonds.call(joiner._meta.address) assert alarm.checkIfCalled.call(callKey) is False call_txn_hash = alarm.doCall.sendTransaction(callKey) wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) after_balance = caller_pool.callerBonds.call(joiner._meta.address) assert alarm.checkIfCalled.call(callKey) is True assert after_balance == before_balance
def test_scheduled_call_execution_with_pool(geth_node, geth_coinbase, geth_node_config, deploy_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm joiner = deployed_contracts.JoinsPool client_contract = deployed_contracts.SpecifyBlock coinbase = geth_coinbase block_sage = BlockSage(deploy_client) # Put in our deposit with the alarm contract. deposit_amount = get_max_gas(deploy_client) * deploy_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) wait_for_transaction(deploy_client, joiner.setCallerPool.sendTransaction(alarm._meta.address)) assert alarm.getBondBalance(coinbase) == 0 bond_amount = alarm.getMinimumBond() * 10 # Put in our bond wait_for_transaction( deploy_client, alarm.depositBond.sendTransaction(value=bond_amount) ) # Put the contract's bond in wait_for_transaction( deploy_client, deploy_client.send_transaction(to=joiner._meta.address, value=bond_amount) ) wait_for_transaction( deploy_client, joiner.deposit.sendTransaction(bond_amount) ) # Both join the pool wait_for_transaction(deploy_client, joiner.enter.sendTransaction()) wait_for_transaction(deploy_client, alarm.enterPool.sendTransaction()) # New pool is formed but not active first_generation_id = alarm.getNextGenerationId() assert first_generation_id > 0 # Go ahead and schedule the call. generation_start_at = alarm.getGenerationStartAt(first_generation_id) target_block = generation_start_at + 5 txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, target_block) wait_for_transaction(deploy_client, txn_hash) # Wait for the pool to become active wait_for_block( deploy_client, generation_start_at, 2 * block_sage.estimated_time_to_block(generation_start_at), ) # We should both be in the pool assert alarm.getCurrentGenerationId() == first_generation_id assert alarm.isInGeneration(joiner._meta.address, first_generation_id) is True assert alarm.isInGeneration(coinbase, first_generation_id) is True call_key = alarm.getLastCallKey() assert call_key is not None scheduled_call = ScheduledCall(alarm, call_key, block_sage=block_sage) pool_manager = PoolManager(alarm, block_sage=block_sage) scheduled_call.execute_async() wait_for_block( deploy_client, scheduled_call.target_block, 2 * block_sage.estimated_time_to_block(scheduled_call.target_block), ) for i in range(alarm.getCallWindowSize() * 2): if scheduled_call.txn_hash: break time.sleep(block_sage.block_time) assert scheduled_call.txn_hash assert scheduled_call.txn_receipt assert scheduled_call.txn assert scheduled_call.was_called assert alarm.checkIfCalled(scheduled_call.call_key) assert scheduled_call.target_block <= scheduled_call.called_at_block assert scheduled_call.called_at_block <= scheduled_call.target_block + scheduled_call.grace_period
def test_free_for_all_window_awards_mega_bonus(geth_node, geth_coinbase, rpc_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm caller_pool = contracts.CallerPool(alarm.getCallerPoolAddress.call(), rpc_client) client_contract = deployed_contracts.NoArgs joiner_a = deployed_contracts.JoinsPool deploy_txn = deploy_contract(rpc_client, contracts.JoinsPool, _from=geth_coinbase, gas=get_max_gas(rpc_client)) joiner_b = contracts.JoinsPool(get_contract_address_from_txn(rpc_client, deploy_txn, 30), rpc_client) # Put in our deposit with the alarm contract. deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) wait_for_transaction(rpc_client, joiner_a.setCallerPool.sendTransaction(caller_pool._meta.address)) wait_for_transaction(rpc_client, joiner_b.setCallerPool.sendTransaction(caller_pool._meta.address)) assert caller_pool.callerBonds.call(geth_coinbase) == 0 deposit_amount = caller_pool.getMinimumBond.call() * 10 # Put in our bond wait_for_transaction(rpc_client, caller_pool.depositBond.sendTransaction(value=deposit_amount)) # Put contract A's bond in wait_for_transaction(rpc_client, rpc_client.send_transaction(to=joiner_a._meta.address, value=deposit_amount)) wait_for_transaction(rpc_client, joiner_a.deposit.sendTransaction(deposit_amount)) # Put contract B's bond in wait_for_transaction(rpc_client, rpc_client.send_transaction(to=joiner_b._meta.address, value=deposit_amount)) wait_for_transaction(rpc_client, joiner_b.deposit.sendTransaction(deposit_amount)) # All join the pool wait_for_transaction(rpc_client, joiner_a.enter.sendTransaction()) wait_for_transaction(rpc_client, joiner_b.enter.sendTransaction()) wait_for_transaction(rpc_client, caller_pool.enterPool.sendTransaction()) # New pool is formed but not active first_pool_key = caller_pool.getNextPoolKey.call() assert first_pool_key > 0 # Wait for it to become active wait_for_block(rpc_client, first_pool_key, 240) # We should both be in the pool assert caller_pool.getActivePoolKey.call() == first_pool_key assert caller_pool.isInPool.call(joiner_a._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(joiner_b._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is True # Schedule the function call. txn_hash = client_contract.setGracePeriod.sendTransaction(24) txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) callKey = alarm.getLastCallKey.call() assert callKey is not None target_block = alarm.getCallTargetBlock.call(callKey) grace_period = alarm.getCallGracePeriod.call(callKey) wait_for_block(rpc_client, target_block + grace_period - 4, 300) my_before_balance = caller_pool.callerBonds.call(geth_coinbase) before_balance_a = caller_pool.callerBonds.call(joiner_a._meta.address) before_balance_b = caller_pool.callerBonds.call(joiner_b._meta.address) assert alarm.checkIfCalled.call(callKey) is False call_txn_hash = alarm.doCall.sendTransaction(callKey) call_txn_receipt = wait_for_transaction(alarm._meta.rpc_client, call_txn_hash) call_txn = rpc_client.get_transaction_by_hash(call_txn_hash) call_block = rpc_client.get_block_by_hash(call_txn_receipt["blockHash"]) my_after_balance = caller_pool.callerBonds.call(geth_coinbase) after_balance_a = caller_pool.callerBonds.call(joiner_a._meta.address) after_balance_b = caller_pool.callerBonds.call(joiner_b._meta.address) assert alarm.checkIfCalled.call(callKey) is True assert after_balance_a < before_balance_a assert after_balance_b < before_balance_b minimum_bond = int(call_block["gasLimit"], 16) * int(call_txn["gasPrice"], 16) assert after_balance_a == before_balance_a - minimum_bond assert after_balance_b == before_balance_b - minimum_bond assert my_after_balance > my_before_balance assert my_after_balance == my_before_balance + 2 * minimum_bond assert caller_pool.getNextPoolKey.call() == 0
def test_pool_manager(geth_node, geth_coinbase, rpc_client, deployed_contracts): caller_pool = deployed_contracts.CallerPool pool_manager = PoolManager(caller_pool) # should be no pools, nor anyone in them. assert pool_manager.active_pool == 0 assert pool_manager.next_pool == 0 assert pool_manager.in_any_pool is False assert pool_manager.in_next_pool is False assert pool_manager.in_active_pool is False # check permissions assert pool_manager.can_enter_pool is True assert pool_manager.can_exit_pool is False pool_manager.run_async() # Wait a few blocks for the pool manager to spin up. wait_for_block(rpc_client, rpc_client.get_block_number() + 5, 60) first_pool = pool_manager.next_pool # should have initiated joining the next pool but won't be in it yet. assert pool_manager.active_pool == 0 assert first_pool > 0 assert pool_manager.in_any_pool is True assert pool_manager.in_next_pool is True assert pool_manager.in_active_pool is False # check permissions assert pool_manager.can_enter_pool is False assert pool_manager.can_exit_pool is False # Wait for the pool to become active. wait_for_block(rpc_client, first_pool) # we should now be in the active pool. assert pool_manager.active_pool == first_pool assert pool_manager.next_pool == 0 assert pool_manager.in_any_pool is True assert pool_manager.in_next_pool is False assert pool_manager.in_active_pool is True # check permissions assert pool_manager.can_enter_pool is False assert pool_manager.can_exit_pool is True # Now we manually remove ourselves. wait_for_transaction(rpc_client, caller_pool.exitPool.sendTransaction()) second_pool_key = pool_manager.next_pool # New pool should have been formed. assert second_pool_key > first_pool # Current status should not have changed except that the next pool has now # been formed but we aren't in it. assert pool_manager.in_any_pool is True assert pool_manager.in_next_pool is False assert pool_manager.in_active_pool is True # Wait for the next pool to become active plus a little. wait_for_block(rpc_client, second_pool_key + 5) # The manager should have rejoined. assert pool_manager.active_pool == second_pool_key assert False
def test_pool_membership_frozen_during_transition_period(geth_node, geth_coinbase, rpc_client, deployed_contracts): caller_pool = deployed_contracts.CallerPool joiner = deployed_contracts.JoinsPool wait_for_transaction(rpc_client, joiner.setCallerPool.sendTransaction(caller_pool._meta.address)) assert caller_pool.callerBonds.call(geth_coinbase) == 0 deposit_amount = caller_pool.getMinimumBond.call() * 10 # Put in our bond wait_for_transaction(rpc_client, caller_pool.depositBond.sendTransaction(value=deposit_amount)) # Put the contract's bond in wait_for_transaction(rpc_client, rpc_client.send_transaction(to=joiner._meta.address, value=deposit_amount)) wait_for_transaction(rpc_client, joiner.deposit.sendTransaction(deposit_amount)) wait_for_transaction(rpc_client, joiner.enter.sendTransaction()) # New pool is formed but not active first_pool_key = caller_pool.getNextPoolKey.call() assert first_pool_key > 0 # Only the contract is in the pool. (but it isn't active yet) assert caller_pool.getActivePoolKey.call() == 0 assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is False # Both are in the pool but it isn't active yet assert caller_pool.getActivePoolKey.call() == 0 assert caller_pool.getNextPoolKey.call() == first_pool_key assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is False # Wait for it to become active wait_for_block(rpc_client, first_pool_key, 180) assert caller_pool.getActivePoolKey.call() == first_pool_key assert caller_pool.getNextPoolKey.call() == 0 # Now the contract leaves the pool. wait_for_transaction(rpc_client, joiner.exit.sendTransaction()) second_pool_key = caller_pool.getNextPoolKey.call() # New pool should have been setup assert second_pool_key > first_pool_key # joiner should not be in next pool. assert caller_pool.isInPool.call(joiner._meta.address, second_pool_key) is False # should not be in the pool until it becomes active assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is False # should still be allowed to enter assert caller_pool.canEnterPool.call(geth_coinbase) is True # wait for the pool to become active wait_for_block(rpc_client, second_pool_key - 20, 180) # should no longer be allowed to leave (within freeze window) assert caller_pool.canEnterPool.call(geth_coinbase) is False # wait for the pool to become active wait_for_block(rpc_client, second_pool_key, 180) # should now be allowed to leave. assert caller_pool.canEnterPool.call(geth_coinbase) is True
def test_pool_membership_is_carried_over(geth_node, geth_coinbase, rpc_client, deployed_contracts): caller_pool = deployed_contracts.CallerPool joiner = deployed_contracts.JoinsPool wait_for_transaction(rpc_client, joiner.setCallerPool.sendTransaction(caller_pool._meta.address)) assert caller_pool.callerBonds.call(geth_coinbase) == 0 deposit_amount = caller_pool.getMinimumBond.call() * 10 # Put in our bond wait_for_transaction( rpc_client, caller_pool.depositBond.sendTransaction(value=deposit_amount) ) # Put the contract's bond in wait_for_transaction( rpc_client, rpc_client.send_transaction(to=joiner._meta.address, value=deposit_amount) ) wait_for_transaction( rpc_client, joiner.deposit.sendTransaction(deposit_amount) ) wait_for_transaction(rpc_client, joiner.enter.sendTransaction()) # New pool is formed but not active first_pool_key = caller_pool.getNextPoolKey.call() assert first_pool_key > 0 # Only the contract is in the pool. (but it isn't active yet) assert caller_pool.getActivePoolKey.call() == 0 assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is False # Now we join the pool wait_for_transaction(rpc_client, caller_pool.enterPool.sendTransaction()) # Both are in the pool but it isn't active yet assert caller_pool.getActivePoolKey.call() == 0 assert caller_pool.getNextPoolKey.call() == first_pool_key assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is True # Wait for it to become active wait_for_block(rpc_client, first_pool_key, 180) assert caller_pool.getActivePoolKey.call() == first_pool_key assert caller_pool.getNextPoolKey.call() == 0 # Now the contract leaves the pool. wait_for_transaction(rpc_client, joiner.exit.sendTransaction()) second_pool_key = caller_pool.getNextPoolKey.call() # New pool should have been setup assert second_pool_key > first_pool_key # should still be in the pool until it becomes active assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is True # wait for the pool to become active wait_for_block(rpc_client, second_pool_key, 180) # contract shouldn't be in the pool anymore but we should. assert caller_pool.getActivePoolKey.call() == second_pool_key assert caller_pool.getNextPoolKey.call() == 0 assert caller_pool.isInPool.call(joiner._meta.address, second_pool_key) is False assert caller_pool.isInPool.call(geth_coinbase, second_pool_key) is True
def test_scheduled_call_execution_with_pool(geth_node, geth_coinbase, geth_node_config, deploy_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm joiner = deployed_contracts.JoinsPool client_contract = deployed_contracts.SpecifyBlock coinbase = geth_coinbase block_sage = BlockSage(deploy_client) # Put in our deposit with the alarm contract. deposit_amount = get_max_gas( deploy_client) * deploy_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) wait_for_transaction( deploy_client, joiner.setCallerPool.sendTransaction(alarm._meta.address)) assert alarm.getBondBalance(coinbase) == 0 bond_amount = alarm.getMinimumBond() * 10 # Put in our bond wait_for_transaction(deploy_client, alarm.depositBond.sendTransaction(value=bond_amount)) # Put the contract's bond in wait_for_transaction( deploy_client, deploy_client.send_transaction(to=joiner._meta.address, value=bond_amount)) wait_for_transaction(deploy_client, joiner.deposit.sendTransaction(bond_amount)) # Both join the pool wait_for_transaction(deploy_client, joiner.enter.sendTransaction()) wait_for_transaction(deploy_client, alarm.enterPool.sendTransaction()) # New pool is formed but not active first_generation_id = alarm.getNextGenerationId() assert first_generation_id > 0 # Go ahead and schedule the call. generation_start_at = alarm.getGenerationStartAt(first_generation_id) target_block = generation_start_at + 5 txn_hash = client_contract.scheduleIt.sendTransaction( alarm._meta.address, target_block) wait_for_transaction(deploy_client, txn_hash) # Wait for the pool to become active wait_for_block( deploy_client, generation_start_at, 2 * block_sage.estimated_time_to_block(generation_start_at), ) # We should both be in the pool assert alarm.getCurrentGenerationId() == first_generation_id assert alarm.isInGeneration(joiner._meta.address, first_generation_id) is True assert alarm.isInGeneration(coinbase, first_generation_id) is True call_key = alarm.getLastCallKey() assert call_key is not None scheduled_call = ScheduledCall(alarm, call_key, block_sage=block_sage) pool_manager = PoolManager(alarm, block_sage=block_sage) scheduled_call.execute_async() wait_for_block( deploy_client, scheduled_call.target_block, 2 * block_sage.estimated_time_to_block(scheduled_call.target_block), ) for i in range(alarm.getCallWindowSize() * 2): if scheduled_call.txn_hash: break time.sleep(block_sage.block_time) assert scheduled_call.txn_hash assert scheduled_call.txn_receipt assert scheduled_call.txn assert scheduled_call.was_called assert alarm.checkIfCalled(scheduled_call.call_key) assert scheduled_call.target_block <= scheduled_call.called_at_block assert scheduled_call.called_at_block <= scheduled_call.target_block + scheduled_call.grace_period
def test_scheduled_call_execution_with_pool(geth_node, geth_coinbase, rpc_client, deployed_contracts, contracts): alarm = deployed_contracts.Alarm joiner = deployed_contracts.JoinsPool client_contract = deployed_contracts.SpecifyBlock caller_pool = contracts.CallerPool(alarm.getCallerPoolAddress.call(), rpc_client) # Put in our deposit with the alarm contract. deposit_amount = get_max_gas(rpc_client) * rpc_client.get_gas_price() * 20 alarm.deposit.sendTransaction(client_contract._meta.address, value=deposit_amount) wait_for_transaction(rpc_client, joiner.setCallerPool.sendTransaction(caller_pool._meta.address)) assert caller_pool.callerBonds.call(geth_coinbase) == 0 deposit_amount = caller_pool.getMinimumBond.call() * 10 # Put in our bond wait_for_transaction(rpc_client, caller_pool.depositBond.sendTransaction(value=deposit_amount)) # Put the contract's bond in wait_for_transaction(rpc_client, rpc_client.send_transaction(to=joiner._meta.address, value=deposit_amount)) wait_for_transaction(rpc_client, joiner.deposit.sendTransaction(deposit_amount)) # Both join the pool wait_for_transaction(rpc_client, joiner.enter.sendTransaction()) wait_for_transaction(rpc_client, caller_pool.enterPool.sendTransaction()) # New pool is formed but not active first_pool_key = caller_pool.getNextPoolKey.call() assert first_pool_key > 0 # Go ahead and schedule the call. target_block = first_pool_key + 5 txn_hash = client_contract.scheduleIt.sendTransaction(alarm._meta.address, target_block) wait_for_transaction(client_contract._meta.rpc_client, txn_hash) # Wait for the pool to become active wait_for_block(rpc_client, first_pool_key, 180) # We should both be in the pool assert caller_pool.getActivePoolKey.call() == first_pool_key assert caller_pool.isInPool.call(joiner._meta.address, first_pool_key) is True assert caller_pool.isInPool.call(geth_coinbase, first_pool_key) is True callKey = alarm.getLastCallKey.call() assert callKey is not None pool_manager = PoolManager(caller_pool) scheduled_call = ScheduledCall(alarm, pool_manager, callKey) scheduled_call.execute_async() wait_for_block(rpc_client, scheduled_call.target_block + 8, 180) assert scheduled_call.txn_hash assert scheduled_call.txn_receipt assert scheduled_call.txn assert scheduled_call.was_called assert alarm.checkIfCalled.call(scheduled_call.call_key) assert scheduled_call.target_block <= scheduled_call.called_at_block assert scheduled_call.called_at_block <= scheduled_call.target_block + scheduled_call.grace_period