mirror of https://github.com/nucypher/nucypher.git
[KMS-ETH]- Reintroduce class based python interface, post-rebase.
parent
234bd5e62e
commit
3608b27024
|
@ -20,6 +20,8 @@ class Blockchain:
|
||||||
network = '' # 'mainnetrpc'
|
network = '' # 'mainnetrpc'
|
||||||
python_project_name = 'nucypher-kms'
|
python_project_name = 'nucypher-kms'
|
||||||
_project = threading.local()
|
_project = threading.local()
|
||||||
|
|
||||||
|
# This config is persistent and is created in user's .local directory
|
||||||
registrar_path = join(appdirs.user_data_dir(python_project_name), 'registrar.json')
|
registrar_path = join(appdirs.user_data_dir(python_project_name), 'registrar.json')
|
||||||
|
|
||||||
def __init__(self, project_name='nucypher-kms', timeout=60):
|
def __init__(self, project_name='nucypher-kms', timeout=60):
|
||||||
|
|
|
@ -1,83 +1,5 @@
|
||||||
import random
|
import random
|
||||||
from typing import List
|
from typing import List
|
||||||
<<<<<<< HEAD
|
|
||||||
from nkms_eth import blockchain
|
|
||||||
from nkms_eth import token
|
|
||||||
|
|
||||||
ESCROW_NAME = 'MinersEscrow'
|
|
||||||
PREMINE = int(1e9) * token.M
|
|
||||||
REWARD = token.SATURATION - PREMINE
|
|
||||||
HOURS_PER_PERIOD = 1 # 24
|
|
||||||
MIN_RELEASE_PERIODS = 1 # 30
|
|
||||||
MAX_AWARDED_PERIODS = 365
|
|
||||||
MIN_ALLOWED_LOCKED = 10 ** 6
|
|
||||||
MAX_ALLOWED_LOCKED = 10 ** 7 * token.M
|
|
||||||
MINING_COEFF = [
|
|
||||||
HOURS_PER_PERIOD,
|
|
||||||
2 * 10 ** 7,
|
|
||||||
MAX_AWARDED_PERIODS,
|
|
||||||
MAX_AWARDED_PERIODS,
|
|
||||||
MIN_RELEASE_PERIODS,
|
|
||||||
MIN_ALLOWED_LOCKED,
|
|
||||||
MAX_ALLOWED_LOCKED
|
|
||||||
]
|
|
||||||
NULL_ADDR = '0x' + '0' * 40
|
|
||||||
|
|
||||||
|
|
||||||
def create():
|
|
||||||
"""
|
|
||||||
Creates an escrow which manages mining
|
|
||||||
"""
|
|
||||||
chain = blockchain.chain()
|
|
||||||
creator = chain.web3.eth.accounts[0] # TODO: make it possible to override
|
|
||||||
tok = token.get()
|
|
||||||
escrow, tx = chain.provider.get_or_deploy_contract(
|
|
||||||
ESCROW_NAME, deploy_args=[token.get().address] + MINING_COEFF,
|
|
||||||
deploy_transaction={'from': creator})
|
|
||||||
chain.wait.for_receipt(tx, timeout=blockchain.TIMEOUT)
|
|
||||||
tx = tok.transact({'from': creator}).transfer(escrow.address, REWARD)
|
|
||||||
chain.wait.for_receipt(tx, timeout=blockchain.TIMEOUT)
|
|
||||||
tx = escrow.transact({'from': creator}).initialize()
|
|
||||||
chain.wait.for_receipt(tx, timeout=blockchain.TIMEOUT)
|
|
||||||
return escrow
|
|
||||||
|
|
||||||
|
|
||||||
def get():
|
|
||||||
"""
|
|
||||||
Returns an escrow object
|
|
||||||
"""
|
|
||||||
return token.get(ESCROW_NAME)
|
|
||||||
|
|
||||||
|
|
||||||
def sample(n: int = 10)-> List[str]:
|
|
||||||
"""
|
|
||||||
Select n random staking Ursulas, according to their stake distribution
|
|
||||||
The returned addresses are shuffled, so one can request more than needed and
|
|
||||||
throw away those which do not respond
|
|
||||||
"""
|
|
||||||
escrow = get()
|
|
||||||
n_select = int(n * 1.7) # Select more ursulas
|
|
||||||
n_tokens = escrow.call().getAllLockedTokens()
|
|
||||||
duration = 10
|
|
||||||
|
|
||||||
for _ in range(5): # number of tries
|
|
||||||
points = [0] + sorted(random.randrange(n_tokens) for _ in
|
|
||||||
range(n_select))
|
|
||||||
deltas = [i - j for i, j in zip(points[1:], points[:-1])]
|
|
||||||
addrs = set()
|
|
||||||
addr = NULL_ADDR
|
|
||||||
shift = 0
|
|
||||||
|
|
||||||
for delta in deltas:
|
|
||||||
addr, shift = escrow.call().findCumSum(addr, delta + shift, duration)
|
|
||||||
addrs.add(addr)
|
|
||||||
|
|
||||||
if len(addrs) >= n:
|
|
||||||
addrs = random.sample(addrs, n)
|
|
||||||
return addrs
|
|
||||||
|
|
||||||
raise Exception('Not enough Ursulas')
|
|
||||||
=======
|
|
||||||
|
|
||||||
from populus.contracts.contract import PopulusContract
|
from populus.contracts.contract import PopulusContract
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,101 @@
|
||||||
from nkms_eth import blockchain
|
from populus.contracts.contract import PopulusContract
|
||||||
|
from .blockchain import Blockchain
|
||||||
CONTRACT_NAME = 'NuCypherKMSToken' # TODO this should be NuCypher's class
|
|
||||||
SUBDIGITS = 18
|
|
||||||
M = 10 ** SUBDIGITS
|
|
||||||
SATURATION = int(1e10) * M
|
|
||||||
|
|
||||||
|
|
||||||
def create():
|
class NuCypherKMSToken:
|
||||||
"""
|
_contract_name = 'NuCypherKMSToken'
|
||||||
Creates a contract with tokens and returns it.
|
subdigits = 18
|
||||||
If it was already created, just returns the already existing contract
|
M = 10 ** subdigits
|
||||||
|
premine = int(1e9) * M
|
||||||
|
saturation = int(1e10) * M
|
||||||
|
|
||||||
:returns: Token contract object
|
class ContractDeploymentError(Exception):
|
||||||
"""
|
pass
|
||||||
chain = blockchain.chain()
|
|
||||||
web3 = chain.web3
|
|
||||||
creator = web3.eth.accounts[0] # TODO: make it possible to override
|
|
||||||
|
|
||||||
token, tx = chain.provider.get_or_deploy_contract(
|
def __init__(self, blockchain: Blockchain, token_contract: PopulusContract=None):
|
||||||
CONTRACT_NAME, deploy_args=[SATURATION],
|
self.creator = blockchain.web3.eth.accounts[0]
|
||||||
deploy_transaction={'from': creator})
|
self.blockchain = blockchain
|
||||||
if tx:
|
self.contract = token_contract
|
||||||
chain.wait.for_receipt(tx, timeout=blockchain.TIMEOUT)
|
self.armed = False
|
||||||
|
|
||||||
return token
|
def __repr__(self):
|
||||||
|
class_name = self.__class__.__name__
|
||||||
|
r = "{}(blockchain={}, contract={})"
|
||||||
|
return r.format(class_name, self.blockchain, self.contract)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Two token objects are equal if they have the same contract address"""
|
||||||
|
return self.contract.address == other.contract.address
|
||||||
|
|
||||||
def get(name=CONTRACT_NAME):
|
def __call__(self, *args, **kwargs):
|
||||||
"""
|
"""Invoke contract -> No state change"""
|
||||||
Gets an existing contract or returns an error
|
return self.contract.call(*args, **kwargs)
|
||||||
"""
|
|
||||||
return blockchain.chain().provider.get_contract(name)
|
|
||||||
|
|
||||||
|
def _check_contract_deployment(self) -> None:
|
||||||
|
"""Raises ContractDeploymentError if the contract has not been armed and deployed."""
|
||||||
|
if not self.contract:
|
||||||
|
class_name = self.__class__.__name__
|
||||||
|
message = '{} contract is not deployed. Arm, then deploy.'.format(class_name)
|
||||||
|
raise self.ContractDeploymentError(message)
|
||||||
|
|
||||||
def balance(address: str):
|
def arm(self):
|
||||||
return get().call().balanceOf(address)
|
"""Arm contract for deployment to blockchain."""
|
||||||
|
self.armed = True
|
||||||
|
return self
|
||||||
|
|
||||||
|
def deploy(self):
|
||||||
|
"""Deploy and publish contract to the blockchain."""
|
||||||
|
|
||||||
|
if not self.armed:
|
||||||
|
raise self.ContractDeploymentError('use .arm() to arm the contract, then .deploy().')
|
||||||
|
|
||||||
|
if self.contract:
|
||||||
|
class_name = self.__class__.__name__
|
||||||
|
message = '{} contract already deployed, use .get() to retrieve it.'.format(class_name)
|
||||||
|
raise self.ContractDeploymentError(message)
|
||||||
|
|
||||||
|
token_contract, txhash = self.blockchain.chain.provider.deploy_contract(
|
||||||
|
self._contract_name,
|
||||||
|
deploy_args=[self.saturation],
|
||||||
|
deploy_transaction={'from': self.creator})
|
||||||
|
|
||||||
|
self.blockchain.chain.wait.for_receipt(txhash, timeout=self.blockchain.timeout)
|
||||||
|
|
||||||
|
self.contract = token_contract
|
||||||
|
return self
|
||||||
|
|
||||||
|
def transact(self, *args):
|
||||||
|
"""Invoke contract -> State change"""
|
||||||
|
self._check_contract_deployment()
|
||||||
|
result = self.contract.transact(*args)
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, blockchain):
|
||||||
|
"""Gets an existing token contract or returns an error"""
|
||||||
|
contract = blockchain.chain.provider.get_contract(cls._contract_name)
|
||||||
|
return cls(blockchain=blockchain, token_contract=contract)
|
||||||
|
|
||||||
|
def registrar(self):
|
||||||
|
"""Retrieve all known addresses for this contract"""
|
||||||
|
self._check_contract_deployment()
|
||||||
|
return self.blockchain.chain.registrar.get_contract_address(self._contract_name)
|
||||||
|
|
||||||
|
def balance(self, address: str):
|
||||||
|
"""Get the balance of a token address"""
|
||||||
|
self._check_contract_deployment()
|
||||||
|
return self.__call__().balanceOf(address)
|
||||||
|
|
||||||
|
def _airdrop(self, amount: int):
|
||||||
|
"""Airdrops from creator address to all other addresses"""
|
||||||
|
self._check_contract_deployment()
|
||||||
|
_, *addresses = self.blockchain.web3.eth.accounts
|
||||||
|
|
||||||
|
def txs():
|
||||||
|
for address in addresses:
|
||||||
|
yield self.transact({'from': self.creator}).transfer(address, amount*(10**6))
|
||||||
|
|
||||||
|
for tx in txs():
|
||||||
|
self.blockchain.chain.wait.for_receipt(tx, timeout=10)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
Loading…
Reference in New Issue