From 694eb7fff93f8e35f83fc5f26078f85c451c6297 Mon Sep 17 00:00:00 2001 From: szotov Date: Sat, 13 Oct 2018 20:41:41 +0300 Subject: [PATCH] Added test for the draft of challenge --- .../sol/source/contracts/ChallengeLibrary.sol | 8 +- .../eth/contracts/main/challenges/conftest.py | 32 +++++ .../main/challenges/test_challenges.py | 119 ++++++++++++++++++ 3 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 tests/blockchain/eth/contracts/main/challenges/conftest.py create mode 100644 tests/blockchain/eth/contracts/main/challenges/test_challenges.py diff --git a/nucypher/blockchain/eth/sol/source/contracts/ChallengeLibrary.sol b/nucypher/blockchain/eth/sol/source/contracts/ChallengeLibrary.sol index 568e6b202..7c425bba9 100644 --- a/nucypher/blockchain/eth/sol/source/contracts/ChallengeLibrary.sol +++ b/nucypher/blockchain/eth/sol/source/contracts/ChallengeLibrary.sol @@ -50,8 +50,8 @@ contract ChallengeLibrary { // copy public key except first byte bytes memory preparedPublicKey = new bytes(64); assembly { - let destination := add(mload(preparedPublicKey), 32) - let source := add(mload(_minerPublicKey), 33) + let destination := add(preparedPublicKey, 32) + let source := add(_minerPublicKey, 33) mstore(destination, mload(source)) mstore(add(destination, 32), mload(add(source, 32))) } @@ -74,8 +74,8 @@ contract ChallengeLibrary { function check( bytes _capsuleBytes, bytes _cFragBytes - ) public returns (bool) { - return false; + ) public pure returns (bool) { + return _capsuleBytes.length == 100 && _cFragBytes.length == 100; // just for tests } } diff --git a/tests/blockchain/eth/contracts/main/challenges/conftest.py b/tests/blockchain/eth/contracts/main/challenges/conftest.py new file mode 100644 index 000000000..79c844447 --- /dev/null +++ b/tests/blockchain/eth/contracts/main/challenges/conftest.py @@ -0,0 +1,32 @@ +import pytest +from web3.contract import Contract + + +ALGORITHM_SHA256 = 1 +secret = (123456).to_bytes(32, byteorder='big') + + +@pytest.fixture() +def escrow(testerchain): + escrow, _ = testerchain.interface.deploy_contract('MinersEscrowStub') + return escrow + + +# @pytest.fixture(params=[False, True]) +@pytest.fixture() +def challenge_contract(testerchain, escrow, request): + # creator, client, bad_node, node1, node2, node3, *everyone_else = testerchain.interface.w3.eth.accounts + + contract, _ = testerchain.interface.deploy_contract('ChallengeLibrary', escrow.address, ALGORITHM_SHA256) + + # if request.param: + # secret_hash = testerchain.interface.w3.sha3(secret) + # dispatcher, _ = testerchain.interface.deploy_contract('Dispatcher', contract.address, secret_hash) + # + # # Deploy second version of the government contract + # contract = testerchain.interface.w3.eth.contract( + # abi=contract.abi, + # address=dispatcher.address, + # ContractFactoryClass=Contract) + + return contract diff --git a/tests/blockchain/eth/contracts/main/challenges/test_challenges.py b/tests/blockchain/eth/contracts/main/challenges/test_challenges.py new file mode 100644 index 000000000..f34370872 --- /dev/null +++ b/tests/blockchain/eth/contracts/main/challenges/test_challenges.py @@ -0,0 +1,119 @@ +import os + +import coincurve +import pytest +from eth_tester.exceptions import TransactionFailed +from eth_utils import to_canonical_address + +from umbral.keys import UmbralPrivateKey +from cryptography.hazmat.backends.openssl import backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec +from umbral.signing import Signature + + +def sign_data(data, umbral_privkey): + umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(is_compressed=False) + + # Prepare hash of the data + hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend) + hash_ctx.update(data) + data_hash = hash_ctx.finalize() + + # Sign data and calculate recoverable signature + cryptography_priv_key = umbral_privkey.to_cryptography_privkey() + signature_der_bytes = cryptography_priv_key.sign(data, ec.ECDSA(hashes.SHA256())) + signature = Signature.from_bytes(signature_der_bytes, der_encoded=True) + recoverable_signature = bytes(signature) + bytes([0]) + pubkey_bytes = coincurve.PublicKey.from_signature_and_message(recoverable_signature, data_hash, hasher=None) \ + .format(compressed=False) + if pubkey_bytes != umbral_pubkey_bytes: + recoverable_signature = bytes(signature) + bytes([1]) + return data_hash, recoverable_signature + + +@pytest.mark.slow +def test_challenge_cfrag(testerchain, escrow, challenge_contract): + creator, miner, wrong_miner, *everyone_else = testerchain.interface.w3.eth.accounts + + # Prepare one miner + tx = escrow.functions.setMinerInfo(miner, 1000).transact() + testerchain.wait_for_receipt(tx) + + # Generate miner's Umbral key + umbral_privkey = UmbralPrivateKey.gen_key() + umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(is_compressed=False) + + # Sign Umbral public key using eth-key + hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend) + hash_ctx.update(umbral_pubkey_bytes) + umbral_pubkey_hash = hash_ctx.finalize() + provider = testerchain.interface.providers[0] + address = to_canonical_address(miner) + sig_key = provider.ethereum_tester.backend._key_lookup[address] + signed_umbral_pubkey = bytes(sig_key.sign_msg_hash(umbral_pubkey_hash)) + + # Prepare hash of the data + capsule = os.urandom(100) + cfrag = os.urandom(100) + data = capsule + cfrag + data_hash, recoverable_signature = sign_data(data, umbral_privkey) + + # Challenge using good data + assert not challenge_contract.functions.challengedCFrags(data_hash).call() + tx = challenge_contract.functions.challengeCFrag( + capsule, cfrag, recoverable_signature, umbral_pubkey_bytes, signed_umbral_pubkey).transact() + testerchain.wait_for_receipt(tx) + # Hash of the data is saved and miner is not slashed + assert challenge_contract.functions.challengedCFrags(data_hash).call() + assert 1000 == escrow.functions.minerInfo(miner).call() + + # Can't challenge miner with data that already was checked + with pytest.raises((TransactionFailed, ValueError)): + tx = challenge_contract.functions.challengeCFrag( + capsule, cfrag, recoverable_signature, umbral_pubkey_bytes, signed_umbral_pubkey).transact() + testerchain.wait_for_receipt(tx) + + # Challenge using bad data + cfrag = os.urandom(101) + data = capsule + cfrag + data_hash, recoverable_signature = sign_data(data, umbral_privkey) + assert not challenge_contract.functions.challengedCFrags(data_hash).call() + tx = challenge_contract.functions.challengeCFrag( + capsule, cfrag, recoverable_signature, umbral_pubkey_bytes, signed_umbral_pubkey).transact() + testerchain.wait_for_receipt(tx) + # Hash of the data is saved and miner is slashed + assert challenge_contract.functions.challengedCFrags(data_hash).call() + assert 900 == escrow.functions.minerInfo(miner).call() + + # Prepare hash of the data + capsule = os.urandom(100) + cfrag = os.urandom(100) + data = capsule + cfrag + data_hash, recoverable_signature = sign_data(data, umbral_privkey) + + # Can't challenge miner using broken signatures + with pytest.raises((TransactionFailed, ValueError)): + tx = challenge_contract.functions.challengeCFrag( + capsule, cfrag, recoverable_signature[1:], umbral_pubkey_bytes, signed_umbral_pubkey).transact() + testerchain.wait_for_receipt(tx) + with pytest.raises((TransactionFailed, ValueError)): + tx = challenge_contract.functions.challengeCFrag( + capsule, cfrag, recoverable_signature, umbral_pubkey_bytes, signed_umbral_pubkey[1:]).transact() + testerchain.wait_for_receipt(tx) + + # Can't use signature for another data + wrong_capsule = os.urandom(100) + with pytest.raises((TransactionFailed, ValueError)): + tx = challenge_contract.functions.challengeCFrag( + wrong_capsule, cfrag, recoverable_signature, umbral_pubkey_bytes, signed_umbral_pubkey).transact() + testerchain.wait_for_receipt(tx) + + # Can't challenge nonexistent miner + address = to_canonical_address(wrong_miner) + sig_key = provider.ethereum_tester.backend._key_lookup[address] + signed_umbral_pubkey = bytes(sig_key.sign_msg_hash(umbral_pubkey_hash)) + with pytest.raises((TransactionFailed, ValueError)): + tx = challenge_contract.functions.challengeCFrag( + capsule, cfrag, recoverable_signature, umbral_pubkey_bytes, signed_umbral_pubkey).transact() + testerchain.wait_for_receipt(tx)