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 transfer(self, input1, newowner1, amount1, signatory1, input2=0, newowner2=None, amount2=0, signatory2=None, cur12=NULL_ADDRESS): newowner_address1 = newowner1['address'] newowner_address2 = NULL_ADDRESS if newowner2 is not None: newowner_address2 = newowner2['address'] tx = Transaction(*decode_utxo_id(input1), *decode_utxo_id(input2), cur12, newowner_address1, amount1, newowner_address2, amount2) if signatory1 is not None: key1 = signatory1['key'] tx.sign1(key1) if signatory2 is not None: key2 = signatory2['key'] tx.sign2(key2) spend_id = self.child_chain.apply_transaction(tx) self.submit_block() return spend_id
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) utxo = testlang.child_chain.get_transaction(deposit_id) spend_tx = Transaction(*decode_utxo_id(deposit_id), 0, 0, 0, utxo.cur12, alice.address, alice_money, bob.address, bob_money) spend_tx.sign1(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, alice_utxo) bob_spend_id = testlang.spend_utxo(bob_utxo, bob, bob_money, bob) alice_spend_id = testlang.spend_utxo(alice_utxo, alice, alice_money, alice) with pytest.raises(TransactionFailed): testlang.challenge_standard_exit(alice_utxo, bob_spend_id) testlang.challenge_standard_exit(alice_utxo, alice_spend_id)
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 test_finalize_exits_priority_for_in_flight_exits_corresponds_to_the_age_of_youngest_input(testlang): owner, amount = testlang.accounts[0], 100 deposit_0_id = testlang.deposit(owner, amount) deposit_1_id = testlang.deposit(owner, amount) spend_00_id = testlang.spend_utxo([deposit_0_id], [owner], [(owner.address, NULL_ADDRESS, 30), (owner.address, NULL_ADDRESS, 70)]) blknum, txindex, _ = decode_utxo_id(spend_00_id) spend_01_id = encode_utxo_id(blknum, txindex, 1) spend_1_id = testlang.spend_utxo([spend_01_id], [owner], [(owner.address, NULL_ADDRESS, 70)]) spend_2_id = testlang.spend_utxo([deposit_1_id], [owner], [(owner.address, NULL_ADDRESS, 100)]) testlang.start_standard_exit(spend_00_id, owner) testlang.start_in_flight_exit(spend_1_id) testlang.piggyback_in_flight_exit_output(spend_1_id, 0, owner) testlang.start_standard_exit(spend_2_id, owner) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) balance = testlang.get_balance(owner) testlang.process_exits(testlang.root_chain.eth_vault_id, NULL_ADDRESS, testlang.get_standard_exit_id(spend_00_id), 1) assert testlang.get_balance(owner) == balance + 30 + testlang.root_chain.standardExitBond() balance = testlang.get_balance(owner) testlang.process_exits(testlang.root_chain.eth_vault_id, NULL_ADDRESS, testlang.get_in_flight_exit_id(spend_1_id), 1) assert testlang.get_balance( owner) == balance + 70 + testlang.root_chain.inFlightExitBond() + testlang.root_chain.piggybackBond() balance = testlang.get_balance(owner) testlang.process_exits(testlang.root_chain.eth_vault_id, NULL_ADDRESS, testlang.get_standard_exit_id(spend_2_id), 1) assert testlang.get_balance(owner) == balance + 100 + testlang.root_chain.standardExitBond()
def test_piggyback_in_flight_exit_output_with_preexisting_standard_exit_should_fail( testlang, num_outputs): # exit cross-spend test, case 5 owner_1, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit(owner_1, amount) outputs = [] for i in range(0, num_outputs): outputs.append((testlang.accounts[i].address, NULL_ADDRESS, 1)) spend_id = testlang.spend_utxo([deposit_id], [owner_1.key], outputs) blknum, txindex, _ = decode_utxo_id(spend_id) exit_pos = encode_utxo_id(blknum, txindex, num_outputs - 1) testlang.start_standard_exit(exit_pos, key=testlang.accounts[num_outputs - 1].key) testlang.start_in_flight_exit(spend_id) assert testlang.get_standard_exit(exit_pos).amount == 1 bond = testlang.root_chain.piggybackBond() with pytest.raises(TransactionFailed): testlang.piggyback_in_flight_exit_output( spend_id, num_outputs - 1, testlang.accounts[num_outputs - 1].key, bond) in_flight_exit = testlang.get_in_flight_exit(spend_id) assert not in_flight_exit.output_piggybacked(num_outputs - 1)
def test_old_signature_scheme_does_not_work_any_longer(testlang, utxo): # In this test I will challenge standard exit with old signature schema to show it no longer works # Then passing new signature to the same challenge data, challenge will succeed alice = testlang.accounts[0] outputs = [(alice.address, NULL_ADDRESS, 50)] spend_id = testlang.spend_utxo([utxo.spend_id], [alice], outputs) testlang.start_standard_exit(spend_id, alice) exit_id = testlang.get_standard_exit_id(spend_id) # let's prepare old schema signature for a transaction with an input of exited utxo spend_tx = Transaction(inputs=[decode_utxo_id(spend_id)], outputs=outputs) old_signature = alice.key.sign_msg_hash(spend_tx.hash).to_bytes() # challenge will fail on signature verification with pytest.raises(TransactionFailed): testlang.root_chain.challengeStandardExit(exit_id, spend_tx.encoded, 0, old_signature) # sanity check: let's provide new schema signature for a challenge new_signature = alice.key.sign_msg_hash( hash_struct(spend_tx, verifying_contract=testlang.root_chain)).to_bytes() testlang.root_chain.challengeStandardExit(exit_id, spend_tx.encoded, 0, new_signature)
def test_start_standard_exit_on_in_flight_exit_output_should_block_future_piggybacks( testlang, num_outputs): # exit cross-spend test, case 7 owner_1, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit(owner_1, amount) outputs = [] for i in range(0, num_outputs): outputs.append((testlang.accounts[i].address, NULL_ADDRESS, 1)) spend_id = testlang.spend_utxo([deposit_id], [owner_1], outputs) testlang.start_in_flight_exit(spend_id) output_index = num_outputs - 1 blknum, txindex, _ = decode_utxo_id(spend_id) output_id = encode_utxo_id(blknum, txindex, output_index) testlang.start_standard_exit(output_id, account=testlang.accounts[output_index]) with pytest.raises(TransactionFailed): testlang.piggyback_in_flight_exit_output( spend_id, output_index, testlang.accounts[output_index]) in_flight_exit = testlang.get_in_flight_exit(spend_id) assert not in_flight_exit.output_piggybacked(output_index) assert in_flight_exit.output_blocked(output_index)
def mark_utxo_spent(self, utxo_id): (_, _, oindex) = decode_utxo_id(utxo_id) tx = self.get_transaction(utxo_id) if oindex == 0: tx.spent1 = True else: tx.spent2 = True
def test_output_exited_via_ife_and_then_se_withdraws_once(testlang, plasma_framework, num_outputs): owner, amount, amount_spent = testlang.accounts[0], 100, 1 deposit_id = testlang.deposit(owner, amount) outputs = [] for i in range(0, num_outputs): outputs.append((owner.address, NULL_ADDRESS, amount_spent)) spend_id = testlang.spend_utxo([deposit_id], [owner], outputs) output_index = num_outputs - 1 testlang.start_in_flight_exit(spend_id) blknum, txindex, _ = decode_utxo_id(spend_id) output_id = encode_utxo_id(blknum, txindex, output_index) testlang.piggyback_in_flight_exit_output(spend_id, output_index, owner) testlang.start_standard_exit(output_id, account=owner) pre_exit_balance = testlang.get_balance(plasma_framework.eth_vault) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) testlang.process_exits(NULL_ADDRESS, 0, 100) post_exit_balance = testlang.get_balance(plasma_framework.eth_vault) assert post_exit_balance == pre_exit_balance - amount_spent
def test_start_standard_exit_on_finalized_in_flight_exit_output_should_fail( testlang, num_outputs): owner, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit(owner, amount) outputs = [(owner.address, NULL_ADDRESS, 1)] * num_outputs spend_id = testlang.spend_utxo([deposit_id], [owner], outputs) output_index = num_outputs - 1 # start IFE, piggyback one output and process the exit testlang.start_in_flight_exit(spend_id) testlang.piggyback_in_flight_exit_output(spend_id, output_index, owner) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) testlang.process_exits(testlang.root_chain.eth_vault_id, NULL_ADDRESS, 0, 1) blknum, txindex, _ = decode_utxo_id(spend_id) # all not finalized outputs can exit via SE for i in range(output_index): output_id = encode_utxo_id(blknum, txindex, i) testlang.start_standard_exit(output_id, account=owner) # an already finalized output __cannot__ exit via SE with pytest.raises(TransactionFailed): output_id = encode_utxo_id(blknum, txindex, output_index) testlang.start_standard_exit(output_id, account=owner)
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_piggyback_in_flight_exit_output_with_preexisting_finalized_standard_exit_should_fail( testlang, num_outputs): # exit cross-spend test, case 6 owner, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit(owner, amount) outputs = [] for i in range(num_outputs): outputs.append((testlang.accounts[i].address, NULL_ADDRESS, 1)) spend_id = testlang.spend_utxo([deposit_id], [owner], outputs) blknum, txindex, _ = decode_utxo_id(spend_id) exit_pos = encode_utxo_id(blknum, txindex, num_outputs - 1) testlang.start_standard_exit(exit_pos, account=testlang.accounts[num_outputs - 1]) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) testlang.process_exits(NULL_ADDRESS, 0, 1) testlang.start_in_flight_exit(spend_id) assert testlang.get_standard_exit(exit_pos).amount == 1 bond = testlang.root_chain.piggybackBond() with pytest.raises(TransactionFailed): testlang.piggyback_in_flight_exit_output( spend_id, num_outputs - 1, testlang.accounts[num_outputs - 1], bond) in_flight_exit = testlang.get_in_flight_exit(spend_id) assert not in_flight_exit.output_piggybacked(num_outputs - 1)
def spend_utxo(self, utxo_id, new_owner, amount, signer): """Creates a spending transaction and inserts it into the chain. Args: utxo_id (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(*decode_utxo_id(utxo_id), 0, 0, 0, NULL_ADDRESS, new_owner.address, amount, NULL_ADDRESS, 0) spend_tx.sign1(signer.key) blknum = self.root_chain.currentChildBlock() block = Block(transaction_set=[spend_tx], number=blknum) block.sign(self.operator.key) self.root_chain.submitBlock(block.root) self.child_chain.add_block(block) return encode_utxo_id(blknum, 0, 0)
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 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 confirm(self, tx_id, signatory1, signatory2=None): tx = self.child_chain.get_transaction(tx_id) (blknum, _, _) = decode_utxo_id(tx_id) block_root = self.child_chain.get_block(blknum).root confirm_sigs = b'' for signatory in [x for x in [signatory1, signatory2] if x is not None]: confirm_sigs += confirm_tx(tx, block_root, signatory['key']) self.confirmations[tx_id] = confirm_sigs
def test_token_deposit_should_succeed(testlang, root_chain, token): owner, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit_token(owner, token, amount) deposit_blknum, _, _ = decode_utxo_id(deposit_id) plasma_block = testlang.get_plasma_block(deposit_blknum) assert plasma_block.root == testlang.child_chain.get_block(deposit_blknum).root assert plasma_block.timestamp == testlang.timestamp assert root_chain.nextDepositBlock() == 2
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 start_exit(self, utxo_id, exitor): tx = self.child_chain.get_transaction(utxo_id) sigs = tx.sig1 + tx.sig2 + self.confirmations[utxo_id] (blknum, _, _) = decode_utxo_id(utxo_id) block = self.child_chain.get_block(blknum) proof = block.merkle.create_membership_proof(tx.merkle_hash) self.root_chain.transact({ 'from': exitor['address'] }).startExit(utxo_id, tx.encoded, proof, sigs)
def test_token_deposit_should_succeed(testlang, root_chain, token): owner, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit_token(owner, token, amount) deposit_blknum, _, _ = decode_utxo_id(deposit_id) plasma_block = testlang.get_plasma_block(deposit_blknum) assert plasma_block.root == get_deposit_hash( address_to_bytes(owner.address), token.address, amount) assert plasma_block.timestamp == testlang.timestamp assert root_chain.currentDepositBlock() == 2
def test_deposit_valid_values_should_succeed(testlang): owner, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit(owner, amount) deposit_blknum, _, _ = decode_utxo_id(deposit_id) plasma_block = testlang.get_plasma_block(deposit_blknum) assert plasma_block.root == get_deposit_hash( address_to_bytes(owner.address), NULL_ADDRESS, amount) assert plasma_block.timestamp == testlang.timestamp assert testlang.root_chain.currentDepositBlock() == 2
def mark_utxo_spent(self, utxo_id): (_, _, oindex) = decode_utxo_id(utxo_id) tx = self.get_transaction(utxo_id) print("tx {0}".format(tx)) if not tx: print("tx is empty") return if oindex == 0: tx.spent1 = True else: tx.spent2 = True
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) 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, verifyingContract=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.key) with pytest.raises(TransactionFailed): testlang.start_standard_exit(alice_utxo, bob.key) testlang.start_standard_exit(alice_utxo, alice.key) testlang.start_standard_exit(bob_utxo, bob.key)
def test_challenge_exit(t, u, root_chain, assert_tx_failed): owner, value_1, key = t.a1, 100, t.k1 tx1 = Transaction(0, 0, 0, 0, 0, 0, NULL_ADDRESS, owner, value_1, NULL_ADDRESS, 0) deposit_tx_hash = get_deposit_hash(owner, NULL_ADDRESS, value_1) utxo_pos1 = encode_utxo_id(root_chain.getDepositBlock(), 0, 0) root_chain.deposit(value=value_1, sender=key) utxo_pos2 = encode_utxo_id(root_chain.getDepositBlock(), 0, 0) root_chain.deposit(value=value_1, sender=key) merkle = FixedMerkle(16, [deposit_tx_hash], True) proof = merkle.create_membership_proof(deposit_tx_hash) confirmSig1 = confirm_tx(tx1, root_chain.getPlasmaBlock(utxo_pos1)[0], key) sigs = tx1.sig1 + tx1.sig2 + confirmSig1 exit_bond = root_chain.EXIT_BOND() root_chain.startDepositExit(utxo_pos1, NULL_ADDRESS, tx1.amount1, sender=key, value=exit_bond) tx3 = Transaction(utxo_pos2, 0, 0, 0, 0, 0, NULL_ADDRESS, owner, value_1, NULL_ADDRESS, 0) tx3.sign1(key) tx_bytes3 = rlp.encode(tx3, UnsignedTransaction) merkle = FixedMerkle(16, [tx3.merkle_hash], True) proof = merkle.create_membership_proof(tx3.merkle_hash) child_blknum = root_chain.currentChildBlock() root_chain.submitBlock(merkle.root) confirmSig = confirm_tx(tx3, root_chain.getPlasmaBlock(child_blknum)[0], key) sigs = tx3.sig1 + tx3.sig2 utxo_pos3 = encode_utxo_id(child_blknum, 0, 0) utxo1_blknum, _, _ = decode_utxo_id(utxo_pos1) tx4 = Transaction(utxo1_blknum, 0, 0, 0, 0, 0, NULL_ADDRESS, owner, value_1, NULL_ADDRESS, 0) tx4.sign1(key) tx_bytes4 = rlp.encode(tx4, UnsignedTransaction) merkle = FixedMerkle(16, [tx4.merkle_hash], True) proof = merkle.create_membership_proof(tx4.merkle_hash) child_blknum = root_chain.currentChildBlock() root_chain.submitBlock(merkle.root) confirmSig = confirm_tx(tx4, root_chain.getPlasmaBlock(child_blknum)[0], key) sigs = tx4.sig1 + tx4.sig2 utxo_pos4 = encode_utxo_id(child_blknum, 0, 0) oindex1 = 0 assert root_chain.exits(utxo_pos1) == ['0x' + owner.hex(), NULL_ADDRESS_HEX, 100] # Fails if transaction after exit doesn't reference the utxo being exited assert_tx_failed(lambda: root_chain.challengeExit(utxo_pos3, oindex1, tx_bytes3, proof, sigs, confirmSig)) # Fails if transaction proof is incorrect assert_tx_failed(lambda: root_chain.challengeExit(utxo_pos4, oindex1, tx_bytes4, proof[::-1], sigs, confirmSig)) # Fails if transaction confirmation is incorrect assert_tx_failed(lambda: root_chain.challengeExit(utxo_pos4, oindex1, tx_bytes4, proof, sigs, confirmSig[::-1])) root_chain.challengeExit(utxo_pos4, oindex1, tx_bytes4, proof, sigs, confirmSig) assert root_chain.exits(utxo_pos1) == [NULL_ADDRESS_HEX, NULL_ADDRESS_HEX, value_1]
def confirm_spend(self, tx_id, signer): """Signs a confirmation signature for a spend. Args: tx_id (int): Identifier of the transaction. signer (EthereumAccount): Account to sign this confirmation. """ spend_tx = self.child_chain.get_transaction(tx_id) (blknum, _, _) = decode_utxo_id(tx_id) block = self.child_chain.blocks[blknum] confirmation_hash = sha3(spend_tx.hash + block.root) self.confirmations[tx_id] = sign(confirmation_hash, signer.key)
def get_transaction(self, utxo_id): (blknum, txindex, _) = decode_utxo_id(utxo_id) print("blocks {0}".format(self.blocks)) if not self.blocks: print("blocks are empty") return {} if blknum not in self.blocks: print("block num not in dictionary") return {} if not self.blocks[blknum].transaction_set: print("transaction_set is empty") return {} return self.blocks[blknum].transaction_set[txindex]
def test_metadata_is_part_of_the_proof(testlang): owner, amount = testlang.accounts[0], 100 deposit_id = testlang.deposit(owner, amount) input_ids = [deposit_id] keys = [owner.key] outputs = [(owner.address, NULL_ADDRESS, amount)] spend_id = testlang.spend_utxo(input_ids, keys, outputs, "metadata info") inputs = [decode_utxo_id(input_id) for input_id in input_ids] bad_spend_tx = Transaction(inputs=inputs, outputs=outputs, metadata="other information") with pytest.raises(TransactionFailed): testlang.start_standard_exit_with_tx_body(spend_id, bad_spend_tx, owner.key)
def start_exit(self, owner, utxo_id): """Starts a standard exit. Args: owner (EthereumAccount): Account to attempt the exit. utxo_id (int): Unique identifier of the UTXO to be exited. """ spend_tx = self.child_chain.get_transaction(utxo_id) (blknum, _, _) = decode_utxo_id(utxo_id) block = self.child_chain.blocks[blknum] proof = block.merkle_tree.create_membership_proof(spend_tx.merkle_hash) sigs = spend_tx.sig1 + spend_tx.sig2 + self.confirmations[utxo_id] self.root_chain.startExit(utxo_id, spend_tx.encoded, proof, sigs, sender=owner.key)
def get_challenge_proof(self, utxo_id, spend_id): """Returns information required to submit a challenge. Args: utxo_id (int): Identifier of the UTXO being exited. spend_id (int): Identifier of the transaction that spent the UTXO. Returns: int, bytes, bytes, bytes, bytes: Information necessary to create a challenge proof. """ spend_tx = self.child_chain.get_transaction(spend_id) inputs = [(spend_tx.blknum1, spend_tx.txindex1, spend_tx.oindex1), (spend_tx.blknum2, spend_tx.txindex2, spend_tx.oindex2)] try: input_index = inputs.index(decode_utxo_id(utxo_id)) except ValueError: input_index = 0 (blknum, _, _) = decode_utxo_id(spend_id) block = self.child_chain.blocks[blknum] proof = block.merkle_tree.create_membership_proof(spend_tx.merkle_hash) sigs = spend_tx.sig1 + spend_tx.sig2 confirmation_sig = self.confirmations[spend_id] return (input_index, spend_tx.encoded, proof, sigs, confirmation_sig)