Skip to content

Commit

Permalink
Add root chain tests
Browse files Browse the repository at this point in the history
Also, fix a minor bug that the depth of merkle tree should be 257.
  • Loading branch information
bun919tw authored and boolafish committed Jul 10, 2018
1 parent 73fd753 commit 2652408
Show file tree
Hide file tree
Showing 15 changed files with 410 additions and 12 deletions.
2 changes: 1 addition & 1 deletion integration_tests/features/basic_flow.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ Feature: Basic flow
When userA transfer 1 eth to userB in child chain
Then userB has 1 eth in the transfer tx in plasma cash
When userB start exit 1 eth from plasma cash
Then root chain got userB start exit 1 eth event
Then root chain got userB start exit 1 eth
10 changes: 10 additions & 0 deletions integration_tests/features/challenge_double_spending_flow.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Feature: Challenge double spending flow

Scenario: userA first transfers to userB and then tries to double spend the same funds to userC. userC tries to withdraw the funds and userB challenges the exit.
Given userA deposits 1 eth in plasma cash
And userA transfers 1 eth to userB
And userA tries to double spend 1 eth to userC
When userC starts to exit 1 eth from plasma cash
Then root chain got the start-exit record from double spending challenge
When userB challenges the exit
Then the challenge is successful and root chain cancels the exit
13 changes: 13 additions & 0 deletions integration_tests/features/challenge_history_flow.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Feature: Challenge history flow

Scenario: userA transfers funds to userB, userB transfers to userC, and userC transfers to userD. userD starts an exit and userB challenges the history. Then userD responds to the history challenge.
Given userA deposits 1 eth in plasma cash
And userA transfers 1 eth to userB
And userB transfers 1 eth to userC
And userC transfers 1 eth to userD
When userD starts to exit 1 eth from plasma cash
Then root chain got the start-exit record from history challenge
When userB challenges the exit history with deposit tx
Then root chain got the challenge record
When userD responds to the history challenge
Then root chain cancels the challenge
10 changes: 10 additions & 0 deletions integration_tests/features/challenge_spent_coin_flow.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Feature: Challenge spent coin flow

Scenario: userA deposits to plasma cash and transfers to userB. userB transfers to userC and tries to withdraw the funds. Then userC challenges the exit.
Given userA deposits 1 eth in plasma cash
And userA transfers 1 eth to userB
And userB transfers 1 eth to userC
When userB starts to exit 1 eth from plasma cash
Then root chain got the start-exit record from coin spent challenge
When userC challenges the coin spent exit
Then the challenge is successful and root chain cancels the coin spent exit
11 changes: 4 additions & 7 deletions integration_tests/features/steps/basic_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from behave import given, then, when
from web3.auto import w3

from integration_tests.features.utils import address_equals, has_value
from plasma_cash.dependency_config import container

userA = '0xb83e232458A092696bE9717045d9A605FB0FEc2b'
Expand All @@ -16,10 +17,6 @@
TRANSFER_TX_BLOCK = 2


def address_equals(address1, address2):
return w3.toChecksumAddress(address1) == w3.toChecksumAddress(address2)


@given('userA has {amount:d} eth in root chain')
def userA_has_some_amount_of_eth_in_root_chain(context, amount):
userA_balance = w3.eth.getBalance(userA)
Expand Down Expand Up @@ -87,7 +84,7 @@ def userB_start_exit_some_eth_from_plasma_cash(context, amount):
client.submit_block(submit_block_sig)


@then('root chain got userB start exit {amount:d} eth event')
def root_chain_got_userB_start_exit_event(context, amount):
@then('root chain got userB start exit {amount:d} eth')
def root_chain_got_userB_start_exit(context, amount):
root_chain = container.get_root_chain()
assert root_chain.functions.exits(uid).call({'from': userA}) != [False, 0, 0, b'', 0, b'']
assert has_value(root_chain.functions.exits(uid).call({'from': userA}))
101 changes: 101 additions & 0 deletions integration_tests/features/steps/challenge_double_spending_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import rlp
import time

from behave import given, then, when
from ethereum import utils

from integration_tests.features.utils import has_value
from plasma_cash.child_chain.transaction import Transaction
from plasma_cash.dependency_config import container
from plasma_cash.utils.merkle.sparse_merkle_tree import SparseMerkleTree

operator = '0x3B0884f4E50e9BC2CE9b224aB72feA89a81CDF7c'
userA = '0xb83e232458A092696bE9717045d9A605FB0FEc2b'
userB = '0x08d92dcA9038eA9433254996a2D4F08D43BE8227'
userC = '0xDF29cFbD5d793Fa5B22D5c730A8E8450740C6f8f'
operator_key = '0xa18969817c2cefadf52b93eb20f917dce760ce13b2ac9025e0361ad1e7a1d448'
userA_key = '0xe4807cf08191b310fe1821e6e5397727ee6bc694e92e25115eca40114e3a4e6b'
eth_currency = '0x0000000000000000000000000000000000000000'
uid = 1693390459388381052156419331572168595237271043726428428352746834777341368960

DEPOSIT_TX_BLOCK = 1
TRANSFER_TX_1_BLOCK = 2
TRANSFER_TX_2_BLOCK = 3


@given('userA deposits {amount:d} eth in plasma cash')
def userA_deposits_some_amount_of_eth_in_plasma_cash(context, amount):
client = container.get_client()
client.deposit(
amount=amount,
depositor=userA,
currency=eth_currency
)
time.sleep(5)
client.submit_block(operator_key)


@given('userA transfers {amount:d} eth to userB')
def userA_transfers_some_eth_to_userB(context, amount):
prev_block = DEPOSIT_TX_BLOCK
client = container.get_client()
client.send_transaction(prev_block, uid, amount, userB, userA_key)
client.submit_block(operator_key)


@given('userA tries to double spend {amount:d} eth to userC')
def userA_tries_to_double_spend_some_eth_to_userC(context, amount):
invalid_tx = Transaction(DEPOSIT_TX_BLOCK, uid, 1, utils.normalize_address(userC))
invalid_tx.sign(utils.normalize_key(userA_key))
invalid_tx_merkle = SparseMerkleTree(257, {uid: invalid_tx.merkle_hash})

root_chain = container.get_root_chain()
root_chain.functions.submitBlock(invalid_tx_merkle.root, TRANSFER_TX_2_BLOCK).transact({
'from': operator
})


@when('userC starts to exit {amount:d} eth from plasma cash')
def userC_starts_to_exit_some_eth_from_plasma_cash(context, amount):
client = container.get_client()

deposit_block = client.get_block(DEPOSIT_TX_BLOCK)
deposit_tx = deposit_block.get_tx_by_uid(uid)
deposit_block.merklize_transaction_set()
deposit_tx_proof = deposit_block.merkle.create_merkle_proof(uid)

# invalid tx doesn't exist in child chain
invalid_tx = Transaction(DEPOSIT_TX_BLOCK, uid, 1, utils.normalize_address(userC))
invalid_tx.sign(utils.normalize_key(userA_key))
invalid_tx_merkle = SparseMerkleTree(257, {uid: invalid_tx.merkle_hash})
invalid_tx_proof = invalid_tx_merkle.create_merkle_proof(uid)

root_chain = container.get_root_chain()
root_chain.functions.startExit(
rlp.encode(deposit_tx),
deposit_tx_proof,
DEPOSIT_TX_BLOCK,
rlp.encode(invalid_tx),
invalid_tx_proof,
TRANSFER_TX_2_BLOCK
).transact({'from': userC})
time.sleep(5)


@then('root chain got the start-exit record from double spending challenge')
def root_chain_got_the_start_exit_record_from_double_spending_challenge(context):
root_chain = container.get_root_chain()
assert has_value(root_chain.functions.exits(uid).call({'from': userC}))


@when('userB challenges the exit')
def userC_challenges_the_exit(context):
client = container.get_client()
client.challenge_exit(userC, uid, tx_blk_num=TRANSFER_TX_1_BLOCK)
time.sleep(5)


@then('the challenge is successful and root chain cancels the exit')
def the_challenge_is_successful_and_root_chain_cancels_the_exit(context):
root_chain = container.get_root_chain()
assert not has_value(root_chain.functions.exits(uid).call({'from': userB}))
124 changes: 124 additions & 0 deletions integration_tests/features/steps/challenge_history_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import rlp
import time

from behave import given, then, when

from integration_tests.features.utils import has_value
from plasma_cash.dependency_config import container

userA = '0xb83e232458A092696bE9717045d9A605FB0FEc2b'
userB = '0x08d92dcA9038eA9433254996a2D4F08D43BE8227'
userC = '0xDF29cFbD5d793Fa5B22D5c730A8E8450740C6f8f'
userD = '0xc5016A1Dc1F2556FD237AbBa2681D221Edf31A20'
operator_key = '0xa18969817c2cefadf52b93eb20f917dce760ce13b2ac9025e0361ad1e7a1d448'
userA_key = '0xe4807cf08191b310fe1821e6e5397727ee6bc694e92e25115eca40114e3a4e6b'
userB_key = '0xee092298d0c0db61969cc4466d57571cf3ca36ca62db94273d5c1513312aeb30'
userC_key = '0x8af3051eb765261b245d586a88700e606431b199f2cce4c825d2b1921086b35c'
eth_currency = '0x0000000000000000000000000000000000000000'
uid = 1693390459388381052156419331572168595237271043726428428352746834777341368960

DEPOSIT_TX_BLOCK = 1
TRANSFER_TX_1_BLOCK = 2
TRANSFER_TX_2_BLOCK = 3
TRANSFER_TX_3_BLOCK = 4


@given('userA deposits {amount:d} eth in plasma cash')
def userA_deposits_some_amount_of_eth_in_plasma_cash(context, amount):
client = container.get_client()
client.deposit(
amount=amount,
depositor=userA,
currency=eth_currency
)
time.sleep(5)
client.submit_block(operator_key)


@given('userA transfers {amount:d} eth to userB')
def userA_transfers_some_eth_to_userB(context, amount):
prev_block = DEPOSIT_TX_BLOCK
client = container.get_client()
client.send_transaction(prev_block, uid, amount, userB, userA_key)
client.submit_block(operator_key)


@given('userB transfers {amount:d} eth to userC')
def userB_transfers_some_eth_to_userC(context, amount):
prev_block = TRANSFER_TX_1_BLOCK
client = container.get_client()
client.send_transaction(prev_block, uid, amount, userC, userB_key)
client.submit_block(operator_key)


@given('userC transfers {amount:d} eth to userD')
def userC_transfers_some_eth_to_userD(context, amount):
prev_block = TRANSFER_TX_2_BLOCK
client = container.get_client()
client.send_transaction(prev_block, uid, amount, userD, userC_key)
client.submit_block(operator_key)


@when('userD starts to exit {amount:d} eth from plasma cash')
def userD_starts_to_exit_some_eth_from_plasma_cash(context, amount):
client = container.get_client()
client.start_exit(
userD,
uid,
prev_tx_blk_num=TRANSFER_TX_2_BLOCK,
tx_blk_num=TRANSFER_TX_3_BLOCK
)
time.sleep(5)
client.submit_block(operator_key)


@then('root chain got the start-exit record from history challenge')
def root_chain_got_the_start_exit_record_from_history_challenge(context):
root_chain = container.get_root_chain()
assert has_value(root_chain.functions.exits(uid).call({'from': userD}))


@when('userB challenges the exit history with deposit tx')
def userC_challenges_the_spent_coin_exit(context):
client = container.get_client()
client.challenge_exit(userB, uid, tx_blk_num=DEPOSIT_TX_BLOCK)
time.sleep(5)


@then('root chain got the challenge record')
def then_root_chain_got_the_challenge_record(context):
client = container.get_client()
deposit_block = client.get_block(DEPOSIT_TX_BLOCK)
deposit_tx = deposit_block.get_tx_by_uid(uid)

root_chain = container.get_root_chain()
assert root_chain.functions.isChallengeExisted(
uid, rlp.encode(deposit_tx)
).call({'from': userB})


@when('userD responds to the history challenge')
def userD_responds_to_the_history_challenge(context):
client = container.get_client()
deposit_block = client.get_block(DEPOSIT_TX_BLOCK)
deposit_tx = deposit_block.get_tx_by_uid(uid)

client.respond_challenge_exit(
userD,
rlp.encode(deposit_tx),
uid,
tx_blk_num=TRANSFER_TX_1_BLOCK
)
time.sleep(5)


@then('root chain cancels the challenge')
def then_root_chain_cancels_the_challenge(context):
client = container.get_client()
deposit_block = client.get_block(DEPOSIT_TX_BLOCK)
deposit_tx = deposit_block.get_tx_by_uid(uid)

root_chain = container.get_root_chain()
assert not root_chain.functions.isChallengeExisted(
uid, rlp.encode(deposit_tx)
).call({'from': userD})
75 changes: 75 additions & 0 deletions integration_tests/features/steps/challenge_spent_coin_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import time

from behave import given, then, when

from integration_tests.features.utils import has_value
from plasma_cash.dependency_config import container

userA = '0xb83e232458A092696bE9717045d9A605FB0FEc2b'
userB = '0x08d92dcA9038eA9433254996a2D4F08D43BE8227'
userC = '0xDF29cFbD5d793Fa5B22D5c730A8E8450740C6f8f'
operator_key = '0xa18969817c2cefadf52b93eb20f917dce760ce13b2ac9025e0361ad1e7a1d448'
userA_key = '0xe4807cf08191b310fe1821e6e5397727ee6bc694e92e25115eca40114e3a4e6b'
userB_key = '0xee092298d0c0db61969cc4466d57571cf3ca36ca62db94273d5c1513312aeb30'
eth_currency = '0x0000000000000000000000000000000000000000'
uid = 1693390459388381052156419331572168595237271043726428428352746834777341368960

DEPOSIT_TX_BLOCK = 1
TRANSFER_TX_1_BLOCK = 2
TRANSFER_TX_2_BLOCK = 3


@given('userA deposits {amount:d} eth in plasma cash')
def userA_deposits_some_amount_of_eth_in_plasma_cash(context, amount):
client = container.get_client()
client.deposit(
amount=amount,
depositor=userA,
currency=eth_currency
)
time.sleep(5)
client.submit_block(operator_key)


@given('userA transfers {amount:d} eth to userB')
def userA_transfers_some_eth_to_userB(context, amount):
prev_block = DEPOSIT_TX_BLOCK
client = container.get_client()
client.send_transaction(prev_block, uid, amount, userB, userA_key)
client.submit_block(operator_key)


@given('userB transfers {amount:d} eth to userC')
def userB_transfers_some_eth_to_userC(context, amount):
prev_block = TRANSFER_TX_1_BLOCK
client = container.get_client()
client.send_transaction(prev_block, uid, amount, userC, userB_key)
client.submit_block(operator_key)


@when('userB starts to exit {amount:d} eth from plasma cash')
def userB_starts_to_exit_some_eth_from_plasma_cash(context, amount):
client = container.get_client()
client.start_exit(userB, uid, prev_tx_blk_num=DEPOSIT_TX_BLOCK, tx_blk_num=TRANSFER_TX_1_BLOCK)
time.sleep(5)
client.submit_block(operator_key)


@then('root chain got the start-exit record from coin spent challenge')
def root_chain_got_the_start_exit_record_from_coin_spent_challenge(context):
root_chain = container.get_root_chain()
assert has_value(root_chain.functions.exits(uid).call({'from': userB}))


@when('userC challenges the coin spent exit')
def userC_challenges_the_coin_spent_exit(context):
client = container.get_client()
client.challenge_exit(userC, uid, tx_blk_num=TRANSFER_TX_2_BLOCK)
time.sleep(5)
client.submit_block(operator_key)


@then('the challenge is successful and root chain cancels the coin spent exit')
def the_challenge_is_successful_and_root_chain_cancels_the_spent_coin_exit(context):
root_chain = container.get_root_chain()
assert not has_value(root_chain.functions.exits(uid).call({'from': userC}))
11 changes: 11 additions & 0 deletions integration_tests/features/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from web3.auto import w3


def address_equals(address1, address2):
return w3.toChecksumAddress(address1) == w3.toChecksumAddress(address2)


def has_value(obj):
if len(obj) > 0:
return obj[0]
return False
2 changes: 1 addition & 1 deletion plasma_cash/child_chain/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def hash(self):

def merklize_transaction_set(self):
hashed_transaction_dict = {tx.uid: tx.merkle_hash for tx in self.transaction_set}
self.merkle = SparseMerkleTree(256, hashed_transaction_dict)
self.merkle = SparseMerkleTree(257, hashed_transaction_dict)
return self.merkle.root

def add_tx(self, tx):
Expand Down
Loading

0 comments on commit 2652408

Please sign in to comment.