mirror of https://github.com/nucypher/nucypher.git
Touches to MultiSigDeployer and MultiSigAgent. Tests for both
parent
2e7cdc984b
commit
4aeb64486e
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue