def test_challenge_standard_exit_wrong_oindex_should_fail(testlang): from plasma_core.utils.transactions import decode_utxo_id, encode_utxo_id from plasma_core.transaction import Transaction alice, bob, alice_money, bob_money = testlang.accounts[ 0], testlang.accounts[1], 10, 90 deposit_id = testlang.deposit(alice, alice_money + bob_money) deposit_blknum, _, _ = decode_utxo_id(deposit_id) spend_tx = Transaction(inputs=[decode_utxo_id(deposit_id)], outputs=[(alice.address, NULL_ADDRESS, alice_money), (bob.address, NULL_ADDRESS, bob_money)]) spend_tx.sign(0, alice.key) blknum = testlang.submit_block([spend_tx]) alice_utxo = encode_utxo_id(blknum, 0, 0) bob_utxo = encode_utxo_id(blknum, 0, 1) testlang.start_standard_exit(alice_utxo, alice.key) bob_spend_id = testlang.spend_utxo([bob_utxo], [bob.key], outputs=[(bob.address, NULL_ADDRESS, bob_money)]) alice_spend_id = testlang.spend_utxo([alice_utxo], [alice.key], outputs=[(alice.address, NULL_ADDRESS, alice_money)]) with pytest.raises(TransactionFailed): testlang.challenge_standard_exit(alice_utxo, bob_spend_id) testlang.challenge_standard_exit(alice_utxo, alice_spend_id)
def test_should_not_allow_to_withdraw_outputs_from_two_ifes_marked_as_canonical_but_sharing_an_input(testlang, plasma_framework, token): alice, amount_token = testlang.accounts[0], 200 caroline, amount_eth = testlang.accounts[1], 100 deposit_id_token = testlang.deposit_token(alice, token, amount_token) deposit_id_eth = testlang.deposit(caroline, amount_eth) swap_tx_id = testlang.spend_utxo( [deposit_id_token, deposit_id_eth], [alice, caroline], [(alice.address, NULL_ADDRESS, amount_eth), (caroline.address, token.address, amount_token)]) # in-flight transaction not included in Plasma steal_tx = Transaction(inputs=[decode_utxo_id(deposit_id_eth)], outputs=[(caroline.address, NULL_ADDRESS, amount_eth)]) steal_tx.sign(0, caroline, verifying_contract=testlang.root_chain.plasma_framework) testlang.start_in_flight_exit(swap_tx_id) testlang.start_in_flight_exit(None, spend_tx=steal_tx) caroline_token_balance_before = token.balanceOf(caroline.address) caroline_eth_balance_before = testlang.get_balance(caroline) testlang.piggyback_in_flight_exit_output(swap_tx_id, 1, caroline) testlang.piggyback_in_flight_exit_output(None, 0, caroline, spend_tx=steal_tx) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) testlang.process_exits(token.address, 0, 1) testlang.process_exits(NULL_ADDRESS, 0, 1) # caroline exits with the tokens (previously owned by alice) caroline_token_balance = token.balanceOf(caroline.address) assert caroline_token_balance == caroline_token_balance_before + amount_token # but she can not exit with Eth caroline_eth_balance = testlang.get_balance(caroline) assert caroline_eth_balance == caroline_eth_balance_before
def test_challenge_standard_exit_with_in_flight_exit_tx_should_succeed( ethtester, testlang): # exit cross-spend test, cases 3 and 4 owner, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit(owner, amount) spend_id = testlang.spend_utxo([deposit_id], [owner.key], outputs=[(owner.address, NULL_ADDRESS, amount)]) ife_tx = Transaction(inputs=[decode_utxo_id(spend_id)], outputs=[(owner.address, NULL_ADDRESS, amount)]) ife_tx.sign(0, owner.key) (encoded_spend, encoded_inputs, proofs, signatures) = testlang.get_in_flight_exit_info(None, spend_tx=ife_tx) bond = testlang.root_chain.inFlightExitBond() testlang.root_chain.startInFlightExit(encoded_spend, encoded_inputs, proofs, signatures, value=bond, sender=owner.key) testlang.start_standard_exit(spend_id, owner.key) assert testlang.get_standard_exit(spend_id).amount == 100 exit_id = testlang.get_standard_exit_id(spend_id) # FIXME a proper way of getting encoded body of IFE tx is to get it out of generated events testlang.root_chain.challengeStandardExit(exit_id, ife_tx.encoded, 0, ife_tx.signatures[0]) assert testlang.get_standard_exit(spend_id) == [ NULL_ADDRESS_HEX, NULL_ADDRESS_HEX, 0, 0 ]
def test_not_canonial_in_flight_exit_processed_successfully(testlang, plasma_framework): owner, deposit_1_amount, deposit_2_amount = testlang.accounts[0], 100, 200 deposit_id_1 = testlang.deposit(owner, deposit_1_amount) deposit_id_2 = testlang.deposit(owner, deposit_2_amount) starting_vault_balance = testlang.get_balance(plasma_framework.eth_vault) amount_spent = 100 spend_deposit_2_id = testlang.spend_utxo([deposit_id_2], [owner], outputs=[(owner.address, NULL_ADDRESS, amount_spent)]) testlang.start_standard_exit(spend_deposit_2_id, account=owner) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) testlang.process_exits(NULL_ADDRESS, 0, 1) vault_balance = testlang.get_balance(plasma_framework.eth_vault) assert vault_balance == starting_vault_balance - amount_spent # in-flight transaction not included in Plasma inputs = [decode_utxo_id(deposit_id_1), decode_utxo_id(deposit_id_2)] spend_deposits_tx = Transaction(inputs=inputs, outputs=[(owner.address, NULL_ADDRESS, deposit_2_amount + deposit_1_amount)]) for i in range(0, len(inputs)): spend_deposits_tx.sign(i, owner, verifying_contract=testlang.root_chain.plasma_framework) testlang.start_in_flight_exit(None, spend_tx=spend_deposits_tx) testlang.piggyback_in_flight_exit_input(None, 0, owner, spend_tx=spend_deposits_tx) testlang.challenge_in_flight_exit_not_canonical(None, spend_deposit_2_id, account=owner, in_flight_tx=spend_deposits_tx) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) testlang.process_exits(NULL_ADDRESS, 0, 1) vault_balance = testlang.get_balance(plasma_framework.eth_vault) # deposit 1 is withdrawn assert vault_balance == starting_vault_balance - amount_spent - deposit_1_amount
def spend_utxo(self, input_ids, keys, outputs=[], force_invalid=False): inputs = [decode_utxo_id(input_id) for input_id in input_ids] spend_tx = Transaction(inputs=inputs, outputs=outputs) for i in range(0, len(inputs)): spend_tx.sign(i, keys[i]) blknum = self.submit_block([spend_tx], force_invalid=force_invalid) spend_id = encode_utxo_id(blknum, 0, 0) return spend_id
def test_decode_mallability(testlang, plasma_core_test): owner, amount = testlang.accounts[0], 100 null = '0x0000000000000000000000000000000000000000' tx = Transaction(outputs=[(owner.address, null, amount)]) tx.sign(0, owner.key) import rlp encoded_with_signatures = rlp.encode(tx, Transaction) with pytest.raises(TransactionFailed): plasma_core_test.getOutput(encoded_with_signatures, 0)
def test_should_not_allow_to_withdraw_inputs_and_outputs_when_ifes_processing_interchanges(testlang, plasma_framework, token): alice, amount_token = testlang.accounts[0], 200 caroline, amount_eth = testlang.accounts[1], 100 deposit_id_token = testlang.deposit_token(alice, token, amount_token) deposit_id_eth = testlang.deposit(caroline, amount_eth) swap_tx_id = testlang.spend_utxo( [deposit_id_token, deposit_id_eth], [alice, caroline], [(alice.address, NULL_ADDRESS, amount_eth), (caroline.address, token.address, amount_token)]) # in-flight transaction not included in Plasma steal_tx = Transaction(inputs=[decode_utxo_id(deposit_id_eth)], outputs=[(caroline.address, NULL_ADDRESS, amount_eth)]) steal_tx.sign(0, caroline, verifying_contract=testlang.root_chain.plasma_framework) alice_token_balance_before = token.balanceOf(alice.address) alice_eth_balance_before = testlang.get_balance(alice) caroline_token_balance_before = token.balanceOf(caroline.address) caroline_eth_balance_before = testlang.get_balance(caroline) testlang.start_in_flight_exit(swap_tx_id) testlang.start_in_flight_exit(None, spend_tx=steal_tx) testlang.piggyback_in_flight_exit_input(swap_tx_id, 0, alice) testlang.piggyback_in_flight_exit_input(swap_tx_id, 1, caroline) testlang.piggyback_in_flight_exit_output(swap_tx_id, 0, alice) testlang.piggyback_in_flight_exit_output(swap_tx_id, 1, caroline) # we have encounter flaky tests. we are guessing it is caused by two exits being enqueued in the time that is too close. # within same root chain block time span, the priority in the queue could be the same and hard to differentiate. # https://github.com/omgnetwork/plasma-contracts/issues/606 TIME_DIFF_FOR_ENSUREING_EXIT_PRIORITY = 10 testlang.forward_timestamp(TIME_DIFF_FOR_ENSUREING_EXIT_PRIORITY) testlang.piggyback_in_flight_exit_output(None, 0, caroline, spend_tx=steal_tx) testlang.piggyback_in_flight_exit_input(None, 0, caroline, spend_tx=steal_tx) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) # process swap_tx Eth exit and then steal_tx Eth exit testlang.process_exits(NULL_ADDRESS, 0, 2) # process token exit testlang.process_exits(token.address, 0, 1) # caroline exits with the tokens caroline_token_balance = token.balanceOf(caroline.address) assert caroline_token_balance == caroline_token_balance_before + amount_token # but she does not get ETH back caroline_eth_balance = testlang.get_balance(caroline) assert caroline_eth_balance == caroline_eth_balance_before # alice exits with eth alice_eth_balance = testlang.get_balance(alice) assert alice_eth_balance == alice_eth_balance_before + amount_eth # but she does not get token back alice_token_balance = token.balanceOf(alice.address) assert alice_token_balance == alice_token_balance_before
def spend_utxo(self, input_ids, keys, outputs=None, metadata=None, force_invalid=False): if outputs is None: outputs = [] inputs = [decode_utxo_id(input_id) for input_id in input_ids] spend_tx = Transaction(inputs=inputs, outputs=outputs, metadata=metadata) for i in range(0, len(inputs)): spend_tx.sign(i, keys[i], verifyingContract=self.root_chain) blknum = self.submit_block([spend_tx], force_invalid=force_invalid) spend_id = encode_utxo_id(blknum, 0, 0) return spend_id
def test_start_standard_exit_wrong_oindex_should_fail(testlang): alice, bob, alice_money, bob_money = testlang.accounts[0], testlang.accounts[1], 10, 90 deposit_id = testlang.deposit(alice, alice_money + bob_money) spend_tx = Transaction(inputs=[decode_utxo_id(deposit_id)], outputs=[(alice.address, NULL_ADDRESS, alice_money), (bob.address, NULL_ADDRESS, bob_money)]) spend_tx.sign(0, alice, verifying_contract=testlang.root_chain) blknum = testlang.submit_block([spend_tx]) alice_utxo = encode_utxo_id(blknum, 0, 0) bob_utxo = encode_utxo_id(blknum, 0, 1) with pytest.raises(TransactionFailed): testlang.start_standard_exit(bob_utxo, alice) with pytest.raises(TransactionFailed): testlang.start_standard_exit(alice_utxo, bob) testlang.start_standard_exit(alice_utxo, alice) testlang.start_standard_exit(bob_utxo, bob)
def spend_utxo(self, utxo_position, new_owner, amount, signer): """Creates a spending transaction and inserts it into the chain. Args: utxo_position (int): Identifier of the UTXO to spend. new_owner (EthereumAccount): Account to own the output of this spend. amount (int): Amount to spend. signer (EthereumAccount): Account to sign this transaction. Returns: int: Unique identifier of the spend. """ spend_tx = Transaction(inputs=[decode_utxo_position(utxo_position)], outputs=[(new_owner.address, amount)]) spend_tx.sign(0, signer.key) blknum = self.root_chain.currentPlasmaBlockNumber() block = Block(transactions=[spend_tx], number=blknum) self.commit_plasma_block_root(block) return encode_utxo_position(blknum, 0, 0)
def test_challenge_standard_exit_with_in_flight_exit_tx_should_succeed(testlang): # exit cross-spend test, cases 3 and 4 owner, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit(owner, amount) spend_id = testlang.spend_utxo([deposit_id], [owner], outputs=[(owner.address, NULL_ADDRESS, amount)]) ife_tx = Transaction(inputs=[decode_utxo_id(spend_id)], outputs=[(owner.address, NULL_ADDRESS, amount)]) ife_tx.sign(0, owner, verifying_contract=testlang.root_chain.plasma_framework) (encoded_spend, encoded_inputs, proofs, signatures) = testlang.get_in_flight_exit_info(None, spend_tx=ife_tx) bond = testlang.root_chain.inFlightExitBond() testlang.root_chain.startInFlightExit(encoded_spend, encoded_inputs, proofs, signatures, **{'value': bond, 'from': owner.address}) testlang.start_standard_exit(spend_id, owner) assert testlang.get_standard_exit(spend_id).amount == 100 exit_id = testlang.get_standard_exit_id(spend_id) exiting_tx = testlang.get_transaction(spend_id) # FIXME a proper way of getting encoded body of IFE tx is to get it out of generated events testlang.root_chain.challengeStandardExit(exit_id, ife_tx.encoded, 0, ife_tx.signatures[0], exiting_tx.encoded) assert testlang.get_standard_exit(spend_id) == [NULL_ADDRESS_HEX, 0, 0, False]
def test_should_not_allow_to_withdraw_inputs_and_outputs_when_ifes_processing_interchanges( testlang, plasma_framework, token): alice, amount_token = testlang.accounts[0], 200 caroline, amount_eth = testlang.accounts[1], 100 deposit_id_token = testlang.deposit_token(alice, token, amount_token) deposit_id_eth = testlang.deposit(caroline, amount_eth) swap_tx_id = testlang.spend_utxo( [deposit_id_token, deposit_id_eth], [alice, caroline], [(alice.address, NULL_ADDRESS, amount_eth), (caroline.address, token.address, amount_token)]) # in-flight transaction not included in Plasma steal_tx = Transaction(inputs=[decode_utxo_id(deposit_id_eth)], outputs=[(caroline.address, NULL_ADDRESS, amount_eth)]) steal_tx.sign(0, caroline, verifying_contract=testlang.root_chain.plasma_framework) alice_token_balance_before = token.balanceOf(alice.address) alice_eth_balance_before = testlang.get_balance(alice) caroline_token_balance_before = token.balanceOf(caroline.address) caroline_eth_balance_before = testlang.get_balance(caroline) testlang.start_in_flight_exit(swap_tx_id) testlang.start_in_flight_exit(None, spend_tx=steal_tx) testlang.piggyback_in_flight_exit_input(swap_tx_id, 0, alice) testlang.piggyback_in_flight_exit_input(swap_tx_id, 1, caroline) testlang.piggyback_in_flight_exit_output(swap_tx_id, 0, alice) testlang.piggyback_in_flight_exit_output(swap_tx_id, 1, caroline) testlang.piggyback_in_flight_exit_output(None, 0, caroline, spend_tx=steal_tx) testlang.piggyback_in_flight_exit_input(None, 0, caroline, spend_tx=steal_tx) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) # process swap_tx Eth exit and then steal_tx Eth exit testlang.process_exits(NULL_ADDRESS, 0, 2) # process token exit testlang.process_exits(token.address, 0, 1) # caroline exits with the tokens caroline_token_balance = token.balanceOf(caroline.address) assert caroline_token_balance == caroline_token_balance_before + amount_token # but she does not get ETH back caroline_eth_balance = testlang.get_balance(caroline) assert caroline_eth_balance == caroline_eth_balance_before # alice exits with eth alice_eth_balance = testlang.get_balance(alice) assert alice_eth_balance == alice_eth_balance_before + amount_eth # but she does not get token back alice_token_balance = token.balanceOf(alice.address) assert alice_token_balance == alice_token_balance_before