mirror of https://github.com/nucypher/nucypher.git
Adapt tests to latest changes in MiningAdjudicator
parent
4baa92c8fc
commit
b51cb74a4d
|
@ -177,7 +177,8 @@ class SlashingEconomics:
|
||||||
reward_coefficient = 2
|
reward_coefficient = 2
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def deployment_parameters(self):
|
def deployment_parameters(self) -> Tuple[int, ...]:
|
||||||
|
"""Cast coefficient attributes to uint256 compatible type for solidity+EVM"""
|
||||||
|
|
||||||
deployment_parameters = [
|
deployment_parameters = [
|
||||||
self.algorithm_sha256,
|
self.algorithm_sha256,
|
||||||
|
@ -187,4 +188,4 @@ class SlashingEconomics:
|
||||||
self.reward_coefficient
|
self.reward_coefficient
|
||||||
]
|
]
|
||||||
|
|
||||||
return deployment_parameters
|
return tuple(map(int, deployment_parameters))
|
||||||
|
|
|
@ -18,21 +18,21 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import coincurve
|
|
||||||
import pytest
|
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_tester.exceptions import TransactionFailed
|
||||||
from eth_utils import to_canonical_address
|
from eth_utils import to_canonical_address
|
||||||
from web3.contract import Contract
|
from web3.contract import Contract
|
||||||
|
|
||||||
from nucypher.blockchain.eth.token import NU
|
|
||||||
from nucypher.policy.models import IndisputableEvidence
|
|
||||||
from umbral import pre
|
from umbral import pre
|
||||||
from umbral.curvebn import CurveBN
|
from umbral.curvebn import CurveBN
|
||||||
from umbral.keys import UmbralPrivateKey
|
from umbral.keys import UmbralPrivateKey
|
||||||
from umbral.signing import Signer, Signature
|
from umbral.signing import Signer
|
||||||
from cryptography.hazmat.backends.openssl import backend
|
|
||||||
from cryptography.hazmat.primitives import hashes
|
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
|
VALUE_FIELD = 0
|
||||||
|
@ -46,12 +46,6 @@ policy_manager_secret = os.urandom(SECRET_LENGTH)
|
||||||
user_escrow_secret = os.urandom(SECRET_LENGTH)
|
user_escrow_secret = os.urandom(SECRET_LENGTH)
|
||||||
adjudicator_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()
|
@pytest.fixture()
|
||||||
def token(testerchain):
|
def token(testerchain):
|
||||||
|
@ -109,7 +103,7 @@ def policy_manager(testerchain, escrow):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def adjudicator(testerchain, escrow):
|
def adjudicator(testerchain, escrow, slashing_economics):
|
||||||
escrow, _ = escrow
|
escrow, _ = escrow
|
||||||
creator = testerchain.interface.w3.eth.accounts[0]
|
creator = testerchain.interface.w3.eth.accounts[0]
|
||||||
|
|
||||||
|
@ -119,11 +113,8 @@ def adjudicator(testerchain, escrow):
|
||||||
contract, _ = testerchain.interface.deploy_contract(
|
contract, _ = testerchain.interface.deploy_contract(
|
||||||
'MiningAdjudicator',
|
'MiningAdjudicator',
|
||||||
escrow.address,
|
escrow.address,
|
||||||
ALGORITHM_SHA256,
|
*slashing_economics.deployment_parameters)
|
||||||
BASE_PENALTY,
|
|
||||||
PENALTY_HISTORY_COEFFICIENT,
|
|
||||||
PERCENTAGE_PENALTY_COEFFICIENT,
|
|
||||||
REWARD_COEFFICIENT)
|
|
||||||
dispatcher, _ = testerchain.interface.deploy_contract('Dispatcher', contract.address, secret_hash)
|
dispatcher, _ = testerchain.interface.deploy_contract('Dispatcher', contract.address, secret_hash)
|
||||||
|
|
||||||
# Wrap dispatcher contract
|
# 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: Obtain real re-encryption metadata. Maybe constructing a WorkOrder and obtaining a response.
|
||||||
# TODO organize support functions
|
# TODO organize support functions
|
||||||
|
# TODO: Repeated method in estimate_gas
|
||||||
def generate_args_for_slashing(testerchain, miner):
|
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()
|
delegating_privkey = UmbralPrivateKey.gen_key()
|
||||||
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
|
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
|
||||||
signing_privkey = UmbralPrivateKey.gen_key()
|
signing_privkey = UmbralPrivateKey.gen_key()
|
||||||
|
@ -174,19 +147,34 @@ def generate_args_for_slashing(testerchain, miner):
|
||||||
sign_delegating_key=False,
|
sign_delegating_key=False,
|
||||||
sign_receiving_key=False)
|
sign_receiving_key=False)
|
||||||
capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob, signing_privkey.get_pubkey())
|
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_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
|
# Sign Umbral public key using eth-key
|
||||||
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
|
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
|
||||||
hash_ctx.update(miner_umbral_public_key_bytes)
|
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]
|
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))
|
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 = IndisputableEvidence(capsule, cfrag, ursula=None)
|
||||||
evidence_data = evidence.precompute_values()
|
evidence_data = evidence.precompute_values()
|
||||||
return data_hash, (capsule_bytes,
|
|
||||||
capsule_signature_by_requester,
|
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
|
||||||
capsule_signature_by_requester_and_miner,
|
hash_ctx.update(bytes(capsule) + bytes(cfrag))
|
||||||
cfrag_bytes,
|
data_hash = hash_ctx.finalize()
|
||||||
cfrag_signature_by_miner,
|
|
||||||
requester_umbral_public_key_bytes,
|
return data_hash, (bytes(capsule),
|
||||||
|
bytes(cfrag),
|
||||||
|
cfrag_signature,
|
||||||
|
task_signature,
|
||||||
|
recovery_values,
|
||||||
|
bob_pubkey_bytes,
|
||||||
miner_umbral_public_key_bytes,
|
miner_umbral_public_key_bytes,
|
||||||
signed_miner_umbral_public_key,
|
signed_miner_umbral_public_key,
|
||||||
evidence_data)
|
evidence_data)
|
||||||
|
@ -272,7 +263,7 @@ def execute_multisig_transaction(testerchain, multisig, accounts, tx):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.slow
|
@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
|
# Travel to the start of the next period to prevent problems with unexpected overflow first period
|
||||||
testerchain.time_travel(hours=1)
|
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()
|
total_lock = escrow.functions.lockedPerPeriod(period).call()
|
||||||
alice1_balance = token.functions.balanceOf(alice1).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)
|
data_hash, slashing_args = generate_args_for_slashing(testerchain, ursula1)
|
||||||
assert not adjudicator.functions.evaluatedCFrags(data_hash).call()
|
assert not adjudicator.functions.evaluatedCFrags(data_hash).call()
|
||||||
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice1})
|
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice1})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
assert adjudicator.functions.evaluatedCFrags(data_hash).call()
|
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 previous_lock == escrow.functions.getLockedTokensInPast(ursula1, 1).call()
|
||||||
assert lock == escrow.functions.getLockedTokens(ursula1).call()
|
assert lock == escrow.functions.getLockedTokens(ursula1).call()
|
||||||
assert next_lock == escrow.functions.getLockedTokens(ursula1, 1).call()
|
assert next_lock == escrow.functions.getLockedTokens(ursula1, 1).call()
|
||||||
assert total_previous_lock == escrow.functions.lockedPerPeriod(period - 1).call()
|
assert total_previous_lock == escrow.functions.lockedPerPeriod(period - 1).call()
|
||||||
assert total_lock == escrow.functions.lockedPerPeriod(period).call()
|
assert total_lock == escrow.functions.lockedPerPeriod(period).call()
|
||||||
assert 0 == escrow.functions.lockedPerPeriod(period + 1).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
|
# Slash part of the one sub stake
|
||||||
tokens_amount = escrow.functions.minerInfo(ursula2).call()[VALUE_FIELD]
|
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})
|
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice1})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
assert adjudicator.functions.evaluatedCFrags(data_hash).call()
|
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 previous_lock == escrow.functions.getLockedTokensInPast(ursula2, 1).call()
|
||||||
assert lock - BASE_PENALTY == escrow.functions.getLockedTokens(ursula2).call()
|
assert lock - base_penalty == escrow.functions.getLockedTokens(ursula2).call()
|
||||||
assert next_lock - BASE_PENALTY == escrow.functions.getLockedTokens(ursula2, 1).call()
|
assert next_lock - base_penalty == escrow.functions.getLockedTokens(ursula2, 1).call()
|
||||||
assert total_previous_lock == escrow.functions.lockedPerPeriod(period - 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 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
|
# Upgrade the adjudicator
|
||||||
# Deploy the same contract as the second version
|
# 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(
|
adjudicator_v2, _ = testerchain.interface.deploy_contract(
|
||||||
'MiningAdjudicator',
|
'MiningAdjudicator',
|
||||||
escrow.address,
|
escrow.address,
|
||||||
ALGORITHM_SHA256,
|
*slashing_economics.deployment_parameters)
|
||||||
BASE_PENALTY,
|
|
||||||
PENALTY_HISTORY_COEFFICIENT,
|
|
||||||
PERCENTAGE_PENALTY_COEFFICIENT,
|
|
||||||
REWARD_COEFFICIENT)
|
|
||||||
adjudicator_secret2 = os.urandom(SECRET_LENGTH)
|
adjudicator_secret2 = os.urandom(SECRET_LENGTH)
|
||||||
adjudicator_secret2_hash = testerchain.interface.w3.keccak(adjudicator_secret2)
|
adjudicator_secret2_hash = testerchain.interface.w3.keccak(adjudicator_secret2)
|
||||||
# Ursula and Alice can't upgrade library, only owner can
|
# 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})
|
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice2})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
assert adjudicator.functions.evaluatedCFrags(data_hash).call()
|
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 lock - penalty == escrow.functions.minerInfo(ursula1).call()[VALUE_FIELD]
|
||||||
assert previous_lock == escrow.functions.getLockedTokensInPast(ursula1, 1).call()
|
assert previous_lock == escrow.functions.getLockedTokensInPast(ursula1, 1).call()
|
||||||
assert lock - penalty == escrow.functions.getLockedTokens(ursula1).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_previous_lock == escrow.functions.lockedPerPeriod(period - 1).call()
|
||||||
assert total_lock - penalty == escrow.functions.lockedPerPeriod(period).call()
|
assert total_lock - penalty == escrow.functions.lockedPerPeriod(period).call()
|
||||||
assert 0 == escrow.functions.lockedPerPeriod(period + 1).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
|
# Unlock and withdraw all tokens in MinersEscrow
|
||||||
for index in range(9):
|
for index in range(9):
|
||||||
|
|
|
@ -34,11 +34,7 @@ def adjudicator_contract(testerchain, escrow, request, slashing_economics):
|
||||||
contract, _ = testerchain.interface.deploy_contract(
|
contract, _ = testerchain.interface.deploy_contract(
|
||||||
'MiningAdjudicator',
|
'MiningAdjudicator',
|
||||||
escrow.address,
|
escrow.address,
|
||||||
slashing_economics.algorithm_sha256,
|
*slashing_economics.deployment_parameters)
|
||||||
slashing_economics.base_penalty,
|
|
||||||
slashing_economics.penalty_history_coefficient,
|
|
||||||
slashing_economics.percentage_penalty_coefficient,
|
|
||||||
slashing_economics.reward_coefficient)
|
|
||||||
|
|
||||||
if request.param:
|
if request.param:
|
||||||
secret = os.urandom(DispatcherDeployer.DISPATCHER_SECRET_LENGTH)
|
secret = os.urandom(DispatcherDeployer.DISPATCHER_SECRET_LENGTH)
|
||||||
|
|
|
@ -18,24 +18,21 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import coincurve
|
|
||||||
import pytest
|
import pytest
|
||||||
from cryptography.hazmat.backends.openssl import backend
|
from cryptography.hazmat.backends.openssl import backend
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.primitives.asymmetric import ec
|
|
||||||
from eth_tester.exceptions import TransactionFailed
|
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 typing import Tuple
|
||||||
from web3.contract import Contract
|
from web3.contract import Contract
|
||||||
|
|
||||||
from umbral import pre
|
from umbral import pre
|
||||||
from umbral.config import default_params
|
|
||||||
from umbral.curvebn import CurveBN
|
from umbral.curvebn import CurveBN
|
||||||
from umbral.keys import UmbralPrivateKey
|
from umbral.keys import UmbralPrivateKey
|
||||||
from umbral.point import Point
|
from umbral.point import Point
|
||||||
from umbral.random_oracles import hash_to_curvebn, ExtendedKeccak
|
from umbral.signing import Signer
|
||||||
from umbral.signing import Signature, Signer
|
|
||||||
|
|
||||||
|
from nucypher.crypto.utils import get_signature_recovery_value
|
||||||
from nucypher.policy.models import IndisputableEvidence
|
from nucypher.policy.models import IndisputableEvidence
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,33 +42,8 @@ secret = (123456).to_bytes(32, byteorder='big')
|
||||||
secret2 = (654321).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.
|
# 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()
|
delegating_privkey = UmbralPrivateKey.gen_key()
|
||||||
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
|
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
|
||||||
signing_privkey = UmbralPrivateKey.gen_key()
|
signing_privkey = UmbralPrivateKey.gen_key()
|
||||||
|
@ -86,8 +58,42 @@ def fragments(metadata):
|
||||||
sign_delegating_key=False,
|
sign_delegating_key=False,
|
||||||
sign_receiving_key=False)
|
sign_receiving_key=False)
|
||||||
capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob, signing_privkey.get_pubkey())
|
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)
|
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
|
@pytest.mark.slow
|
||||||
|
@ -113,7 +119,7 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
|
||||||
|
|
||||||
# Generate miner's Umbral key
|
# Generate miner's Umbral key
|
||||||
miner_umbral_private_key = UmbralPrivateKey.gen_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
|
# Sign Umbral public key using eth-key
|
||||||
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
|
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]
|
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))
|
signed_miner_umbral_public_key = bytes(sig_key.sign_msg_hash(miner_umbral_public_key_hash))
|
||||||
|
|
||||||
# Prepare hash of the data
|
# Prepare evaluation data
|
||||||
metadata = os.urandom(33)
|
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key)
|
||||||
capsule, cfrag = fragments(metadata)
|
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
|
||||||
|
|
||||||
assert cfrag.verify_correctness(capsule)
|
assert cfrag.verify_correctness(capsule)
|
||||||
|
|
||||||
capsule_bytes = capsule.to_bytes()
|
# Investigator prepares supporting Evidence
|
||||||
cfrag_bytes = cfrag.to_bytes()
|
|
||||||
|
|
||||||
# Bob prepares supporting Evidence
|
|
||||||
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
|
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
|
||||||
|
|
||||||
evidence_data = evidence.precompute_values()
|
evidence_data = evidence.precompute_values()
|
||||||
assert len(evidence_data) == 20 * 32 + 32 + 20 + 1
|
assert len(evidence_data) == 20 * 32 + 32 + 20 + 1
|
||||||
|
|
||||||
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
|
data_hash = evaluation_hash(capsule, cfrag)
|
||||||
hash_ctx.update(capsule_bytes + cfrag_bytes)
|
|
||||||
data_hash = hash_ctx.finalize()
|
|
||||||
# This capsule and cFrag are not yet evaluated
|
# This capsule and cFrag are not yet evaluated
|
||||||
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
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
|
# Challenge using good data
|
||||||
args = (capsule_bytes,
|
args = (bytes(capsule),
|
||||||
capsule_signature_by_requester,
|
bytes(cfrag),
|
||||||
capsule_signature_by_requester_and_miner,
|
cfrag_signature,
|
||||||
cfrag_bytes,
|
task_signature,
|
||||||
cfrag_signature_by_miner,
|
recovery_values,
|
||||||
requester_umbral_public_key_bytes,
|
bob_pubkey_bytes,
|
||||||
miner_umbral_public_key_bytes,
|
miner_umbral_public_key_bytes,
|
||||||
signed_miner_umbral_public_key,
|
signed_miner_umbral_public_key,
|
||||||
evidence_data)
|
evidence_data)
|
||||||
|
@ -183,39 +174,36 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
|
||||||
assert investigator == event_args['investigator']
|
assert investigator == event_args['investigator']
|
||||||
assert event_args['correctness']
|
assert event_args['correctness']
|
||||||
|
|
||||||
|
###############################
|
||||||
# Test: Don't evaluate miner with data that already was checked
|
# Test: Don't evaluate miner with data that already was checked
|
||||||
|
###############################
|
||||||
|
# TODO: be more specific on the expected exception
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact()
|
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
###############################
|
||||||
# Test: Ursula produces incorrect proof:
|
# Test: Ursula produces incorrect proof:
|
||||||
metadata = os.urandom(34)
|
###############################
|
||||||
capsule, cfrag = fragments(metadata)
|
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key, corrupt_cfrag=True)
|
||||||
capsule_bytes = capsule.to_bytes()
|
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
|
||||||
# Corrupt proof
|
|
||||||
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
|
|
||||||
cfrag_bytes = cfrag.to_bytes()
|
|
||||||
assert not cfrag.verify_correctness(capsule)
|
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 = IndisputableEvidence(capsule, cfrag, ursula=None)
|
||||||
evidence_data = evidence.precompute_values()
|
evidence_data = evidence.precompute_values()
|
||||||
args = (capsule_bytes,
|
args = (bytes(capsule),
|
||||||
capsule_signature_by_requester,
|
bytes(cfrag),
|
||||||
capsule_signature_by_requester_and_miner,
|
cfrag_signature,
|
||||||
cfrag_bytes,
|
task_signature,
|
||||||
cfrag_signature_by_miner,
|
recovery_values,
|
||||||
requester_umbral_public_key_bytes,
|
bob_pubkey_bytes,
|
||||||
miner_umbral_public_key_bytes,
|
miner_umbral_public_key_bytes,
|
||||||
signed_miner_umbral_public_key,
|
signed_miner_umbral_public_key,
|
||||||
evidence_data)
|
evidence_data)
|
||||||
|
|
||||||
|
data_hash = evaluation_hash(capsule, cfrag)
|
||||||
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
||||||
|
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
|
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
number_of_evaluations += 1
|
number_of_evaluations += 1
|
||||||
|
@ -243,18 +231,10 @@ def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract, slashing_econ
|
||||||
# Test: Bob produces wrong precomputed data
|
# Test: Bob produces wrong precomputed data
|
||||||
###############################
|
###############################
|
||||||
|
|
||||||
metadata = os.urandom(34)
|
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key)
|
||||||
capsule, cfrag = fragments(metadata)
|
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
|
||||||
capsule_bytes = capsule.to_bytes()
|
|
||||||
cfrag_bytes = cfrag.to_bytes()
|
|
||||||
assert cfrag.verify_correctness(capsule)
|
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 = IndisputableEvidence(capsule, cfrag, ursula=None)
|
||||||
evidence_data = evidence.precompute_values()
|
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[32:32+64] = random_point_bytes
|
||||||
evidence_data = bytes(evidence_data)
|
evidence_data = bytes(evidence_data)
|
||||||
|
|
||||||
args = (capsule_bytes,
|
args = (bytes(capsule),
|
||||||
capsule_signature_by_requester,
|
bytes(cfrag),
|
||||||
capsule_signature_by_requester_and_miner,
|
cfrag_signature,
|
||||||
cfrag_bytes,
|
task_signature,
|
||||||
cfrag_signature_by_miner,
|
recovery_values,
|
||||||
requester_umbral_public_key_bytes,
|
bob_pubkey_bytes,
|
||||||
miner_umbral_public_key_bytes,
|
miner_umbral_public_key_bytes,
|
||||||
signed_miner_umbral_public_key,
|
signed_miner_umbral_public_key,
|
||||||
evidence_data)
|
evidence_data)
|
||||||
|
|
||||||
|
data_hash = evaluation_hash(capsule, cfrag)
|
||||||
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
||||||
|
|
||||||
# Evaluation must fail since Bob precomputed wrong values
|
# 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
|
# Test: Second violation. Penalty is increased
|
||||||
###############################
|
###############################
|
||||||
|
|
||||||
# Prepare hash of the data
|
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key, corrupt_cfrag=True)
|
||||||
metadata = os.urandom(34)
|
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
|
||||||
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)
|
|
||||||
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
|
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
|
||||||
evidence_data = evidence.precompute_values()
|
evidence_data = evidence.precompute_values()
|
||||||
args = [capsule_bytes,
|
args = (bytes(capsule),
|
||||||
capsule_signature_by_requester,
|
bytes(cfrag),
|
||||||
capsule_signature_by_requester_and_miner,
|
cfrag_signature,
|
||||||
cfrag_bytes,
|
task_signature,
|
||||||
cfrag_signature_by_miner,
|
recovery_values,
|
||||||
requester_umbral_public_key_bytes,
|
bob_pubkey_bytes,
|
||||||
miner_umbral_public_key_bytes,
|
miner_umbral_public_key_bytes,
|
||||||
signed_miner_umbral_public_key,
|
signed_miner_umbral_public_key,
|
||||||
evidence_data]
|
evidence_data)
|
||||||
|
|
||||||
|
data_hash = evaluation_hash(capsule, cfrag)
|
||||||
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
||||||
|
|
||||||
worker_stake = escrow.functions.minerInfo(miner).call()[0]
|
worker_stake = escrow.functions.minerInfo(miner).call()[0]
|
||||||
investigator_balance = escrow.functions.rewardInfo(investigator).call()
|
investigator_balance = escrow.functions.rewardInfo(investigator).call()
|
||||||
|
|
||||||
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
|
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
|
||||||
testerchain.wait_for_receipt(tx)
|
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
|
# Test: Third violation. Penalty reaches the maximum allowed
|
||||||
###############################
|
###############################
|
||||||
|
|
||||||
# Prepare corrupted data again
|
capsule, cfrag, *other_stuff = mock_ursula_reencrypts(miner_umbral_private_key, corrupt_cfrag=True)
|
||||||
capsule, cfrag = fragments(metadata)
|
cfrag_signature, task_signature, recovery_values, bob_pubkey_bytes = other_stuff
|
||||||
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)
|
|
||||||
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
|
evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
|
||||||
evidence_data = evidence.precompute_values()
|
evidence_data = evidence.precompute_values()
|
||||||
args = [capsule_bytes,
|
args = (bytes(capsule),
|
||||||
capsule_signature_by_requester,
|
bytes(cfrag),
|
||||||
capsule_signature_by_requester_and_miner,
|
cfrag_signature,
|
||||||
cfrag_bytes,
|
task_signature,
|
||||||
cfrag_signature_by_miner,
|
recovery_values,
|
||||||
requester_umbral_public_key_bytes,
|
bob_pubkey_bytes,
|
||||||
miner_umbral_public_key_bytes,
|
miner_umbral_public_key_bytes,
|
||||||
signed_miner_umbral_public_key,
|
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]
|
worker_stake = escrow.functions.minerInfo(miner).call()[0]
|
||||||
investigator_balance = escrow.functions.rewardInfo(investigator).call()
|
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})
|
tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
number_of_evaluations += 1
|
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
|
# Can't evaluate miner using broken signatures
|
||||||
wrong_args = args[:]
|
wrong_args = list(args)
|
||||||
wrong_args[1] = capsule_signature_by_requester[1:]
|
wrong_args[2] = cfrag_signature[1:]
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
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)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
wrong_args = args[:]
|
|
||||||
wrong_args[4] = cfrag_signature_by_miner[1:]
|
wrong_args = list(args)
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
|
||||||
testerchain.wait_for_receipt(tx)
|
|
||||||
wrong_args = args[:]
|
|
||||||
wrong_args[7] = signed_miner_umbral_public_key[1:]
|
wrong_args[7] = signed_miner_umbral_public_key[1:]
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
# Can't evaluate miner using wrong keys
|
# Can't evaluate miner using wrong keys
|
||||||
wrong_args = args[:]
|
wrong_args = list(args)
|
||||||
wrong_args[5] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(is_compressed=False)
|
wrong_args[5] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(is_compressed=False)[1:]
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
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)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
# Can't use signature for another data
|
# Can't use signature for another data
|
||||||
wrong_args = args[:]
|
wrong_args = list(args)
|
||||||
wrong_args[0] = bytes(args[0][0] + 1) + args[0][1:]
|
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)
|
|
||||||
wrong_args = args[:]
|
|
||||||
wrong_args[3] = bytes(args[3][0] + 1) + args[3][1:]
|
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
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)
|
address = to_canonical_address(wrong_miner)
|
||||||
sig_key = provider.ethereum_tester.backend._key_lookup[address]
|
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))
|
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
|
wrong_args[7] = signed_wrong_miner_umbral_public_key
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
|
||||||
|
|
|
@ -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/>.
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import coincurve
|
import io
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
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 time
|
import time
|
||||||
from os.path import abspath, dirname
|
from os.path import abspath, dirname
|
||||||
|
|
||||||
import io
|
from eth_utils import to_canonical_address
|
||||||
import re
|
|
||||||
|
|
||||||
from cryptography.hazmat.backends.openssl import backend
|
from cryptography.hazmat.backends.openssl import backend
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
from twisted.logger import globalLogPublisher, Logger, jsonFileLogObserver, ILogObserver
|
from twisted.logger import globalLogPublisher, Logger, jsonFileLogObserver, ILogObserver
|
||||||
from zope.interface import provider
|
from zope.interface import provider
|
||||||
|
|
||||||
from nucypher.blockchain.eth.agents import NucypherTokenAgent, MinerAgent, PolicyAgent, MiningAdjudicatorAgent
|
from umbral import pre
|
||||||
from nucypher.utilities.sandbox.blockchain import TesterBlockchain
|
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
|
ALGORITHM_SHA256 = 1
|
||||||
TOKEN_ECONOMICS = TokenEconomics()
|
TOKEN_ECONOMICS = TokenEconomics()
|
||||||
|
@ -121,26 +116,8 @@ class AnalyzeGas:
|
||||||
|
|
||||||
|
|
||||||
# TODO organize support functions
|
# TODO organize support functions
|
||||||
|
# TODO: Repeated method in test_intercontract_integration
|
||||||
def generate_args_for_slashing(testerchain, miner, corrupt: bool = True):
|
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()
|
delegating_privkey = UmbralPrivateKey.gen_key()
|
||||||
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
|
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
|
||||||
signing_privkey = UmbralPrivateKey.gen_key()
|
signing_privkey = UmbralPrivateKey.gen_key()
|
||||||
|
@ -155,18 +132,35 @@ def generate_args_for_slashing(testerchain, miner, corrupt: bool = True):
|
||||||
sign_delegating_key=False,
|
sign_delegating_key=False,
|
||||||
sign_receiving_key=False)
|
sign_receiving_key=False)
|
||||||
capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob, signing_privkey.get_pubkey())
|
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:
|
if corrupt:
|
||||||
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
|
cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
|
||||||
cfrag_bytes = cfrag.to_bytes()
|
|
||||||
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
|
cfrag_signature = bytes(ursulas_signer(bytes(cfrag)))
|
||||||
hash_ctx.update(capsule_bytes + cfrag_bytes)
|
|
||||||
requester_umbral_private_key = UmbralPrivateKey.gen_key()
|
bob_pubkey_bytes = pub_key_bob.to_bytes(is_compressed=False)[1:]
|
||||||
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)
|
cfrag_signature_v = get_signature_recovery_value(message=bytes(cfrag),
|
||||||
miner_umbral_private_key = UmbralPrivateKey.gen_key()
|
signature=cfrag_signature,
|
||||||
miner_umbral_public_key_bytes = miner_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)
|
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
|
# Sign Umbral public key using eth-key
|
||||||
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
|
hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
|
||||||
hash_ctx.update(miner_umbral_public_key_bytes)
|
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]
|
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))
|
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 = IndisputableEvidence(capsule, cfrag, ursula=None)
|
||||||
evidence_data = evidence.precompute_values()
|
evidence_data = evidence.precompute_values()
|
||||||
return (capsule_bytes,
|
|
||||||
capsule_signature_by_requester,
|
return (bytes(capsule),
|
||||||
capsule_signature_by_requester_and_miner,
|
bytes(cfrag),
|
||||||
cfrag_bytes,
|
cfrag_signature,
|
||||||
cfrag_signature_by_miner,
|
task_signature,
|
||||||
requester_umbral_public_key_bytes,
|
recovery_values,
|
||||||
|
bob_pubkey_bytes,
|
||||||
miner_umbral_public_key_bytes,
|
miner_umbral_public_key_bytes,
|
||||||
signed_miner_umbral_public_key,
|
signed_miner_umbral_public_key,
|
||||||
evidence_data)
|
evidence_data)
|
||||||
|
|
Loading…
Reference in New Issue