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'
|
||||
python_project_name = 'nucypher-kms'
|
||||
_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')
|
||||
|
||||
def __init__(self, project_name='nucypher-kms', timeout=60):
|
||||
|
|
|
@ -1,83 +1,5 @@
|
|||
import random
|
||||
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
|
||||
|
||||
|
|
|
@ -1,37 +1,101 @@
|
|||
from nkms_eth import blockchain
|
||||
|
||||
CONTRACT_NAME = 'NuCypherKMSToken' # TODO this should be NuCypher's class
|
||||
SUBDIGITS = 18
|
||||
M = 10 ** SUBDIGITS
|
||||
SATURATION = int(1e10) * M
|
||||
from populus.contracts.contract import PopulusContract
|
||||
from .blockchain import Blockchain
|
||||
|
||||
|
||||
def create():
|
||||
"""
|
||||
Creates a contract with tokens and returns it.
|
||||
If it was already created, just returns the already existing contract
|
||||
class NuCypherKMSToken:
|
||||
_contract_name = 'NuCypherKMSToken'
|
||||
subdigits = 18
|
||||
M = 10 ** subdigits
|
||||
premine = int(1e9) * M
|
||||
saturation = int(1e10) * M
|
||||
|
||||
:returns: Token contract object
|
||||
"""
|
||||
chain = blockchain.chain()
|
||||
web3 = chain.web3
|
||||
creator = web3.eth.accounts[0] # TODO: make it possible to override
|
||||
class ContractDeploymentError(Exception):
|
||||
pass
|
||||
|
||||
token, tx = chain.provider.get_or_deploy_contract(
|
||||
CONTRACT_NAME, deploy_args=[SATURATION],
|
||||
deploy_transaction={'from': creator})
|
||||
if tx:
|
||||
chain.wait.for_receipt(tx, timeout=blockchain.TIMEOUT)
|
||||
def __init__(self, blockchain: Blockchain, token_contract: PopulusContract=None):
|
||||
self.creator = blockchain.web3.eth.accounts[0]
|
||||
self.blockchain = blockchain
|
||||
self.contract = token_contract
|
||||
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):
|
||||
"""
|
||||
Gets an existing contract or returns an error
|
||||
"""
|
||||
return blockchain.chain().provider.get_contract(name)
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Invoke contract -> No state change"""
|
||||
return self.contract.call(*args, **kwargs)
|
||||
|
||||
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):
|
||||
return get().call().balanceOf(address)
|
||||
def arm(self):
|
||||
"""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