Skip to content
This repository has been archived by the owner on Jun 13, 2019. It is now read-only.

Commit

Permalink
Build, sign and send tx instead of using transact
Browse files Browse the repository at this point in the history
Previously, all our call to smart contract uses web3's `transact` api.
However, that api relies on the eth node to have account information in
its wallet. This is an unwanted feature that we want to remove. Also, this
would block us from using infura (web service), and also parity node would
need another manual action to approve account tx, which make previous code
impossible to use with the two eth nodes. This commit changes `transact` to
`buildTransaction` and then sign and send.
  • Loading branch information
boolafish authored and bun919tw committed Sep 24, 2018
1 parent e1006b5 commit 650d1a1
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 50 deletions.
17 changes: 12 additions & 5 deletions plasma_cash/child_chain/child_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
26 changes: 18 additions & 8 deletions plasma_cash/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
2 changes: 1 addition & 1 deletion plasma_cash/client/history.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
6 changes: 3 additions & 3 deletions plasma_cash/dependency_config.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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):
Expand Down
18 changes: 11 additions & 7 deletions plasma_cash/root_chain/deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
28 changes: 18 additions & 10 deletions unit_tests/child_chain/test_child_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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')
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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()
Expand Down
52 changes: 37 additions & 15 deletions unit_tests/client/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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()

Expand Down Expand Up @@ -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()

Expand All @@ -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)
Expand All @@ -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'
Expand All @@ -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'
Expand All @@ -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)
2 changes: 1 addition & 1 deletion unit_tests/operator_cron_job/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down

0 comments on commit 650d1a1

Please sign in to comment.