def test_start_exit_invalid_proof_should_fail(testlang): owner, amount = testlang.accounts[0], 100 # Create a deposit deposit_blknum = testlang.deposit(owner, amount) deposit_utxo_position = encode_utxo_position(deposit_blknum, 0, 0) # Spend the deposit and submit the block spend_utxo_position = testlang.spend_utxo(deposit_utxo_position, owner, amount, owner) testlang.confirm(spend_utxo_position, 0, owner) # Start an exit bond = testlang.root_chain.EXIT_BOND() proof = b'' # Using empty proof (encoded_tx, _, signatures, confirmations) = testlang.get_exit_proof(spend_utxo_position) with pytest.raises(TransactionFailed): testlang.root_chain.startExit( *decode_utxo_position(spend_utxo_position), encoded_tx, proof, signatures, confirmations, value=bond)
def test_challenge_exit_invalid_confirmation_signatures_should_fail(testlang): (exiting_utxo_position, spending_utxo_position) = start_exit_spend(testlang) # Exit should fail confirmation_signature = b'' # Using empty confirmation signatures (encoded_tx, _) = testlang.get_challenge_proof(exiting_utxo_position, spending_utxo_position) with pytest.raises(TransactionFailed): testlang.root_chain.challengeExit(*decode_utxo_position(exiting_utxo_position), encoded_tx, confirmation_signature)
def test_challenge_exit_invalid_tx_should_fail(testlang): (exiting_utxo_position, spending_utxo_position) = start_exit_spend(testlang) # Exit should fail encoded_tx = testlang.child_chain.get_transaction(exiting_utxo_position).encoded # Using the wrong tx (_, confirmation_signature) = testlang.get_challenge_proof(exiting_utxo_position, spending_utxo_position) with pytest.raises(TransactionFailed): testlang.root_chain.challengeExit(*decode_utxo_position(exiting_utxo_position), encoded_tx, confirmation_signature)
def get_transaction(self, transaction_position): """Returns the transaction at a given position. Args: transaction_position (int): Transaction position to query. Returns: Transaction: Corresponding transaction object. """ (blknum, txindex, _) = decode_utxo_position(transaction_position) return self.blocks[blknum].transactions[txindex]
def challenge_exit(self, exiting_utxo_position, spending_tx_position): """Challenges an exit with a double spend. Args: exiting_utxo_position (int): Position of the UTXO being exited. spending_tx_position (int): Position of the transaction that spent the UTXO. """ proof_data = self.get_challenge_proof(exiting_utxo_position, spending_tx_position) self.root_chain.challengeExit( *decode_utxo_position(exiting_utxo_position), *proof_data)
def start_exit(self, owner, utxo_position): """Starts a standard exit. Args: owner (EthereumAccount): Account to attempt the exit. utxo_position (int): Position of the UTXO to be exited. """ bond = self.root_chain.EXIT_BOND() self.root_chain.startExit(*decode_utxo_position(utxo_position), *self.get_exit_proof(utxo_position), sender=owner.key, value=bond)
def get_challenge_proof(self, exiting_utxo_position, spending_tx_position): """Returns information required to submit a challenge. Args: exiting_utxo_position (int): Position of the UTXO being exited. spending_tx_position (int): Position of the transaction that spent the UTXO. Returns: bytes, bytes: Information necessary to create a challenge proof. """ spend_tx = self.child_chain.get_transaction(spending_tx_position) (_, _, oindex) = decode_utxo_position(spending_tx_position) confirmation_signature = spend_tx.confirmations[oindex] return (spend_tx.encoded, confirmation_signature)
def get_exit_proof(self, utxo_position): """Returns information required to exit Args: utxo_position (int): Position of the UTXO to be exited. Returns: bytes, bytes, bytes, bytes: Information necessary to exit the UTXO. """ (blknum, _, _) = decode_utxo_position(utxo_position) block = self.child_chain.get_block(blknum) spend_tx = self.child_chain.get_transaction(utxo_position) encoded_tx = spend_tx.encoded proof = block.merkle.create_membership_proof(spend_tx.encoded) signatures = spend_tx.joined_signatures confirmation_signatures = spend_tx.joined_confirmations return (encoded_tx, proof, signatures, confirmation_signatures)
def test_start_exit_wrong_bond_value_should_fail(testlang): owner, amount = testlang.accounts[0], 100 # Create a deposit deposit_blknum = testlang.deposit(owner, amount) deposit_utxo_position = encode_utxo_position(deposit_blknum, 0, 0) # Spend the deposit and submit the block spend_utxo_position = testlang.spend_utxo(deposit_utxo_position, owner, amount, owner) testlang.confirm(spend_utxo_position, 0, owner) # Start an exit bond = 0 # Using wrong bond value with pytest.raises(TransactionFailed): testlang.root_chain.startExit( *decode_utxo_position(spend_utxo_position), *testlang.get_exit_proof(spend_utxo_position), value=bond)
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)