def test_challenge_in_flight_exit_input_spent_should_succeed_for_all_indices( testlang, double_spend_output_index, challenge_input_index ): alice, bob, carol = testlang.accounts[0], testlang.accounts[1], testlang.accounts[2] deposit_amount = 100 deposit_id = testlang.deposit(alice, deposit_amount) tx_output_amount = deposit_amount // PAYMENT_TX_MAX_OUTPUT_SIZE outputs = [(alice.address, NULL_ADDRESS, tx_output_amount)] * PAYMENT_TX_MAX_OUTPUT_SIZE input_tx_id = testlang.spend_utxo([deposit_id], [alice], outputs=outputs) blknum, tx_index, _ = decode_utxo_id(input_tx_id) double_spend_utxo = encode_utxo_id(blknum, tx_index, double_spend_output_index) ife_output_amount = tx_output_amount ife_tx_id = testlang.spend_utxo( [double_spend_utxo], [alice], [(bob.address, NULL_ADDRESS, ife_output_amount)] ) inputs = [] for i in range(0, PAYMENT_TX_MAX_INPUT_SIZE): if i == challenge_input_index: inputs.append(double_spend_utxo) else: inputs.append(testlang.deposit(alice, tx_output_amount)) challenge_tx_id = testlang.spend_utxo( inputs, [alice] * PAYMENT_TX_MAX_INPUT_SIZE, [ (carol.address, NULL_ADDRESS, tx_output_amount), ], force_invalid=True ) testlang.start_in_flight_exit(ife_tx_id, sender=bob) testlang.piggyback_in_flight_exit_input(ife_tx_id, 0, alice) testlang.challenge_in_flight_exit_input_spent(ife_tx_id, challenge_tx_id, carol) in_flight_exit = testlang.get_in_flight_exit(ife_tx_id) for i in range(0, PAYMENT_TX_MAX_INPUT_SIZE): assert not in_flight_exit.input_piggybacked(i)
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.key], [(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.key], [(owner.address, NULL_ADDRESS, 70)]) testlang.ethtester.chain.mine() spend_2_id = testlang.spend_utxo([deposit_1_id], [owner.key], [(owner.address, NULL_ADDRESS, 100)]) testlang.start_standard_exit(spend_00_id, owner.key) testlang.start_in_flight_exit(spend_1_id) testlang.piggyback_in_flight_exit_output(spend_1_id, 0, owner.key) testlang.start_standard_exit(spend_2_id, owner.key) testlang.forward_timestamp(2 * MIN_EXIT_PERIOD + 1) balance = testlang.get_balance(owner) testlang.process_exits(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(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(NULL_ADDRESS, testlang.get_standard_exit_id(spend_2_id), 1) assert testlang.get_balance( owner) == balance + 100 + testlang.root_chain.standardExitBond()
def spend_utxo(self, input_ids, accounts, 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, accounts[i], verifying_contract=self.root_chain.plasma_framework) blknum = self.submit_block([spend_tx], force_invalid=force_invalid) spend_id = encode_utxo_id(blknum, 0, 0) return spend_id
def application(request): # Dispatcher is dictionary {<method_name>: callable} dispatcher["submit_block"] = lambda block: child_chain.submit_block( rlp.decode(utils.decode_hex(block), Block)) dispatcher[ "apply_transaction"] = lambda transaction: child_chain.apply_transaction( rlp.decode(utils.decode_hex(transaction), Transaction)) dispatcher["get_transaction"] = lambda blknum, txindex: rlp.encode( child_chain.get_transaction(encode_utxo_id(blknum, txindex, 0)), Transaction).hex() dispatcher["get_current_block"] = lambda: rlp.encode( child_chain.get_current_block(), Block).hex() dispatcher[ "get_current_block_num"] = lambda: child_chain.get_current_block_num() dispatcher["get_block"] = lambda blknum: rlp.encode( child_chain.get_block(blknum), Block).hex() dispatcher["get_all_blocks"] = lambda: child_chain.get_all_blocks() response = JSONRPCResponseManager.handle(request.data, dispatcher) return Response(response.json, mimetype='application/json')
def test_start_standard_exit_on_piggyback_in_flight_exit_valid_output_owner_should_fail(testlang, num_outputs): # exit cross-spend test, case 9 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) testlang.start_in_flight_exit(spend_id) output_index = num_outputs - 1 testlang.piggyback_in_flight_exit_output(spend_id, output_index, testlang.accounts[output_index].key) in_flight_exit = testlang.get_in_flight_exit(spend_id) assert in_flight_exit.output_piggybacked(output_index) blknum, txindex, _ = decode_utxo_id(spend_id) output_id = encode_utxo_id(blknum, txindex, output_index) with pytest.raises(TransactionFailed): testlang.start_standard_exit(output_id, testlang.accounts[output_index].key)
def test_start_fee_exit(t, u, root_chain, assert_tx_failed): two_weeks = 60 * 60 * 24 * 7 * 2 value_1 = 100 blknum = root_chain.getDepositBlock() root_chain.deposit(value=value_1) expected_utxo_pos = root_chain.currentFeeExit() expected_exitable_at = t.chain.head_state.timestamp + two_weeks + 1 assert root_chain.currentFeeExit() == 1 root_chain.startFeeExit(NULL_ADDRESS, 1) assert root_chain.currentFeeExit() == 2 exitable_at, utxo_pos = root_chain.getNextExit(NULL_ADDRESS) fee_priority = exitable_at << 128 | utxo_pos assert utxo_pos == expected_utxo_pos assert exitable_at == expected_exitable_at expected_utxo_pos = encode_utxo_id(blknum, 0, 0) root_chain.startDepositExit(expected_utxo_pos, NULL_ADDRESS, value_1) created_at, utxo_pos = root_chain.getNextExit(NULL_ADDRESS) deposit_priority = created_at << 128 | utxo_pos assert fee_priority > deposit_priority # Fails if transaction sender isn't the authority assert_tx_failed( lambda: root_chain.startFeeExit(NULL_ADDRESS, 1, sender=t.k1))
def test_finalize_exits(t, u, root_chain): two_weeks = 60 * 60 * 24 * 14 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) dep1_blknum = root_chain.getDepositBlock() root_chain.deposit(value=value_1, sender=key) utxo_pos1 = encode_utxo_id(dep1_blknum, 0, 0) root_chain.startDepositExit(utxo_pos1, NULL_ADDRESS, tx1.amount1, sender=key) t.chain.head_state.timestamp += two_weeks * 2 assert root_chain.exits(utxo_pos1) == [ '0x' + owner.hex(), NULL_ADDRESS_HEX, 100 ] pre_balance = t.chain.head_state.get_balance(owner) root_chain.finalizeExits(sender=t.k2) post_balance = t.chain.head_state.get_balance(owner) assert post_balance == pre_balance + value_1 assert root_chain.exits(utxo_pos1) == [ NULL_ADDRESS_HEX, NULL_ADDRESS_HEX, value_1 ]
def test_start_deposit_exit(t, u, root_chain, assert_tx_failed): two_weeks = 60 * 60 * 24 * 7 * 2 value_1 = 100 # Deposit once to make sure everything works for deposit block root_chain.deposit(value=value_1) blknum = root_chain.getDepositBlock() root_chain.deposit(value=value_1) expected_utxo_pos = encode_utxo_id(blknum, 0, 0) expected_exitable_at = t.chain.head_state.timestamp + two_weeks exit_bond = root_chain.EXIT_BOND() root_chain.startDepositExit(expected_utxo_pos, NULL_ADDRESS, value_1, value=exit_bond) exitable_at, utxo_pos = root_chain.getNextExit(NULL_ADDRESS) assert utxo_pos == expected_utxo_pos assert exitable_at == expected_exitable_at assert root_chain.exits(utxo_pos) == ['0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1', NULL_ADDRESS_HEX, 100] # Same deposit cannot be exited twice assert_tx_failed(lambda: root_chain.startDepositExit(utxo_pos, NULL_ADDRESS, value_1, value=exit_bond)) # Fails if transaction sender is not the depositor assert_tx_failed(lambda: root_chain.startDepositExit(utxo_pos, NULL_ADDRESS, value_1, sender=t.k1, value=exit_bond)) # Fails if utxo_pos is wrong assert_tx_failed(lambda: root_chain.startDepositExit(utxo_pos * 2, NULL_ADDRESS, value_1, value=exit_bond)) # Fails if value given is not equal to deposited value assert_tx_failed(lambda: root_chain.startDepositExit(utxo_pos, NULL_ADDRESS, value_1 + 1, value=exit_bond))
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_output_exited_via_se_and_then_ife_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 blknum, txindex, _ = decode_utxo_id(spend_id) output_id = encode_utxo_id(blknum, txindex, output_index) testlang.start_standard_exit(output_id, account=owner) pre_exit_balance = testlang.get_balance(plasma_framework.eth_vault) 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(NULL_ADDRESS, 0, 10) post_exit_balance = testlang.get_balance(plasma_framework.eth_vault) assert post_exit_balance == pre_exit_balance - amount_spent
def withdrawdeposit(client, owner, blknum, amount): deposit_pos = encode_utxo_id(blknum, 0, 0) client.withdraw_deposit(owner, deposit_pos, amount) print("Submitted withdrawal")
def identifier(self): return encode_utxo_id(self.blknum, self.txindex, self.oindex)
def test_start_exit(t, root_chain, assert_tx_failed): week_and_a_half = 60 * 60 * 24 * 13 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) dep_blknum = root_chain.getDepositBlock() assert dep_blknum == 1 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(dep_blknum)[0], key) snapshot = t.chain.snapshot() sigs = tx1.sig1 + tx1.sig2 + confirmSig1 utxoId = encode_utxo_id(dep_blknum, 0, 0) # Deposit exit root_chain.startDepositExit(utxoId, NULL_ADDRESS, tx1.amount1, sender=key) t.chain.head_state.timestamp += week_and_a_half # Cannot exit twice off of the same utxo utxo_pos1 = encode_utxo_id(dep_blknum, 0, 0) assert_tx_failed(lambda: root_chain.startExit( utxo_pos1, deposit_tx_hash, proof, sigs, sender=key)) assert root_chain.getExit(utxo_pos1) == [ '0x' + owner.hex(), NULL_ADDRESS_HEX, 100 ] t.chain.revert(snapshot) tx2 = Transaction(dep_blknum, 0, 0, 0, 0, 0, NULL_ADDRESS, owner, value_1, NULL_ADDRESS, 0) tx2.sign1(key) tx_bytes2 = rlp.encode(tx2, UnsignedTransaction) merkle = FixedMerkle(16, [tx2.merkle_hash], True) proof = merkle.create_membership_proof(tx2.merkle_hash) child_blknum = root_chain.currentChildBlock() assert child_blknum == 1000 root_chain.submitBlock(merkle.root) confirmSig1 = confirm_tx(tx2, root_chain.getPlasmaBlock(child_blknum)[0], key) sigs = tx2.sig1 + tx2.sig2 + confirmSig1 snapshot = t.chain.snapshot() # # Single input exit utxo_pos2 = encode_utxo_id(child_blknum, 0, 0) root_chain.startExit(utxo_pos2, tx_bytes2, proof, sigs, sender=key) assert root_chain.getExit(utxo_pos2) == [ '0x' + owner.hex(), NULL_ADDRESS_HEX, 100 ] t.chain.revert(snapshot) dep2_blknum = root_chain.getDepositBlock() assert dep2_blknum == 1001 root_chain.deposit(value=value_1, sender=key) tx3 = Transaction(child_blknum, 0, 0, dep2_blknum, 0, 0, NULL_ADDRESS, owner, value_1, NULL_ADDRESS, 0, 0) tx3.sign1(key) tx3.sign2(key) tx_bytes3 = rlp.encode(tx3, UnsignedTransaction) merkle = FixedMerkle(16, [tx3.merkle_hash], True) proof = merkle.create_membership_proof(tx3.merkle_hash) child2_blknum = root_chain.currentChildBlock() assert child2_blknum == 2000 root_chain.submitBlock(merkle.root) confirmSig1 = confirm_tx(tx3, root_chain.getPlasmaBlock(child2_blknum)[0], key) confirmSig2 = confirm_tx(tx3, root_chain.getPlasmaBlock(child2_blknum)[0], key) sigs = tx3.sig1 + tx3.sig2 + confirmSig1 + confirmSig2 # Double input exit utxo_pos3 = encode_utxo_id(child2_blknum, 0, 0) root_chain.startExit(utxo_pos3, tx_bytes3, proof, sigs, sender=key) assert root_chain.getExit(utxo_pos3) == [ '0x' + owner.hex(), NULL_ADDRESS_HEX, 100 ]
def apply_transaction(self, tx): self.chain.validate_transaction(tx, self.current_block.spent_utxos) self.current_block.add_transaction(tx) return encode_utxo_id(self.current_block.number, len(self.current_block.transaction_set) - 1, 0)
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 withdraw(self, blknum, txindex, oindex, tx, proof, sigs): utxo_pos = encode_utxo_id(blknum, txindex, oindex) encoded_transaction = rlp.encode(tx, UnsignedTransaction) self.root_chain.startExit(utxo_pos, encoded_transaction, proof, sigs, transact={'from': '0x' + tx.newowner1.hex()})