Skip to content
This repository was archived by the owner on Feb 16, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions hmt_escrow/eth_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,19 @@ def get_contract_interface(contract_entrypoint):
return contract_interface


def get_hmtoken_interface():
"""Retrieve the HMToken interface.

Returns:
Contract interface: returns the HMToken interface solidity contract.

"""

return get_contract_interface(
"{}/HMTokenInterface.sol:HMTokenInterface".format(CONTRACT_FOLDER)
)


def get_hmtoken(hmtoken_addr=HMTOKEN_ADDR, hmt_server_addr: str = None) -> Contract:
"""Retrieve the HMToken contract from a given address.

Expand All @@ -214,9 +227,7 @@ def get_hmtoken(hmtoken_addr=HMTOKEN_ADDR, hmt_server_addr: str = None) -> Contr

"""
w3 = get_w3(hmt_server_addr)
contract_interface = get_contract_interface(
"{}/HMTokenInterface.sol:HMTokenInterface".format(CONTRACT_FOLDER)
)
contract_interface = get_hmtoken_interface()
contract = w3.eth.contract(address=hmtoken_addr, abi=contract_interface["abi"])
return contract

Expand Down Expand Up @@ -437,3 +448,28 @@ def set_pub_key_at_addr(
}

return handle_transaction(txn_func, *func_args, **txn_info)


def get_entity_topic(contract_interface: Dict, name: str) -> str:
"""
Args:
contract_interface (Dict): contract inteface.

name (str): event name to find in abi.

Returns
str: returns keccak_256 hash of event name with input parameters.
"""
s = ""

for entity in contract_interface["abi"]:
event_name = entity.get("name")
if event_name == name:
s += event_name + "("
inputs = entity.get("inputs", [])
input_types = []
for input in inputs:
input_types.append(input.get("internalType"))
s += ",".join(input_types) + ")"

return Web3.keccak(text=s)
43 changes: 39 additions & 4 deletions hmt_escrow/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from hmt_escrow import utils
from hmt_escrow.eth_bridge import (
get_hmtoken,
get_hmtoken_interface,
get_entity_topic,
get_escrow,
get_factory,
deploy_factory,
Expand All @@ -26,6 +28,7 @@
from hmt_escrow.storage import download, upload, get_public_bucket_url, get_key_from_url

GAS_LIMIT = int(os.getenv("GAS_LIMIT", 4712388))
TRANSFER_EVENT = get_entity_topic(get_hmtoken_interface(), "Transfer")

# Explicit env variable that will use s3 for storing results.

Expand Down Expand Up @@ -617,6 +620,7 @@ def bulk_payout(
bool: returns True if paying to ethereum addresses and oracles succeeds.

"""
bulk_paid = False
txn_event = "Bulk payout"
txn_func = self.job_contract.functions.bulkPayOut
txn_info = {
Expand Down Expand Up @@ -646,23 +650,35 @@ def bulk_payout(
func_args = [eth_addrs, hmt_amounts, url, hash_, 1]

try:
handle_transaction_with_retry(txn_func, self.retry, *func_args, **txn_info)
return self._bulk_paid() is True
tx_receipt = handle_transaction_with_retry(
txn_func,
self.retry,
*func_args,
**txn_info,
)
bulk_paid = self._check_transfer_event(tx_receipt)

except Exception as e:
LOG.warn(
f"{txn_event} failed with main credentials: {self.gas_payer}, {self.gas_payer_priv} due to {e}. Using secondary ones..."
)

if bulk_paid:
return bulk_paid

LOG.warn(
f"{txn_event} failed with main credentials: {self.gas_payer}, {self.gas_payer_priv}. Using secondary ones..."
)

raffle_txn_res = self._raffle_txn(
self.multi_credentials, txn_func, func_args, txn_event
)
bulk_paid = raffle_txn_res["txn_succeeded"]
bulk_paid = self._check_transfer_event(raffle_txn_res["tx_receipt"])

if not bulk_paid:
LOG.warning(f"{txn_event} failed with all credentials.")

return bulk_paid is True
return bulk_paid

def abort(self) -> bool:
"""Kills the contract and returns the HMT back to the gas payer.
Expand Down Expand Up @@ -1508,3 +1524,22 @@ def _raffle_txn(self, multi_creds, txn_func, txn_args, txn_event) -> RaffleTxn:
)

return {"txn_succeeded": txn_succeeded, "tx_receipt": tx_receipt}

def _check_transfer_event(self, tx_receipt: Optional[TxReceipt]) -> bool:
"""
Check if transaction receipt has bulkTransfer event, to make sure that transaction was successful.

Args:
tx_receipt (Optional[TxReceipt]): a dict with transaction receipt.

Returns:
bool: returns True if transaction has bulkTransfer event, otherwise returns False.
"""
if not tx_receipt:
return False

for log in tx_receipt.get("logs", {}):
for topic in log["topics"]:
if TRANSFER_EVENT == topic:
return True
return False
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hmt-escrow",
"version": "0.14.6",
"version": "0.14.7",
"description": "Launch escrow contracts to the HUMAN network",
"main": "truffle.js",
"directories": {
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setuptools.setup(
name="hmt-escrow",
version="0.14.6",
version="0.14.7",
author="HUMAN Protocol",
description="A python library to launch escrow contracts to the HUMAN network.",
url="https://github.com/humanprotocol/hmt-escrow",
Expand Down
34 changes: 28 additions & 6 deletions test/hmt_escrow/test_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ def test_job_bulk_payout(self):
]
self.assertTrue(self.job.bulk_payout(payouts, {}, self.rep_oracle_pub_key))

def test_job_bulk_payout_with_encryption_option(self):
"""Tests whether final results must be persisted in storage encrypted or plain."""
def test_job_bulk_payout_with_false_encryption_option(self):
"""Test that final results are stored encrypted"""
job = Job(self.credentials, manifest)
self.assertEqual(job.launch(self.rep_oracle_pub_key), True)
self.assertEqual(job.setup(), True)
Expand All @@ -253,10 +253,24 @@ def test_job_bulk_payout_with_encryption_option(self):
encrypt_data=False,
use_public_bucket=False,
)
mock_upload.reset_mock()

# Testing option as: encrypt final results: encrypt_final_results=True
def test_job_bulk_payout_with_true_encryption_option(self):
"""Test that final results are stored uncrypted"""
job = Job(self.credentials, manifest)
self.assertEqual(job.launch(self.rep_oracle_pub_key), True)
self.assertEqual(job.setup(), True)

payouts = [("0x852023fbb19050B8291a335E5A83Ac9701E7B4E6", Decimal("100.0"))]

final_results = {"results": 0}

mock_upload = MagicMock(return_value=("hash", "url"))

# Testing option as: encrypt final results: encrypt_final_results=True
with patch("hmt_escrow.job.upload") as mock_upload:
# Bulk payout with final results as plain (not encrypted)
mock_upload.return_value = ("hash", "url")

job.bulk_payout(
payouts=payouts,
results=final_results,
Expand Down Expand Up @@ -325,15 +339,19 @@ def test_job_bulk_payout_with_full_qualified_url(self):
self.assertEqual(job.setup(), True)

payouts = [("0x852023fbb19050B8291a335E5A83Ac9701E7B4E6", Decimal("100.0"))]

final_results = {"results": 0}

with patch(
"hmt_escrow.job.handle_transaction_with_retry"
) as transaction_retry_mock, patch("hmt_escrow.job.upload") as upload_mock:
) as transaction_retry_mock, patch(
"hmt_escrow.job.upload"
) as upload_mock, patch.object(
Job, "_check_transfer_event"
) as _check_transfer_event_mock:
key = "abcdefg"
hash_ = f"s3{key}"
upload_mock.return_value = hash_, key
_check_transfer_event_mock.return_value = True

# Bulk payout with option to store final results privately
job.bulk_payout(
Expand Down Expand Up @@ -397,6 +415,10 @@ def test_retrieving_encrypted_final_results(self):
self.assertEqual(persisted_final_results, final_results)

# Bulk payout with encryption OFF
job = Job(self.credentials, manifest)
self.assertEqual(job.launch(self.rep_oracle_pub_key), True)
self.assertEqual(job.setup(), True)

job.bulk_payout(
payouts=payouts,
results=final_results,
Expand Down