Adapt tests to latest changes in MiningAdjudicator

pull/947/head
David Núñez 2019-05-03 09:55:24 +02:00
parent 4baa92c8fc
commit b51cb74a4d
5 changed files with 232 additions and 290 deletions

View File

@ -177,7 +177,8 @@ class SlashingEconomics:
reward_coefficient = 2
@property
def deployment_parameters(self):
def deployment_parameters(self) -> Tuple[int, ...]:
"""Cast coefficient attributes to uint256 compatible type for solidity+EVM"""
deployment_parameters = [
self.algorithm_sha256,
@ -187,4 +188,4 @@ class SlashingEconomics:
self.reward_coefficient
]
return deployment_parameters
return tuple(map(int, deployment_parameters))

View File

@ -18,21 +18,21 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import os
import coincurve
import pytest
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.primitives import hashes
from eth_tester.exceptions import TransactionFailed
from eth_utils import to_canonical_address
from web3.contract import Contract
from nucypher.blockchain.eth.token import NU
from nucypher.policy.models import IndisputableEvidence
from umbral import pre
from umbral.curvebn import CurveBN
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer, Signature
from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.primitives import hashes
from umbral.signing import Signer
from nucypher.blockchain.eth.token import NU
from nucypher.crypto.utils import get_signature_recovery_value
from nucypher.policy.models import IndisputableEvidence
VALUE_FIELD = 0
@ -46,12 +46,6 @@ policy_manager_secret = os.urandom(SECRET_LENGTH)
user_escrow_secret = os.urandom(SECRET_LENGTH)
adjudicator_secret = os.urandom(SECRET_LENGTH)
ALGORITHM_SHA256 = 1
BASE_PENALTY = 300
PENALTY_HISTORY_COEFFICIENT = 10
PERCENTAGE_PENALTY_COEFFICIENT = 2
REWARD_COEFFICIENT = 2
@pytest.fixture()
def token(testerchain):
@ -109,7 +103,7 @@ def policy_manager(testerchain, escrow):
@pytest.fixture()
def adjudicator(testerchain, escrow):
def adjudicator(testerchain, escrow, slashing_economics):
escrow, _ = escrow
creator = testerchain.interface.w3.eth.accounts[0]
@ -119,11 +113,8 @@ def adjudicator(testerchain, escrow):
contract, _ = testerchain.interface.deploy_contract(
'MiningAdjudicator',
escrow.address,
ALGORITHM_SHA256,
BASE_PENALTY,
PENALTY_HISTORY_COEFFICIENT,
PERCENTAGE_PENALTY_COEFFICIENT,
REWARD_COEFFICIENT)
*slashing_economics.deployment_parameters)
dispatcher, _ = testerchain.interface.deploy_contract('Dispatcher', contract.address, secret_hash)
# Wrap dispatcher contract
@ -140,26 +131,8 @@ def adjudicator(testerchain, escrow):
# TODO: Obtain real re-encryption metadata. Maybe constructing a WorkOrder and obtaining a response.
# TODO organize support functions
# TODO: Repeated method in estimate_gas
def generate_args_for_slashing(testerchain, miner):
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 recoverable_signature
delegating_privkey = UmbralPrivateKey.gen_key()
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
signing_privkey = UmbralPrivateKey.gen_key()
@ -174,19 +147,34 @@ def generate_args_for_slashing(testerchain, miner):
sign_delegating_key=False,
sign_receiving_key=False)
capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob, signing_privkey.get_pubkey())
cfrag = pre.reencrypt(kfrags[0], capsule, metadata=os.urandom(34))
capsule_bytes = capsule.to_bytes()
# Corrupt proof
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
cfrag_bytes = cfrag.to_bytes()
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(capsule_bytes + cfrag_bytes)
data_hash = hash_ctx.finalize()
requester_umbral_private_key = UmbralPrivateKey.gen_key()
requester_umbral_public_key_bytes = requester_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)
capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
miner_umbral_private_key = UmbralPrivateKey.gen_key()
miner_umbral_public_key_bytes = miner_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)
ursula_pubkey = miner_umbral_private_key.get_pubkey()
ursula_pubkey_bytes = ursula_pubkey.to_bytes(is_compressed=False)[1:]
specification = bytes(capsule) + ursula_pubkey_bytes + bytes(20) + bytes(32)
ursulas_signer = Signer(miner_umbral_private_key)
bobs_signer = Signer(priv_key_bob)
task_signature = bytes(bobs_signer(specification))
metadata = bytes(ursulas_signer(task_signature))
cfrag = pre.reencrypt(kfrags[0], capsule, metadata=metadata)
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
cfrag_signature = bytes(ursulas_signer(bytes(cfrag)))
bob_pubkey_bytes = pub_key_bob.to_bytes(is_compressed=False)[1:]
cfrag_signature_v = get_signature_recovery_value(message=bytes(cfrag),
signature=cfrag_signature,
public_key=ursula_pubkey)
task_signature_v = get_signature_recovery_value(message=task_signature,
signature=metadata,
public_key=ursula_pubkey)
recovery_values = cfrag_signature_v + task_signature_v
miner_umbral_public_key_bytes = miner_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)[1:]
# Sign Umbral public key using eth-key
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(miner_umbral_public_key_bytes)
@ -195,16 +183,19 @@ def generate_args_for_slashing(testerchain, miner):
sig_key = testerchain.interface.provider.ethereum_tester.backend._key_lookup[address]
signed_miner_umbral_public_key = bytes(sig_key.sign_msg_hash(miner_umbral_public_key_hash))
capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
evidence_data = evidence.precompute_values()
return data_hash, (capsule_bytes,
capsule_signature_by_requester,
capsule_signature_by_requester_and_miner,
cfrag_bytes,
cfrag_signature_by_miner,
requester_umbral_public_key_bytes,
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(bytes(capsule) + bytes(cfrag))
data_hash = hash_ctx.finalize()
return data_hash, (bytes(capsule),
bytes(cfrag),
cfrag_signature,
task_signature,
recovery_values,
bob_pubkey_bytes,
miner_umbral_public_key_bytes,
signed_miner_umbral_public_key,
evidence_data)
@ -272,7 +263,7 @@ def execute_multisig_transaction(testerchain, multisig, accounts, tx):
@pytest.mark.slow
def test_all(testerchain, token, escrow, policy_manager, adjudicator, user_escrow_proxy, multisig):
def test_all(testerchain, token, escrow, policy_manager, adjudicator, user_escrow_proxy, multisig, slashing_economics):
# Travel to the start of the next period to prevent problems with unexpected overflow first period
testerchain.time_travel(hours=1)
@ -780,19 +771,22 @@ def test_all(testerchain, token, escrow, policy_manager, adjudicator, user_escro
total_lock = escrow.functions.lockedPerPeriod(period).call()
alice1_balance = token.functions.balanceOf(alice1).call()
algorithm_sha256, base_penalty, *coefficients = slashing_economics.deployment_parameters
penalty_history_coefficient, percentage_penalty_coefficient, reward_coefficient = coefficients
data_hash, slashing_args = generate_args_for_slashing(testerchain, ursula1)
assert not adjudicator.functions.evaluatedCFrags(data_hash).call()
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice1})
testerchain.wait_for_receipt(tx)
assert adjudicator.functions.evaluatedCFrags(data_hash).call()
assert tokens_amount - BASE_PENALTY == escrow.functions.minerInfo(ursula1).call()[VALUE_FIELD]
assert tokens_amount - base_penalty == escrow.functions.minerInfo(ursula1).call()[VALUE_FIELD]
assert previous_lock == escrow.functions.getLockedTokensInPast(ursula1, 1).call()
assert lock == escrow.functions.getLockedTokens(ursula1).call()
assert next_lock == escrow.functions.getLockedTokens(ursula1, 1).call()
assert total_previous_lock == escrow.functions.lockedPerPeriod(period - 1).call()
assert total_lock == escrow.functions.lockedPerPeriod(period).call()
assert 0 == escrow.functions.lockedPerPeriod(period + 1).call()
assert alice1_balance + BASE_PENALTY / REWARD_COEFFICIENT == token.functions.balanceOf(alice1).call()
assert alice1_balance + base_penalty / reward_coefficient == token.functions.balanceOf(alice1).call()
# Slash part of the one sub stake
tokens_amount = escrow.functions.minerInfo(ursula2).call()[VALUE_FIELD]
@ -807,14 +801,14 @@ def test_all(testerchain, token, escrow, policy_manager, adjudicator, user_escro
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice1})
testerchain.wait_for_receipt(tx)
assert adjudicator.functions.evaluatedCFrags(data_hash).call()
assert lock - BASE_PENALTY == escrow.functions.minerInfo(ursula2).call()[VALUE_FIELD]
assert lock - base_penalty == escrow.functions.minerInfo(ursula2).call()[VALUE_FIELD]
assert previous_lock == escrow.functions.getLockedTokensInPast(ursula2, 1).call()
assert lock - BASE_PENALTY == escrow.functions.getLockedTokens(ursula2).call()
assert next_lock - BASE_PENALTY == escrow.functions.getLockedTokens(ursula2, 1).call()
assert lock - base_penalty == escrow.functions.getLockedTokens(ursula2).call()
assert next_lock - base_penalty == escrow.functions.getLockedTokens(ursula2, 1).call()
assert total_previous_lock == escrow.functions.lockedPerPeriod(period - 1).call()
assert total_lock - BASE_PENALTY == escrow.functions.lockedPerPeriod(period).call()
assert total_lock - base_penalty == escrow.functions.lockedPerPeriod(period).call()
assert 0 == escrow.functions.lockedPerPeriod(period + 1).call()
assert alice1_balance + BASE_PENALTY == token.functions.balanceOf(alice1).call()
assert alice1_balance + base_penalty == token.functions.balanceOf(alice1).call()
# Upgrade the adjudicator
# Deploy the same contract as the second version
@ -822,11 +816,7 @@ def test_all(testerchain, token, escrow, policy_manager, adjudicator, user_escro
adjudicator_v2, _ = testerchain.interface.deploy_contract(
'MiningAdjudicator',
escrow.address,
ALGORITHM_SHA256,
BASE_PENALTY,
PENALTY_HISTORY_COEFFICIENT,
PERCENTAGE_PENALTY_COEFFICIENT,
REWARD_COEFFICIENT)
*slashing_economics.deployment_parameters)
adjudicator_secret2 = os.urandom(SECRET_LENGTH)
adjudicator_secret2_hash = testerchain.interface.w3.keccak(adjudicator_secret2)
# Ursula and Alice can't upgrade library, only owner can
@ -900,7 +890,7 @@ def test_all(testerchain, token, escrow, policy_manager, adjudicator, user_escro
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice2})
testerchain.wait_for_receipt(tx)
assert adjudicator.functions.evaluatedCFrags(data_hash).call()
penalty = (2 * BASE_PENALTY + 3 * PENALTY_HISTORY_COEFFICIENT)
penalty = (2 * base_penalty + 3 * penalty_history_coefficient)
assert lock - penalty == escrow.functions.minerInfo(ursula1).call()[VALUE_FIELD]
assert previous_lock == escrow.functions.getLockedTokensInPast(ursula1, 1).call()
assert lock - penalty == escrow.functions.getLockedTokens(ursula1).call()
@ -908,7 +898,7 @@ def test_all(testerchain, token, escrow, policy_manager, adjudicator, user_escro
assert total_previous_lock == escrow.functions.lockedPerPeriod(period - 1).call()
assert total_lock - penalty == escrow.functions.lockedPerPeriod(period).call()
assert 0 == escrow.functions.lockedPerPeriod(period + 1).call()
assert alice2_balance + penalty / REWARD_COEFFICIENT == token.functions.balanceOf(alice2).call()
assert alice2_balance + penalty / reward_coefficient == token.functions.balanceOf(alice2).call()
# Unlock and withdraw all tokens in MinersEscrow
for index in range(9):

View File

@ -34,11 +34,7 @@ def adjudicator_contract(testerchain, escrow, request, slashing_economics):
contract, _ = testerchain.interface.deploy_contract(
'MiningAdjudicator',
escrow.address,
slashing_economics.algorithm_sha256,
slashing_economics.base_penalty,
slashing_economics.penalty_history_coefficient,
slashing_economics.percentage_penalty_coefficient,
slashing_economics.reward_coefficient)
*slashing_economics.deployment_parameters)
if request.param:
secret = os.urandom(DispatcherDeployer.DISPATCHER_SECRET_LENGTH)

View File

@ -18,24 +18,21 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import os
import coincurve
import pytest
from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from eth_tester.exceptions import TransactionFailed
from eth_utils import to_canonical_address, to_checksum_address
from eth_utils import to_canonical_address
from typing import Tuple
from web3.contract import Contract
from umbral import pre
from umbral.config import default_params
from umbral.curvebn import CurveBN
from umbral.keys import UmbralPrivateKey
from umbral.point import Point
from umbral.random_oracles import hash_to_curvebn, ExtendedKeccak
from umbral.signing import Signature, Signer
from umbral.signing import Signer
from nucypher.crypto.utils import get_signature_recovery_value
from nucypher.policy.models import IndisputableEvidence
@ -45,33 +42,8 @@ secret = (123456).to_bytes(32, byteorder='big')
secret2 = (654321).to_bytes(32, byteorder='big')
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 = make_recoverable_signature(data_hash, signature, umbral_pubkey_bytes)
return recoverable_signature
def make_recoverable_signature(data_hash, signature, umbral_pubkey_bytes):
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 recoverable_signature
# TODO: Obtain real re-encryption metadata. Maybe constructing a WorkOrder and obtaining a response.
def fragments(metadata):
def mock_ursula_reencrypts(ursula_privkey, corrupt_cfrag: bool = False):
delegating_privkey = UmbralPrivateKey.gen_key()
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
signing_privkey = UmbralPrivateKey.gen_key()
@ -86,8 +58,42 @@ def fragments(metadata):
sign_delegating_key=False,
sign_receiving_key=False)
capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob, signing_privkey.get_pubkey())
ursula_pubkey = ursula_privkey.get_pubkey()
ursula_pubkey_bytes = ursula_pubkey.to_bytes(is_compressed=False)[1:]
specification = bytes(capsule) + ursula_pubkey_bytes + bytes(20) + bytes(32)
ursulas_signer = Signer(ursula_privkey)
bobs_signer = Signer(priv_key_bob)
task_signature = bytes(bobs_signer(specification))
metadata = bytes(ursulas_signer(task_signature))
cfrag = pre.reencrypt(kfrags[0], capsule, metadata=metadata)
return capsule, cfrag
if corrupt_cfrag:
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
cfrag_signature = bytes(ursulas_signer(bytes(cfrag)))
bob_pubkey_bytes = pub_key_bob.to_bytes(is_compressed=False)[1:]
cfrag_signature_v = get_signature_recovery_value(message=bytes(cfrag),
signature=cfrag_signature,
public_key=ursula_pubkey)
task_signature_v = get_signature_recovery_value(message=task_signature,
signature=metadata,
public_key=ursula_pubkey)
recovery_values = cfrag_signature_v + task_signature_v
return capsule, cfrag, cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes
def evaluation_hash(capsule, cfrag):
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(bytes(capsule) + bytes(cfrag))
data_hash = hash_ctx.finalize()
return data_hash
@pytest.mark.slow
@ -113,7 +119,7 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
# Generate miner's Umbral key
miner_umbral_private_key = UmbralPrivateKey.gen_key()
miner_umbral_public_key_bytes = miner_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)
miner_umbral_public_key_bytes = miner_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)[1:]
# Sign Umbral public key using eth-key
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
@ -124,43 +130,28 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
sig_key = provider.ethereum_tester.backend._key_lookup[address]
signed_miner_umbral_public_key = bytes(sig_key.sign_msg_hash(miner_umbral_public_key_hash))
# Prepare hash of the data
metadata = os.urandom(33)
capsule, cfrag = fragments(metadata)
# Prepare evaluation data
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key)
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
assert cfrag.verify_correctness(capsule)
capsule_bytes = capsule.to_bytes()
cfrag_bytes = cfrag.to_bytes()
# Bob prepares supporting Evidence
# Investigator prepares supporting Evidence
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
evidence_data = evidence.precompute_values()
assert len(evidence_data) == 20 * 32 + 32 + 20 + 1
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(capsule_bytes + cfrag_bytes)
data_hash = hash_ctx.finalize()
data_hash = evaluation_hash(capsule, cfrag)
# This capsule and cFrag are not yet evaluated
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
# Generate requester's Umbral key
requester_umbral_private_key = UmbralPrivateKey.gen_key()
requester_umbral_public_key_bytes = requester_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)
# Sign capsule and cFrag
capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
# Challenge using good data
args = (capsule_bytes,
capsule_signature_by_requester,
capsule_signature_by_requester_and_miner,
cfrag_bytes,
cfrag_signature_by_miner,
requester_umbral_public_key_bytes,
args = (bytes(capsule),
bytes(cfrag),
cfrag_signature,
task_signature,
recovery_values,
bob_pubkey_bytes,
miner_umbral_public_key_bytes,
signed_miner_umbral_public_key,
evidence_data)
@ -183,39 +174,36 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
assert investigator == event_args['investigator']
assert event_args['correctness']
###############################
# Test: Don't evaluate miner with data that already was checked
###############################
# TODO: be more specific on the expected exception
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact()
testerchain.wait_for_receipt(tx)
###############################
# Test: Ursula produces incorrect proof:
metadata = os.urandom(34)
capsule, cfrag = fragments(metadata)
capsule_bytes = capsule.to_bytes()
# Corrupt proof
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
cfrag_bytes = cfrag.to_bytes()
###############################
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key, corrupt_cfrag=True)
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
assert not cfrag.verify_correctness(capsule)
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(capsule_bytes + cfrag_bytes)
data_hash = hash_ctx.finalize()
capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
evidence_data = evidence.precompute_values()
args = (capsule_bytes,
capsule_signature_by_requester,
capsule_signature_by_requester_and_miner,
cfrag_bytes,
cfrag_signature_by_miner,
requester_umbral_public_key_bytes,
args = (bytes(capsule),
bytes(cfrag),
cfrag_signature,
task_signature,
recovery_values,
bob_pubkey_bytes,
miner_umbral_public_key_bytes,
signed_miner_umbral_public_key,
evidence_data)
data_hash = evaluation_hash(capsule, cfrag)
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
testerchain.wait_for_receipt(tx)
number_of_evaluations += 1
@ -243,18 +231,10 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
# Test: Bob produces wrong precomputed data
###############################
metadata = os.urandom(34)
capsule, cfrag = fragments(metadata)
capsule_bytes = capsule.to_bytes()
cfrag_bytes = cfrag.to_bytes()
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key)
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
assert cfrag.verify_correctness(capsule)
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(capsule_bytes + cfrag_bytes)
data_hash = hash_ctx.finalize()
capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
evidence_data = evidence.precompute_values()
@ -265,16 +245,17 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
evidence_data[32:32+64] = random_point_bytes
evidence_data = bytes(evidence_data)
args = (capsule_bytes,
capsule_signature_by_requester,
capsule_signature_by_requester_and_miner,
cfrag_bytes,
cfrag_signature_by_miner,
requester_umbral_public_key_bytes,
args = (bytes(capsule),
bytes(cfrag),
cfrag_signature,
task_signature,
recovery_values,
bob_pubkey_bytes,
miner_umbral_public_key_bytes,
signed_miner_umbral_public_key,
evidence_data)
data_hash = evaluation_hash(capsule, cfrag)
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
# Evaluation must fail since Bob precomputed wrong values
@ -292,34 +273,27 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
# Test: Second violation. Penalty is increased
###############################
# Prepare hash of the data
metadata = os.urandom(34)
capsule, cfrag = fragments(metadata)
capsule_bytes = capsule.to_bytes()
# Corrupt proof
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
cfrag_bytes = cfrag.to_bytes()
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(capsule_bytes + cfrag_bytes)
data_hash = hash_ctx.finalize()
capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key, corrupt_cfrag=True)
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
evidence_data = evidence.precompute_values()
args = [capsule_bytes,
capsule_signature_by_requester,
capsule_signature_by_requester_and_miner,
cfrag_bytes,
cfrag_signature_by_miner,
requester_umbral_public_key_bytes,
args = (bytes(capsule),
bytes(cfrag),
cfrag_signature,
task_signature,
recovery_values,
bob_pubkey_bytes,
miner_umbral_public_key_bytes,
signed_miner_umbral_public_key,
evidence_data]
evidence_data)
data_hash = evaluation_hash(capsule, cfrag)
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
worker_stake = escrow.functions.minerInfo(miner).call()[0]
investigator_balance = escrow.functions.rewardInfo(investigator).call()
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
testerchain.wait_for_receipt(tx)
@ -350,33 +324,27 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
# Test: Third violation. Penalty reaches the maximum allowed
###############################
# Prepare corrupted data again
capsule, cfrag = fragments(metadata)
capsule_bytes = capsule.to_bytes()
# Corrupt proof
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
cfrag_bytes = cfrag.to_bytes()
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(capsule_bytes + cfrag_bytes)
data_hash = hash_ctx.finalize()
capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key, corrupt_cfrag=True)
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
evidence_data = evidence.precompute_values()
args = [capsule_bytes,
capsule_signature_by_requester,
capsule_signature_by_requester_and_miner,
cfrag_bytes,
cfrag_signature_by_miner,
requester_umbral_public_key_bytes,
args = (bytes(capsule),
bytes(cfrag),
cfrag_signature,
task_signature,
recovery_values,
bob_pubkey_bytes,
miner_umbral_public_key_bytes,
signed_miner_umbral_public_key,
evidence_data]
evidence_data)
data_hash = evaluation_hash(capsule, cfrag)
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
worker_stake = escrow.functions.minerInfo(miner).call()[0]
investigator_balance = escrow.functions.rewardInfo(investigator).call()
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
testerchain.wait_for_receipt(tx)
number_of_evaluations += 1
@ -406,47 +374,40 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
##############
# Can't evaluate miner using broken signatures
wrong_args = args[:]
wrong_args[1] = capsule_signature_by_requester[1:]
wrong_args = list(args)
wrong_args[2] = cfrag_signature[1:]
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
testerchain.wait_for_receipt(tx)
wrong_args = args[:]
wrong_args[2] = capsule_signature_by_requester_and_miner[1:]
wrong_args = list(args)
wrong_args[3] = task_signature[1:]
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
testerchain.wait_for_receipt(tx)
wrong_args = args[:]
wrong_args[4] = cfrag_signature_by_miner[1:]
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
testerchain.wait_for_receipt(tx)
wrong_args = args[:]
wrong_args = list(args)
wrong_args[7] = signed_miner_umbral_public_key[1:]
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
testerchain.wait_for_receipt(tx)
# Can't evaluate miner using wrong keys
wrong_args = args[:]
wrong_args[5] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(is_compressed=False)
wrong_args = list(args)
wrong_args[5] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(is_compressed=False)[1:]
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
testerchain.wait_for_receipt(tx)
wrong_args = args[:]
wrong_args[6] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(is_compressed=False)
wrong_args = list(args)
wrong_args[6] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(is_compressed=False)[1:]
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
testerchain.wait_for_receipt(tx)
# Can't use signature for another data
wrong_args = args[:]
wrong_args[0] = bytes(args[0][0] + 1) + args[0][1:]
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
testerchain.wait_for_receipt(tx)
wrong_args = args[:]
wrong_args[3] = bytes(args[3][0] + 1) + args[3][1:]
wrong_args = list(args)
wrong_args[1] = os.urandom(len(bytes(cfrag)))
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
testerchain.wait_for_receipt(tx)
@ -455,7 +416,8 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
address = to_canonical_address(wrong_miner)
sig_key = provider.ethereum_tester.backend._key_lookup[address]
signed_wrong_miner_umbral_public_key = bytes(sig_key.sign_msg_hash(miner_umbral_public_key_hash))
wrong_args = args[:]
wrong_args = list(args)
wrong_args[7] = signed_wrong_miner_umbral_public_key
with pytest.raises((TransactionFailed, ValueError)):
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()

View File

@ -18,34 +18,29 @@ You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import coincurve
import io
import json
import os
from cryptography.hazmat.primitives.asymmetric import ec
from eth_utils import to_canonical_address
from nucypher.blockchain.economics import TokenEconomics
from nucypher.policy.models import IndisputableEvidence, Policy
from umbral import pre
from umbral.curvebn import CurveBN
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer, Signature
import re
import time
from os.path import abspath, dirname
import io
import re
from eth_utils import to_canonical_address
from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.primitives import hashes
from twisted.logger import globalLogPublisher, Logger, jsonFileLogObserver, ILogObserver
from zope.interface import provider
from nucypher.blockchain.eth.agents import NucypherTokenAgent, MinerAgent, PolicyAgent, MiningAdjudicatorAgent
from nucypher.utilities.sandbox.blockchain import TesterBlockchain
from umbral import pre
from umbral.curvebn import CurveBN
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from nucypher.blockchain.economics import TokenEconomics
from nucypher.blockchain.eth.agents import NucypherTokenAgent, MinerAgent, PolicyAgent, MiningAdjudicatorAgent
from nucypher.crypto.utils import get_signature_recovery_value
from nucypher.policy.models import IndisputableEvidence, Policy
from nucypher.utilities.sandbox.blockchain import TesterBlockchain
ALGORITHM_SHA256 = 1
TOKEN_ECONOMICS = TokenEconomics()
@ -121,26 +116,8 @@ class AnalyzeGas:
# TODO organize support functions
# TODO: Repeated method in test_intercontract_integration
def generate_args_for_slashing(testerchain, miner, corrupt: bool = True):
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 recoverable_signature
delegating_privkey = UmbralPrivateKey.gen_key()
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
signing_privkey = UmbralPrivateKey.gen_key()
@ -155,18 +132,35 @@ def generate_args_for_slashing(testerchain, miner, corrupt: bool = True):
sign_delegating_key=False,
sign_receiving_key=False)
capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob, signing_privkey.get_pubkey())
cfrag = pre.reencrypt(kfrags[0], capsule, metadata=os.urandom(34))
capsule_bytes = capsule.to_bytes()
miner_umbral_private_key = UmbralPrivateKey.gen_key()
ursula_pubkey = miner_umbral_private_key.get_pubkey()
ursula_pubkey_bytes = ursula_pubkey.to_bytes(is_compressed=False)[1:]
specification = bytes(capsule) + ursula_pubkey_bytes + bytes(20) + bytes(32)
ursulas_signer = Signer(miner_umbral_private_key)
bobs_signer = Signer(priv_key_bob)
task_signature = bytes(bobs_signer(specification))
metadata = bytes(ursulas_signer(task_signature))
cfrag = pre.reencrypt(kfrags[0], capsule, metadata=metadata)
if corrupt:
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
cfrag_bytes = cfrag.to_bytes()
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(capsule_bytes + cfrag_bytes)
requester_umbral_private_key = UmbralPrivateKey.gen_key()
requester_umbral_public_key_bytes = requester_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)
capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
miner_umbral_private_key = UmbralPrivateKey.gen_key()
miner_umbral_public_key_bytes = miner_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)
cfrag_signature = bytes(ursulas_signer(bytes(cfrag)))
bob_pubkey_bytes = pub_key_bob.to_bytes(is_compressed=False)[1:]
cfrag_signature_v = get_signature_recovery_value(message=bytes(cfrag),
signature=cfrag_signature,
public_key=ursula_pubkey)
task_signature_v = get_signature_recovery_value(message=task_signature,
signature=metadata,
public_key=ursula_pubkey)
recovery_values = cfrag_signature_v + task_signature_v
miner_umbral_public_key_bytes = miner_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)[1:]
# Sign Umbral public key using eth-key
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
hash_ctx.update(miner_umbral_public_key_bytes)
@ -175,16 +169,15 @@ def generate_args_for_slashing(testerchain, miner, corrupt: bool = True):
sig_key = testerchain.interface.provider.ethereum_tester.backend._key_lookup[address]
signed_miner_umbral_public_key = bytes(sig_key.sign_msg_hash(miner_umbral_public_key_hash))
capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
evidence_data = evidence.precompute_values()
return (capsule_bytes,
capsule_signature_by_requester,
capsule_signature_by_requester_and_miner,
cfrag_bytes,
cfrag_signature_by_miner,
requester_umbral_public_key_bytes,
return (bytes(capsule),
bytes(cfrag),
cfrag_signature,
task_signature,
recovery_values,
bob_pubkey_bytes,
miner_umbral_public_key_bytes,
signed_miner_umbral_public_key,
evidence_data)