Skip to content

Commit

Permalink
Merge pull request #311 from dragonchain/master
Browse files Browse the repository at this point in the history
Release 4.4.0
  • Loading branch information
cheeseandcereal committed Feb 5, 2020
2 parents 79d73af + cc53cfc commit 47ecb3e
Show file tree
Hide file tree
Showing 38 changed files with 204 additions and 83 deletions.
1 change: 0 additions & 1 deletion .github/CODEOWNERS

This file was deleted.

2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.3.3
4.4.0
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## 4.4.0

- **Feature:**
- Modify L4 block schema to include chain name in the L4 block header
- Add new endpoint `DELETE /v1/contract/txn_type/<txn_type>` for deleting smart contracts by transaction type
- Add new endpoint `POST /v1/interchains/transaction/publish` for publishing a signed interchain transaction
- **Bugs:**
- Fix issue where L5 recovery queue would not process when trying to resolve already resolved claim-checks
- **Packaging:**
- Update redis, web3, and boto3 dependencies
- Update redisearch in helm chart to 1.6.7
- Update fwatchdog to 0.18.10 for OpenFaaS smart contracts
- **Development:**
- Removed all references about lab chain
- Remove codeowners

## 4.3.3

- **Bugs:**
Expand Down
2 changes: 1 addition & 1 deletion docs/deployment/deploying.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Once the values are set, install the helm chart with:
```sh
helm repo add dragonchain https://dragonchain-charts.s3.amazonaws.com
helm repo update
helm upgrade --install my-dragonchain --values opensource-config.yaml --namespace dragonchain dragonchain/dragonchain-k8s --version 1.0.7
helm upgrade --install my-dragonchain --values opensource-config.yaml --namespace dragonchain dragonchain/dragonchain-k8s --version 1.0.8
```

If you need to change any values AFTER the helm chart has already been
Expand Down
1 change: 1 addition & 0 deletions docs/usage/permissioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ custom schema:
| `interchains` | `create_interchain` | `create` | default |
| `interchains` | `update_interchain` | `update` | default |
| `interchains` | `create_interchain_transaction` | `create` | default |
| `interchains` | `publish_interchain_transaction` | `create` | default |
| `interchains` | `list_interchains` | `read` | default |
| `interchains` | `get_interchain` | `read` | default |
| `interchains` | `delete_interchain` | `delete` | default |
Expand Down
8 changes: 4 additions & 4 deletions dragonchain/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ class OpenFaasException(DragonchainException):
"""Exception raised when OpenFaaS returns with error status"""


class LabChainForbiddenException(DragonchainException):
"""Exception raised when lab chain action is not allowed"""


class NotFound(DragonchainException):
"""Exception raised when object is not found"""

Expand Down Expand Up @@ -155,6 +151,10 @@ class SanityCheckFailure(DragonchainException):
"""Exception raised when sanity check fails"""


class InterchainPublishError(DragonchainException):
"""Exception raised when an interchain publish action fails"""


class InterchainConnectionError(DragonchainException):
"""Exception raise when RPC / API call has an error"""

Expand Down
Binary file modified dragonchain/job_processor/bin/fwatchdog
Binary file not shown.
4 changes: 2 additions & 2 deletions dragonchain/job_processor/contract_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,10 @@ def get_openfaas_spec(self) -> dict:
"com.openfaas.scale.max": "20",
"com.openfaas.scale.factor": "20",
"com.dragonchain.id": INTERNAL_ID,
"com.openfaas.fwatchdog.version": "0.18.7", # Update this as the fwatchdog executable in bin is updates
"com.openfaas.fwatchdog.version": "0.18.10", # Update this as the fwatchdog executable in bin is updates
},
"limits": {"cpu": "0.50", "memory": "600M"},
"requests": {"cpu": "0.25", "memory": "600M"},
"requests": {"cpu": "0.1", "memory": "600M"},
}
_log.info(f"OpenFaaS spec: {spec}")
return spec
Expand Down
4 changes: 2 additions & 2 deletions dragonchain/job_processor/contract_job_utest.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,10 @@ def test_get_openfaas_spec(self, mock_get_id):
"com.openfaas.scale.factor": "20",
"com.openfaas.scale.max": "20",
"com.openfaas.scale.min": "1",
"com.openfaas.fwatchdog.version": "0.18.7",
"com.openfaas.fwatchdog.version": "0.18.10",
},
"limits": {"cpu": "0.50", "memory": "600M"},
"requests": {"cpu": "0.25", "memory": "600M"},
"requests": {"cpu": "0.1", "memory": "600M"},
"image": "/customer-contracts@sha256:imasha",
},
)
Expand Down
1 change: 1 addition & 0 deletions dragonchain/lib/dto/api_key_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def _check_create_transaction_permission(api_name_permissions: Dict[str, Any], e
"create_interchain": _check_default_endpoint_permission,
"update_interchain": _check_default_endpoint_permission,
"create_interchain_transaction": _check_default_endpoint_permission,
"publish_interchain_transaction": _check_default_endpoint_permission,
"list_interchains": _check_default_endpoint_permission,
"get_interchain": _check_default_endpoint_permission,
"delete_interchain": _check_default_endpoint_permission,
Expand Down
25 changes: 17 additions & 8 deletions dragonchain/lib/dto/bnb.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,22 @@ def sign_transaction(self, raw_transaction: Dict[str, Any]) -> str:
raise exceptions.BadRequest(f"[BINANCE] Error signing transaction: {e}")

# https://docs.binance.org/api-reference/node-rpc.html#622-broadcasttxcommit
def _publish_transaction(self, transaction_payload: str) -> str:
def publish_transaction(self, signed_transaction: str) -> str:
"""Publish an already signed transaction to this network
Args:
signed_transaction: The already signed transaction from self.sign_transaction
Returns:
The string of the published transaction hash
"""
_log.debug(f"[BINANCE] Publishing transaction {signed_transaction}")
response = self._call_node_rpc("broadcast_tx_commit", {"tx": signed_transaction})
response_json = response.json()
# cannot check HTTP status codes, errors will return 200 :
if response_json.get("error") is not None:
_log.warning(f"[BINANCE] Error response from Binance node: {response_json['error']['data']}")
return response_json["result"]["hash"] # transaction hash

def _publish_l5_transaction(self, transaction_payload: str) -> str:
"""Publish a transaction to this network with a certain data payload
Args:
transaction_payload: The arbitrary data to send with this transaction
Expand All @@ -351,13 +366,7 @@ def _publish_transaction(self, transaction_payload: str) -> str:
# send funds to yourself, avoid hardcoding a dummy recipient address
raw_transaction = {"amount": 1, "to_address": self.address, "symbol": "BNB", "memo": transaction_payload}
signed_tx = self.sign_transaction(raw_transaction)
_log.info(f"[BINANCE] Sending signed transaction: {signed_tx}")
response = self._call_node_rpc("broadcast_tx_commit", {"tx": signed_tx})
response_json = response.json()
# cannot check HTTP status codes, errors will return 200 :
if response_json.get("error") is not None:
_log.warning(f"[BINANCE] Error response from Binance node: {response_json['error']['data']}")
return response_json["result"]["hash"] # transaction hash
return self.publish_transaction(signed_tx)

# endpoints currently hit are:
# "status" (ping check)
Expand Down
4 changes: 2 additions & 2 deletions dragonchain/lib/dto/bnb_utest.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def test_sign_transaction(self, mock_encode):
self.assertEqual(response, "ZmFrZV9lbmNvZGVkX3R4bg==")
mock_encode.assert_called_once()

def test_publish_transaction(self):
def test_publish_l5_transaction(self):
fake_response = requests.Response()
fake_response._content = b'{"result": {"hash": "BOGUS_RESULT_HASH"}}'
self.client._build_transaction_msg = MagicMock(return_value={"built_tx": "fake"})
Expand All @@ -121,7 +121,7 @@ def test_publish_transaction(self):
fake_acct._content = b'{"sequence": 0, "account_number": 12345}'
self.client._call_node_api = MagicMock(return_value=fake_acct)
self.client._call_node_rpc = MagicMock(return_value=fake_response)
response = self.client._publish_transaction("DC-L5:_fake_L5_block_hash")
response = self.client._publish_l5_transaction("DC-L5:_fake_L5_block_hash")
self.assertEqual(response, "BOGUS_RESULT_HASH")
self.client._call_node_rpc.assert_called_once_with("broadcast_tx_commit", {"tx": "signed_tx"})

Expand Down
14 changes: 12 additions & 2 deletions dragonchain/lib/dto/btc.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,17 @@ def get_private_key(self) -> str:
"""
return base64.b64encode(self.priv_key.to_bytes()).decode("ascii")

def _publish_transaction(self, transaction_payload: str) -> str:
def publish_transaction(self, signed_transaction: str) -> str:
"""Publish an already signed transaction to this network
Args:
signed_transaction: The already signed transaction from self.sign_transaction
Returns:
The string of the published transaction hash
"""
_log.debug(f"[BTC] Publishing transaction {signed_transaction}")
return self._call("sendrawtransaction", signed_transaction)

def _publish_l5_transaction(self, transaction_payload: str) -> str:
"""Publish a transaction to this network with a certain data payload
Args:
transaction_payload: The arbitrary data to send with this transaction
Expand All @@ -265,7 +275,7 @@ def _publish_transaction(self, transaction_payload: str) -> str:
# Sign transaction data
signed_transaction = self.sign_transaction({"data": transaction_payload})
# Send signed transaction
return self._call("sendrawtransaction", signed_transaction)
return self.publish_transaction(signed_transaction)

def _calculate_transaction_fee(self) -> int:
"""Get the current satoshi/byte fee estimate
Expand Down
2 changes: 1 addition & 1 deletion dragonchain/lib/dto/btc_utest.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def test_should_retry_broadcast_false(self):
def test_publish_creates_signs_and_sends(self):
self.client._call = MagicMock(return_value="MyFakeTransactionHash")
self.client.sign_transaction = MagicMock(return_value="signed_transaction")
response = self.client._publish_transaction("DC-L5:0xhash")
response = self.client._publish_l5_transaction("DC-L5:0xhash")
self.assertEqual(response, "MyFakeTransactionHash")

self.client._call.assert_called_once_with("sendrawtransaction", "signed_transaction")
Expand Down
14 changes: 12 additions & 2 deletions dragonchain/lib/dto/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,17 @@ def get_private_key(self) -> str:
"""
return base64.b64encode(self.priv_key.to_bytes()).decode("ascii")

def _publish_transaction(self, transaction_payload: str) -> str:
def publish_transaction(self, signed_transaction: str) -> str:
"""Publish an already signed transaction to this network
Args:
signed_transaction: The already signed transaction from self.sign_transaction
Returns:
The hex string of the published transaction hash
"""
_log.debug(f"[ETH] Publishing transaction {signed_transaction}")
return self.w3.toHex(self.w3.eth.sendRawTransaction(signed_transaction))

def _publish_l5_transaction(self, transaction_payload: str) -> str:
"""Publish a transaction to this network with a certain data payload
Args:
transaction_payload: The arbitrary data to send with this transaction
Expand All @@ -251,7 +261,7 @@ def _publish_transaction(self, transaction_payload: str) -> str:
}
)
# Send signed transaction
return self.w3.toHex(self.w3.eth.sendRawTransaction(signed_transaction))
return self.publish_transaction(signed_transaction)

def _calculate_transaction_fee(self) -> int:
"""Get the current gas price estimate
Expand Down
2 changes: 1 addition & 1 deletion dragonchain/lib/dto/eth_utest.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_publish_creates_signs_and_sends(self):
return_value=b"\xec>s;\xb6\x8a\xbb?\xfa\x87\xa1+\x03\x9at\x9f\xcc\xafXDn\xee\xed\xa9:\xd0\xd5\x9fQ\x03\x8f\xf2"
)

response = self.client._publish_transaction("DC-L5:0xhash")
response = self.client._publish_l5_transaction("DC-L5:0xhash")
self.assertEqual(response, "0xec3e733bb68abb3ffa87a12b039a749fccaf58446eeeeda93ad0d59f51038ff2")

self.client.sign_transaction.assert_called_once_with(
Expand Down
23 changes: 21 additions & 2 deletions dragonchain/lib/dto/l4_block_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,23 @@ def new_from_at_rest(block: Dict[str, Any]) -> "L4BlockModel":
validations = []
for item in block["l3-validations"]:
validations.append({"l3_dc_id": item["l3_dc_id"], "l3_block_id": item["l3_block_id"], "l3_proof": item["l3_proof"], "valid": item["valid"]})
if block.get("version") == "2":
if block.get("version") == "3":
return L4BlockModel(
dc_id=block["header"]["dc_id"],
current_ddss=block["header"].get("current_ddss"),
block_id=block["header"]["block_id"],
timestamp=block["header"].get("timestamp") or "-1",
prev_proof=block["header"]["prev_proof"],
scheme=block["proof"]["scheme"],
proof=block["proof"]["proof"],
nonce=block["proof"].get("nonce"),
l1_dc_id=block["header"]["l1_dc_id"],
l1_block_id=block["header"]["l1_block_id"],
l1_proof=block["header"]["l1_proof"],
validations=validations,
chain_name=block["header"]["chain_name"],
)
elif block.get("version") == "2":
return L4BlockModel(
dc_id=block["header"]["dc_id"],
current_ddss=block["header"].get("current_ddss"),
Expand Down Expand Up @@ -78,6 +94,7 @@ def __init__(
l1_block_id=None,
l1_proof=None,
validations=None,
chain_name="",
):
"""Model Constructor"""
if validations is None:
Expand All @@ -96,6 +113,7 @@ def __init__(
self.l1_block_id = l1_block_id
self.l1_proof = l1_proof
self.validations = validations
self.chain_name = chain_name

def get_associated_l1_dcid(self) -> str:
"""Interface function for compatibility"""
Expand All @@ -113,9 +131,10 @@ def export_as_at_rest(self) -> Dict[str, Any]:
else:
proof = {"scheme": self.scheme, "proof": self.proof, "nonce": self.nonce}
return {
"version": "2",
"version": "3",
"dcrn": schema.DCRN.Block_L4_At_Rest.value,
"header": {
"chain_name": self.chain_name,
"dc_id": self.dc_id,
"current_ddss": self.current_ddss,
"level": 4,
Expand Down
15 changes: 12 additions & 3 deletions dragonchain/lib/dto/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def publish_l5_hash_to_public_network(self, l5_block_hash: str) -> str:
Returns:
String of the transaction hash (or equivalent) for the posted transaction
"""
return self._publish_transaction(f"DC-L5:{l5_block_hash}")
return self._publish_l5_transaction(f"DC-L5:{l5_block_hash}")

def is_transaction_confirmed(self, transaction_hash: str) -> bool:
"""Check if a transaction is considered confirmed
Expand Down Expand Up @@ -121,8 +121,17 @@ def get_network_string(self) -> str:
"""
raise NotImplementedError("This is an abstract method")

def _publish_transaction(self, payload: str) -> str:
"""Publish a transaction to this network with a certain data payload
def publish_transaction(self, signed_transaction: str) -> str:
"""Publish an already signed transaction to this network
Args:
signed_transaction: The already signed transaction from self.sign_transaction
Returns:
String of created transaction hash (or equivalent)
"""
raise NotImplementedError("This is an abstract method")

def _publish_l5_transaction(self, payload: str) -> str:
"""Publish an l5 transaction to this network with a certain data payload (l5 block hash)
Args:
transaction_payload: The arbitrary data to send with this transaction
Returns:
Expand Down
12 changes: 12 additions & 0 deletions dragonchain/lib/dto/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,17 @@ def get_transaction_queue_task_schema(dict_payload: bool = False) -> Dict[str, A
"properties": {"version": {"type": "string", "enum": ["1"]}, "blockchain": {"type": "string"}, "name": {"type": "string"}},
}

publish_interchain_transaction_schema_v1 = {
"type": "object",
"properties": {
"version": {"type": "string", "enum": ["1"]},
"blockchain": {"type": "string"},
"name": {"type": "string"},
"signed_txn": {"type": "string"},
},
"additionalProperties": False,
}

# BITCOIN INTERCHAIN #

create_bitcoin_interchain_schema_v1 = {
Expand Down Expand Up @@ -778,6 +789,7 @@ def add_crud_default_properties(other_properties: Dict[str, Any]):
"create_interchain": default_endpoint_property_schema,
"update_interchain": default_endpoint_property_schema,
"create_interchain_transaction": default_endpoint_property_schema,
"publish_interchain_transaction": default_endpoint_property_schema,
"list_interchains": default_endpoint_property_schema,
"get_interchain": default_endpoint_property_schema,
"delete_interchain": default_endpoint_property_schema,
Expand Down
2 changes: 2 additions & 0 deletions dragonchain/transaction_processor/level_4_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

PROOF_SCHEME = os.environ["PROOF_SCHEME"].lower()
ADDRESS = os.environ["INTERNAL_ID"]
CHAIN_NAME = os.environ["DRAGONCHAIN_NAME"]

_log = logger.get_logger()

Expand Down Expand Up @@ -167,6 +168,7 @@ def create_block(l1_headers: "L1Headers", validations: List[Dict[str, Any]]) ->
l1_block_id=l1_headers["block_id"],
l1_proof=l1_headers["proof"],
validations=validations,
chain_name=CHAIN_NAME,
)

sign_block(l4_block)
Expand Down
3 changes: 3 additions & 0 deletions dragonchain/transaction_processor/level_5_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ def process_claims_backlog() -> None:
for claim_check_id in claims_set:
try:
matchmaking.resolve_claim_check(claim_check_id)
except exceptions.NotFound:
# If claim is not found, then this claim is irrelevant and we can safely skip
pass
except Exception:
_log.exception("Failure to finalize claim in matchmaking. Skipping the rest of the retry queue.")
return # short-circuit and exit early, matchmaking still unreachable
Expand Down
8 changes: 8 additions & 0 deletions dragonchain/transaction_processor/level_5_actions_utest.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ def test_process_claims_backlog_with_empty_backlog(self, mock_resolve_claim_chec
mock_resolve_claim_check.assert_not_called()
mock_srem_sync.assert_not_called()

@patch("dragonchain.lib.database.redis.smembers_sync", return_value={"CLAIMID_1", "CLAIMID_2", "CLAIMID_3"})
@patch("dragonchain.lib.database.redis.srem_sync", return_value=1) # successful removal of member from set
@patch("dragonchain.lib.matchmaking.resolve_claim_check", side_effect=exceptions.NotFound)
def test_process_claims_backlog_works_with_matchmaking_404(self, mock_resolve_claim_check, mock_srem_sync, mock_smembers_sync):
level_5_actions.process_claims_backlog()
self.assertEqual(mock_resolve_claim_check.call_count, 3)
self.assertEqual(mock_srem_sync.call_count, 3)

@patch("dragonchain.lib.database.redis.smembers_sync", return_value={"CLAIMID_1", "CLAIMID_2", "CLAIMID_3"})
@patch("dragonchain.lib.database.redis.srem_sync", return_value=1) # successful removal of member from set
@patch("dragonchain.lib.matchmaking.resolve_claim_check", side_effect=exceptions.MatchmakingRetryableError)
Expand Down

0 comments on commit 47ecb3e

Please sign in to comment.