diff --git a/plasma_cash/child_chain/child_chain.py b/plasma_cash/child_chain/child_chain.py index b10178a..e44c54c 100644 --- a/plasma_cash/child_chain/child_chain.py +++ b/plasma_cash/child_chain/child_chain.py @@ -18,9 +18,10 @@ class ChildChain(object): - def __init__(self, authority, root_chain, db): + def __init__(self, key, root_chain, db): + self.key = utils.normalize_key(key) + self.authority = utils.privtoaddr(self.key) self.root_chain = root_chain - self.authority = authority self.db = db self.current_block = Block() self.current_block_number = self.db.get_current_block_num() @@ -66,9 +67,15 @@ def submit_block(self, sig): merkle_hash = self.current_block.merklize_transaction_set() authority_address = w3.toChecksumAddress('0x' + self.authority.hex()) - self.root_chain.functions.submitBlock(merkle_hash, self.current_block_number).transact( - {'from': authority_address} - ) + tx = (self.root_chain.functions + .submitBlock(merkle_hash, self.current_block_number) + .buildTransaction({ + 'from': authority_address, + 'nonce': w3.eth.getTransactionCount(authority_address, 'pending') + })) + + signed = w3.eth.account.signTransaction(tx, self.key) + w3.eth.sendRawTransaction(signed.rawTransaction) emit('chain.block', self.current_block_number) self.db.save_block(self.current_block, self.current_block_number) diff --git a/plasma_cash/client/client.py b/plasma_cash/client/client.py index 95e01b5..90eab31 100644 --- a/plasma_cash/client/client.py +++ b/plasma_cash/client/client.py @@ -18,11 +18,17 @@ def __init__(self, root_chain, child_chain, key): def address(self): return w3.toChecksumAddress(utils.privtoaddr(self.key)) + def _sign_and_send_tx(self, tx): + tx['nonce'] = w3.eth.getTransactionCount(self.address, 'pending') + signed = w3.eth.account.signTransaction(tx, self.key) + w3.eth.sendRawTransaction(signed.rawTransaction) + def deposit(self, amount, currency): value = w3.toWei(amount, 'ether') if currency == '0x' + '00' * 20 else 0 - self.root_chain.functions.deposit(currency, amount).transact( + tx = self.root_chain.functions.deposit(currency, amount).buildTransaction( {'from': self.address, 'value': value} ) + self._sign_and_send_tx(tx) def submit_block(self): # TODO: this method should be a cron job in child chain @@ -65,14 +71,15 @@ def start_exit(self, uid, prev_tx_blk_num, tx_blk_num): block.merklize_transaction_set() tx_proof = block.merkle.create_merkle_proof(uid) - self.root_chain.functions.startExit( + tx = self.root_chain.functions.startExit( rlp.encode(prev_tx), prev_tx_proof, prev_tx_blk_num, rlp.encode(tx), tx_proof, tx_blk_num - ).transact({'from': self.address}) + ).buildTransaction({'from': self.address}) + self._sign_and_send_tx(tx) def challenge_exit(self, uid, tx_blk_num): block = self.get_block(tx_blk_num) @@ -81,9 +88,10 @@ def challenge_exit(self, uid, tx_blk_num): block.merklize_transaction_set() tx_proof = block.merkle.create_merkle_proof(uid) - self.root_chain.functions.challengeExit( + tx = self.root_chain.functions.challengeExit( uid, rlp.encode(challenge_tx), tx_proof, tx_blk_num - ).transact({'from': self.address}) + ).buildTransaction({'from': self.address}) + self._sign_and_send_tx(tx) def respond_challenge_exit(self, challenge_tx, uid, tx_blk_num): block = self.get_block(tx_blk_num) @@ -92,9 +100,11 @@ def respond_challenge_exit(self, challenge_tx, uid, tx_blk_num): block.merklize_transaction_set() tx_proof = block.merkle.create_merkle_proof(uid) - self.root_chain.functions.respondChallengeExit( + tx = self.root_chain.functions.respondChallengeExit( uid, challenge_tx, rlp.encode(respond_tx), tx_proof, tx_blk_num - ).transact({'from': self.address}) + ).buildTransaction({'from': self.address}) + self._sign_and_send_tx(tx) def finalize_exit(self, uid): - self.root_chain.functions.finalizeExit(uid).transact({'from': self.address}) + tx = self.root_chain.functions.finalizeExit(uid).buildTransaction({'from': self.address}) + self._sign_and_send_tx(tx) diff --git a/plasma_cash/client/history.py b/plasma_cash/client/history.py index 511a8e3..0760ac1 100644 --- a/plasma_cash/client/history.py +++ b/plasma_cash/client/history.py @@ -1,5 +1,5 @@ import rlp -from rlp.sedes import big_endian_int, binary, CountableList +from rlp.sedes import CountableList, big_endian_int, binary from .exceptions import TxHistoryNotFoundException diff --git a/plasma_cash/dependency_config.py b/plasma_cash/dependency_config.py index f5ac684..a4058f8 100644 --- a/plasma_cash/dependency_config.py +++ b/plasma_cash/dependency_config.py @@ -1,9 +1,9 @@ import os from plasma_cash.child_chain.child_chain import ChildChain +from plasma_cash.child_chain.child_chain_client import ChildChainClient from plasma_cash.child_chain.db.leveldb import LevelDb from plasma_cash.child_chain.db.memory_db import MemoryDb -from plasma_cash.child_chain.child_chain_client import ChildChainClient from plasma_cash.config import PROJECT_DIR, db_config, plasma_config from plasma_cash.root_chain.deployer import Deployer @@ -38,10 +38,10 @@ def get_root_chain(self): def get_child_chain(self): if self._child_chain is None: - authority = plasma_config['AUTHORITY'] + key = plasma_config['AUTHORITY_KEY'] root_chain = self.get_root_chain() db = self.get_db() - self._child_chain = ChildChain(authority, root_chain, db) + self._child_chain = ChildChain(key, root_chain, db) return self._child_chain def get_child_chain_client(self): diff --git a/plasma_cash/root_chain/deployer.py b/plasma_cash/root_chain/deployer.py index 5ea667c..9297279 100644 --- a/plasma_cash/root_chain/deployer.py +++ b/plasma_cash/root_chain/deployer.py @@ -49,14 +49,18 @@ def deploy_contract(self, path, args=(), gas=4410000): abi, bytecode, contract_name = self.compile_contract(path, args) contract = w3.eth.contract(abi=abi, bytecode=bytecode) - # Get transaction hash from deployed contract - tx_hash = contract.deploy( - transaction={'from': w3.eth.accounts[0], 'gas': gas}, - args=args - ) + address = w3.toChecksumAddress(plasma_config['AUTHORITY'].hex()) + key = plasma_config['AUTHORITY_KEY'] + tx = contract.constructor().buildTransaction({ + 'from': address, + 'nonce': w3.eth.getTransactionCount(address, 'pending') + }) + signed = w3.eth.account.signTransaction(tx, key) + tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction) + tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash) - print('Successfully deployed {} contract with tx hash {}!'.format( - contract_name, tx_hash.hex())) + print('Successfully deployed {} contract with tx hash {} in contract address {}!'.format( + contract_name, tx_hash.hex(), tx_receipt.contractAddress)) def get_contract(self, path): file_name = path.split('/')[1] diff --git a/unit_tests/child_chain/test_child_chain.py b/unit_tests/child_chain/test_child_chain.py index ce766b2..df1e0f0 100644 --- a/unit_tests/child_chain/test_child_chain.py +++ b/unit_tests/child_chain/test_child_chain.py @@ -3,7 +3,7 @@ import pytest import rlp from ethereum import utils as eth_utils -from mockito import ANY, expect, mock, verify, when +from mockito import ANY, expect, mock, when from plasma_cash.child_chain.block import Block from plasma_cash.child_chain.child_chain import ChildChain @@ -20,8 +20,7 @@ class TestChildChain(UnstubMixin): - DUMMY_AUTHORITY = b"\x14\x7f\x08\x1b\x1a6\xa8\r\xf0Y\x15(ND'\xc1\xf6\xdd\x98\x84" - DUMMY_SIG = '01' * 65 # sig for DUMMY_AUTHORITY + DUMMY_KEY = '0xa18969817c2cefadf52b93eb20f917dce760ce13b2ac9025e0361ad1e7a1d448' DUMMY_TX_NEW_OWNER = b'\xfd\x02\xec\xeeby~u\xd8k\xcf\xf1d.\xb0\x84J\xfb(\xc7' @pytest.fixture(scope='function') @@ -40,7 +39,7 @@ def child_chain(self, root_chain, db): expect(root_chain).eventFilter('Deposit', {'fromBlock': 0}) expect(Thread).start() - child_chain = ChildChain(self.DUMMY_AUTHORITY, root_chain, db) + child_chain = ChildChain(self.DUMMY_KEY, root_chain, db) # create a dummy transaction tx = Transaction(prev_block=0, uid=1, amount=10, new_owner=DUMMY_TX_OWNER) @@ -55,7 +54,7 @@ def test_constructor(self, root_chain, db): expect(root_chain).eventFilter('Deposit', {'fromBlock': 0}) expect(Thread).start() - ChildChain(self.DUMMY_AUTHORITY, root_chain, db) + ChildChain(self.DUMMY_KEY, root_chain, db) def test_apply_deposit(self, child_chain): DUMMY_AMOUNT = 123 @@ -81,18 +80,27 @@ def test_apply_deposit_should_fail_when_is_already_applied(self, child_chain, ro def test_submit_block(self, child_chain, root_chain): DUMMY_MERKLE = 'merkle hash' - MOCK_TRANSACT = mock() + MOCK_FUNCTION = mock() + DUMMY_NONCE = 100 + DUMMY_TX = {'nonce': DUMMY_NONCE, 'gas': 100, 'gasPrice': 100} block_number = child_chain.current_block_number block = child_chain.current_block when(child_chain.current_block).merklize_transaction_set().thenReturn(DUMMY_MERKLE) (when(root_chain.functions) .submitBlock(DUMMY_MERKLE, block_number) - .thenReturn(MOCK_TRANSACT)) + .thenReturn(MOCK_FUNCTION)) + when(MOCK_FUNCTION).buildTransaction(ANY).thenReturn(DUMMY_TX) + (when('plasma_cash.child_chain.child_chain') + .get_sender(ANY, ANY).thenReturn(child_chain.authority)) + (when('plasma_cash.child_chain.child_chain.w3.eth') + .getTransactionCount(ANY, ANY).thenReturn(DUMMY_NONCE)) + (when('plasma_cash.child_chain.child_chain.w3.eth') + .sendRawTransaction(ANY).thenReturn(None)) + + DUMMY_SIG = '01' * 65 + child_chain.submit_block(DUMMY_SIG) - child_chain.submit_block(self.DUMMY_SIG) - - verify(MOCK_TRANSACT).transact(ANY) assert child_chain.current_block_number == block_number + 1 assert child_chain.db.get_block(block_number) == block assert child_chain.current_block == Block() diff --git a/unit_tests/client/test_client.py b/unit_tests/client/test_client.py index 27f7d1f..c066c55 100644 --- a/unit_tests/client/test_client.py +++ b/unit_tests/client/test_client.py @@ -40,16 +40,37 @@ def test_address(self, client): assert client.address == DUMMY_ADDRESS + def test_sign_and_send_tx(self, client): + DUMMY_NONCE = 123 + DUMMY_TX = { + 'gas': 100, + 'gasPrice': 100, + 'nonce': DUMMY_NONCE + } + (when('plasma_cash.client.client.w3.eth') + .getTransactionCount(ANY, ANY).thenReturn(DUMMY_NONCE)) + when('plasma_cash.client.client.w3.eth').sendRawTransaction(ANY).thenReturn(None) + + client._sign_and_send_tx(DUMMY_TX) + + verify('plasma_cash.client.client.w3.eth').sendRawTransaction(ANY) + def test_deposit(self, client, root_chain): - MOCK_TRANSACT = mock() DUMMY_AMOUNT = 1 DUMMY_CURRENCY = '0x0000000000000000000000000000000000000000' - when(root_chain.functions).deposit(DUMMY_CURRENCY, DUMMY_AMOUNT).thenReturn(MOCK_TRANSACT) + MOCK_FUNCTION = mock() + TX = mock() + when(root_chain.functions).deposit(DUMMY_CURRENCY, DUMMY_AMOUNT).thenReturn(MOCK_FUNCTION) + when(MOCK_FUNCTION).buildTransaction({ + 'from': client.address, + 'value': DUMMY_AMOUNT * 10**18 + }).thenReturn(TX) + when(client)._sign_and_send_tx(ANY).thenReturn(None) client.deposit(DUMMY_AMOUNT, DUMMY_CURRENCY) - verify(MOCK_TRANSACT).transact({'from': client.address, 'value': DUMMY_AMOUNT * 10**18}) + verify(client)._sign_and_send_tx(ANY) def test_submit_block(self, client, child_chain): MOCK_HASH = 'mock hash' @@ -59,6 +80,7 @@ def test_submit_block(self, client, child_chain): when(client).get_current_block().thenReturn(MOCK_BLOCK) when('plasma_cash.client.client').sign(MOCK_HASH, client.key).thenReturn(MOCK_SIG) + when(client)._sign_and_send_tx(ANY).thenReturn(None) client.submit_block() @@ -118,7 +140,6 @@ def test_get_proof(self, child_chain, client): assert client.get_proof(DUMMY_BLOCK_NUM, DUMMY_UID) == DUMMY_PROOF def test_start_exit(self, client, root_chain): - MOCK_TRANSACT = mock() MOCK_PREVIOUS_BLOCK = mock() MOCK_BLOCK = mock() @@ -139,7 +160,7 @@ def test_start_exit(self, client, root_chain): DUMMY_ENCODED_TX, DUMMY_TX_PROOF, DUMMY_TX_BLK_NUM - ).thenReturn(MOCK_TRANSACT) + ).thenReturn(mock()) when(client).get_block(DUMMY_PREVIOUS_TX_BLK_NUM).thenReturn(MOCK_PREVIOUS_BLOCK) when(client).get_block(DUMMY_TX_BLK_NUM).thenReturn(MOCK_BLOCK) when(MOCK_PREVIOUS_BLOCK).get_tx_by_uid(DUMMY_UID).thenReturn(DUMMY_PREVIOUS_TX) @@ -159,14 +180,14 @@ def test_start_exit(self, client, root_chain): (when('plasma_cash.client.client.rlp') .encode(DUMMY_TX) .thenReturn(DUMMY_ENCODED_TX)) + when(client)._sign_and_send_tx(ANY).thenReturn(None) client.start_exit(DUMMY_UID, DUMMY_PREVIOUS_TX_BLK_NUM, DUMMY_TX_BLK_NUM) - verify(MOCK_TRANSACT).transact({'from': client.address}) + verify(client)._sign_and_send_tx(ANY) def test_challenge_exit(self, client, root_chain): MOCK_BLOCK = mock() - MOCK_TRANSACT = mock() DUMMY_UID = 'dummy uid' DUMMY_TX = 'dummy tx' @@ -189,15 +210,15 @@ def test_challenge_exit(self, client, root_chain): DUMMY_ENCODED_TX, DUMMY_TX_PROOF, DUMMY_TX_BLK_NUM - ).thenReturn(MOCK_TRANSACT) + ).thenReturn(mock()) + when(client)._sign_and_send_tx(ANY).thenReturn(None) client.challenge_exit(DUMMY_UID, DUMMY_TX_BLK_NUM) - verify(MOCK_TRANSACT).transact({'from': client.address}) + verify(client)._sign_and_send_tx(ANY) def test_respond_challenge_exit(self, client, root_chain): MOCK_BLOCK = mock() - MOCK_TRANSACT = mock() DUMMY_UID = 'dummy uid' DUMMY_CHALLENGE_TX = 'dummy challenge tx' @@ -222,18 +243,19 @@ def test_respond_challenge_exit(self, client, root_chain): DUMMY_ENCODED_TX, DUMMY_TX_PROOF, DUMMY_TX_BLK_NUM - ).thenReturn(MOCK_TRANSACT) + ).thenReturn(mock()) + when(client)._sign_and_send_tx(ANY).thenReturn(None) client.respond_challenge_exit(DUMMY_CHALLENGE_TX, DUMMY_UID, DUMMY_TX_BLK_NUM) - verify(MOCK_TRANSACT).transact({'from': client.address}) + verify(client)._sign_and_send_tx(ANY) def test_finalize_exit(self, client, root_chain): DUMMY_UID = 'dummy uid' - MOCK_TRANSACT = mock() - when(root_chain.functions).finalizeExit(DUMMY_UID).thenReturn(MOCK_TRANSACT) + when(root_chain.functions).finalizeExit(DUMMY_UID).thenReturn(mock()) + when(client)._sign_and_send_tx(ANY).thenReturn(None) client.finalize_exit(DUMMY_UID) - verify(MOCK_TRANSACT).transact({'from': client.address}) + verify(client)._sign_and_send_tx(ANY) diff --git a/unit_tests/operator_cron_job/test_main.py b/unit_tests/operator_cron_job/test_main.py index 9d1e1f2..10c5100 100644 --- a/unit_tests/operator_cron_job/test_main.py +++ b/unit_tests/operator_cron_job/test_main.py @@ -26,7 +26,7 @@ def child_chain(self): def test_setup_job_handler(self, root_chain, child_chain): (when('plasma_cash.operator_cron_job.__main__.container') - .get_child_chain().thenReturn(child_chain)) + .get_child_chain_client().thenReturn(child_chain)) (when('plasma_cash.operator_cron_job.__main__.container') .get_root_chain().thenReturn(root_chain))