Touches to MultiSigDeployer and MultiSigAgent. Tests for both

pull/1560/head
David Núñez 2020-01-14 22:17:34 +01:00
parent 2e7cdc984b
commit 4aeb64486e
4 changed files with 112 additions and 13 deletions

View File

@ -22,6 +22,7 @@ from typing import Generator, List, Tuple, Union
import math
from constant_sorrow.constants import NO_CONTRACT_AVAILABLE
from eth_utils.address import to_checksum_address
from eth_tester.exceptions import TransactionFailed
from twisted.logger import Logger
from web3.contract import Contract
@ -34,6 +35,7 @@ from nucypher.blockchain.eth.constants import (
STAKING_INTERFACE_ROUTER_CONTRACT_NAME,
ADJUDICATOR_CONTRACT_NAME,
NUCYPHER_TOKEN_CONTRACT_NAME,
MULTISIG_CONTRACT_NAME,
ETH_ADDRESS_BYTE_LENGTH
)
from nucypher.blockchain.eth.decorators import validate_checksum_address
@ -1126,6 +1128,8 @@ class SeederAgent(EthereumContractAgent):
class MultiSigAgent(EthereumContractAgent):
registry_contract_name = MULTISIG_CONTRACT_NAME
Vector = List[str]
@property
@ -1133,9 +1137,24 @@ class MultiSigAgent(EthereumContractAgent):
nonce = self.contract.functions.nonce().call()
return nonce
def get_owners(self) -> Tuple[str]:
result = self.contract.functions.owners().call()
return tuple(result)
def get_owner(self, index: int) -> str:
owner = self.contract.functions.owners(index).call()
return owner
@property
def owners(self) -> Tuple[str]:
i = 0
owners = list()
array_is_within_bounds = True
while array_is_within_bounds:
try:
owner = self.get_owner(i)
except (TransactionFailed, ValueError):
array_is_within_bounds = False
else:
owners.append(owner)
i += 1
return tuple(owners)
@property
def threshold(self) -> int:
@ -1167,13 +1186,13 @@ class MultiSigAgent(EthereumContractAgent):
data: bytes,
nonce: int
) -> bytes:
transaction_hash = self.contract.functions.getUnsignedTransactionHash(
trustee_address,
target_address,
value,
data,
nonce
).call()
transaction_args = (trustee_address,
target_address,
value,
data,
nonce)
transaction_hash = self.contract.functions.getUnsignedTransactionHash(*transaction_args).call()
return transaction_hash
def execute(self,

View File

@ -13,6 +13,7 @@ STAKING_INTERFACE_CONTRACT_NAME = 'StakingInterface'
PREALLOCATION_ESCROW_CONTRACT_NAME = 'PreallocationEscrow'
ADJUDICATOR_CONTRACT_NAME = 'Adjudicator'
WORKLOCK_CONTRACT_NAME = 'WorkLock'
MULTISIG_CONTRACT_NAME = 'MultiSig'
# Ethereum

View File

@ -1176,8 +1176,19 @@ class MultiSigDeployer(BaseContractDeployer):
agency = MultiSigAgent
contract_name = agency.registry_contract_name
deployment_steps = ('contract_deployment', )
_upgradeable = False
MAX_OWNER_COUNT = 50 # Hard-coded limit in MultiSig contract
def _deploy_essential(self, threshold: int, owners: List[str], gas_limit: int = None):
if not (0 < threshold <= len(owners) <= self.MAX_OWNER_COUNT):
raise ValueError(f"Parameters threshold={threshold} and len(owners)={len(owners)} don't satisfy inequality "
f"0 < threshold <= len(owners) <= {self.MAX_OWNER_COUNT}")
if BlockchainDeployerInterface.NULL_ADDRESS in owners:
raise ValueError("The null address is not allowed as an owner")
if len(owners) != len(set(owners)):
raise ValueError("Can't use the same owner address more than once")
constructor_args = (threshold, owners)
multisig_contract, deploy_receipt = self.blockchain.deploy_contract(self.deployer_address,
@ -1187,7 +1198,7 @@ class MultiSigDeployer(BaseContractDeployer):
gas_limit=gas_limit)
return multisig_contract, deploy_receipt
def deploy(self, gas_limit: int, progress=None, *args, **kwargs) -> dict:
def deploy(self, gas_limit: int = None, progress=None, *args, **kwargs) -> dict:
self.check_deployment_readiness()
multisig_contract, deploy_receipt = self._deploy_essential(gas_limit=gas_limit, *args, **kwargs)
@ -1197,7 +1208,7 @@ class MultiSigDeployer(BaseContractDeployer):
progress.update(1)
# Gather the transaction receipts
self.deployment_receipts.update({'deployment': deploy_receipt})
self.deployment_receipts.update({self.deployment_steps[0]: deploy_receipt})
self._contract = multisig_contract
return deploy_receipt
return self.deployment_receipts

View File

@ -0,0 +1,68 @@
"""
This file is part of nucypher.
nucypher is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
nucypher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
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 pytest
from nucypher.blockchain.eth.agents import MultiSigAgent
from nucypher.blockchain.eth.deployers import MultiSigDeployer
from nucypher.blockchain.eth.interfaces import BlockchainInterface
@pytest.mark.slow()
def test_multisig_deployer_and_agent(testerchain,
deployment_progress,
test_registry):
origin = testerchain.etherbase_account
multisig_deployer = MultiSigDeployer(deployer_address=origin, registry=test_registry)
# Can't have a threshold of 0
with pytest.raises(ValueError):
owners = testerchain.unassigned_accounts[0:3]
_ = multisig_deployer.deploy(threshold=0, owners=owners)
# Can't have no owners
with pytest.raises(ValueError):
_ = multisig_deployer.deploy(threshold=1, owners=[])
# Can't have the zero address as an owner
with pytest.raises(ValueError):
owners = testerchain.unassigned_accounts[0:3] + [BlockchainInterface.NULL_ADDRESS]
_ = multisig_deployer.deploy(threshold=1, owners=owners)
# Can't have repeated owners
with pytest.raises(ValueError):
owners = testerchain.unassigned_accounts[0] * 3
_ = multisig_deployer.deploy(threshold=1, owners=owners)
# At last, sane initialization arguments for the MultiSig
threshold = 2
owners = testerchain.unassigned_accounts[0:3]
receipts = multisig_deployer.deploy(threshold=threshold, owners=owners)
for step in multisig_deployer.deployment_steps:
assert receipts[step]['status'] == 1
multisig_agent = multisig_deployer.make_agent() # type: MultiSigAgent
assert multisig_agent.nonce == 0
assert multisig_agent.threshold == threshold
for i, owner in enumerate(owners):
assert multisig_agent.get_owner(i) == owner
assert multisig_agent.is_owner(owner)
assert multisig_agent.owners == tuple(owners)