Exemple #1
0
    def calculate_all_rewards(block, client: ContractingClient):
        total_stamps_to_split = RewardManager.stamps_in_block(block)

        master_ratio, delegate_ratio, burn_ratio, foundation_ratio, developer_ratio = \
            client.get_var(contract='rewards', variable='S', arguments=['value'])

        master_reward = RewardManager.calculate_participant_reward(
            participant_ratio=master_ratio,
            number_of_participants=len(
                client.get_var(contract='masternodes',
                               variable='S',
                               arguments=['members'])),
            total_stamps_to_split=total_stamps_to_split)

        delegate_reward = RewardManager.calculate_participant_reward(
            participant_ratio=delegate_ratio,
            number_of_participants=len(
                client.get_var(contract='delegates',
                               variable='S',
                               arguments=['members'])),
            total_stamps_to_split=total_stamps_to_split)

        foundation_reward = RewardManager.calculate_participant_reward(
            participant_ratio=foundation_ratio,
            number_of_participants=1,
            total_stamps_to_split=total_stamps_to_split)

        developer_mapping = RewardManager.create_to_send_map(
            block=block, client=client, developer_ratio=developer_ratio)

        # burn does nothing, as the stamps are already deducted from supply

        return master_reward, delegate_reward, foundation_reward, developer_mapping
class TestDeveloperSubmission(TestCase):
    def setUp(self):
        self.c = ContractingClient(signer='stu')
        self.c.raw_driver.flush()

        with open('../../contracting/contracts/submission.s.py') as f:
            contract = f.read()

        self.c.raw_driver.set_contract(
            name='submission',
            code=contract,
        )

        self.c.raw_driver.commit()

    def test_submit_sets_developer(self):
        self.c.submit(test)

        dev = self.c.get_var('test', '__developer__')

        self.assertEqual(dev, 'stu')

    def test_change_developer_if_developer_works(self):
        self.c.submit(test)

        submission = self.c.get_contract('submission')

        submission.change_developer(contract='test', new_developer='not_stu')

        dev = self.c.get_var('test', '__developer__')

        self.assertEqual(dev, 'not_stu')

    def test_change_developer_prevents_new_change(self):
        self.c.submit(test)

        submission = self.c.get_contract('submission')

        submission.change_developer(contract='test', new_developer='not_stu')

        with self.assertRaises(AssertionError):
            submission.change_developer(contract='test',
                                        new_developer='woohoo')

    def test_cannot_import_submission(self):
        self.c.submit(import_submission)

        imp_con = self.c.get_contract('import_submission')

        with self.assertRaises(AssertionError):
            imp_con.haha()
Exemple #3
0
    def distribute_rewards(master_reward, delegate_reward, foundation_reward,
                           developer_mapping, client: ContractingClient):
        stamp_cost = client.get_var(contract='stamp_cost',
                                    variable='S',
                                    arguments=['value'])

        master_reward /= stamp_cost
        delegate_reward /= stamp_cost
        foundation_reward /= stamp_cost

        log.info(
            f'Master reward: {format(master_reward, ".4f")}t per master. '
            f'Delegate reward: {format(delegate_reward, ".4f")}t per delegate. '
            f'Foundation reward: {format(foundation_reward, ".4f")}t.')

        for m in client.get_var(contract='masternodes',
                                variable='S',
                                arguments=['members']):
            RewardManager.add_to_balance(vk=m,
                                         amount=master_reward,
                                         client=client)

        for d in client.get_var(contract='delegates',
                                variable='S',
                                arguments=['members']):
            RewardManager.add_to_balance(vk=d,
                                         amount=delegate_reward,
                                         client=client)

        foundation_wallet = client.get_var(contract='foundation',
                                           variable='owner')
        RewardManager.add_to_balance(vk=foundation_wallet,
                                     amount=foundation_reward,
                                     client=client)

        # Send rewards to each developer calculated from the block
        for recipient, amount in developer_mapping.items():
            dev_reward = round((amount / stamp_cost), DUST_EXPONENT)
            RewardManager.add_to_balance(vk=recipient,
                                         amount=dev_reward,
                                         client=client)

        log.info(f'Remainder is burned.')
Exemple #4
0
def register_policies(client: ContractingClient):
    # add to election house
    election_house = client.get_contract('election_house')

    policies_to_register = [
        'masternodes', 'delegates', 'rewards', 'stamp_cost'
    ]

    for policy in policies_to_register:
        if client.get_var(contract='election_house',
                          variable='policies',
                          arguments=[policy]) is None:
            election_house.register_policy(contract=policy)
Exemple #5
0
    def add_to_balance(vk, amount, client: ContractingClient):
        current_balance = client.get_var(contract='currency',
                                         variable='balances',
                                         arguments=[vk],
                                         mark=False)

        if current_balance is None:
            current_balance = ContractingDecimal(0)

        amount = ContractingDecimal(amount)

        client.set_var(contract='currency',
                       variable='balances',
                       arguments=[vk],
                       value=amount + current_balance,
                       mark=True)
Exemple #6
0
    def create_to_send_map(block, developer_ratio, client: ContractingClient):
        # Find all transactions and the developer of the contract.
        # Count all stamps used by people and multiply it by the developer ratio
        send_map = defaultdict(lambda: 0)

        for sb in block['subblocks']:
            for tx in sb['transactions']:
                contract = tx['transaction']['payload']['contract']

                recipient = client.get_var(contract=contract,
                                           variable='__developer__')

                send_map[recipient] += (tx['stamps_used'] * developer_ratio)

        for developer in send_map.keys():
            send_map[developer] /= len(send_map)

        return send_map
Exemple #7
0
class TestRewards(TestCase):
    def setUp(self):
        self.client = ContractingClient()
        self.rewards = rewards.RewardManager()

    def tearDown(self):
        self.client.flush()

    def sync(self):
        sync.setup_genesis_contracts(['stu', 'raghu', 'steve'],
                                     ['tejas', 'alex2'],
                                     client=self.client)

    def test_contract_exists_false_before_sync(self):
        self.assertFalse(
            self.rewards.contract_exists('stamp_cost', self.client))

    def test_contract_exists_true_after_sync(self):
        # Sync contracts
        self.sync()
        self.assertTrue(self.rewards.contract_exists('stamp_cost',
                                                     self.client))

    def test_is_setup_false_before_sync(self):
        self.assertFalse(self.rewards.is_setup(self.client))

    def test_is_setup_true_after_sync(self):
        self.sync()
        self.assertTrue(self.rewards.is_setup(self.client))

    def test_add_to_balance_if_none_sets(self):
        self.rewards.add_to_balance('stu', 123, self.client)
        bal = self.client.get_var('currency',
                                  variable='balances',
                                  arguments=['stu'])
        self.assertEqual(bal, 123)

    def test_add_to_balance_twice_sets_accordingly(self):
        self.rewards.add_to_balance('stu', 123, self.client)
        bal = self.client.get_var('currency',
                                  variable='balances',
                                  arguments=['stu'])
        self.assertEqual(bal, 123)

        self.rewards.add_to_balance('stu', 123, self.client)
        bal = self.client.get_var('currency',
                                  variable='balances',
                                  arguments=['stu'])
        self.assertEqual(bal, 246)

    def test_calculate_rewards_returns_accurate_amounts_per_participant_group(
            self):
        self.sync()
        self.client.set_var(contract='rewards',
                            variable='S',
                            arguments=['value'],
                            value=[0.4, 0.3, 0.1, 0.1, 0.1])

        m, d, f, mapping = self.rewards.calculate_all_rewards(
            client=self.client, block=BLOCK)

        reconstructed = (m * 3) + (d * 2) + (f * 1) + (f * 1) + (f * 1)

        self.assertAlmostEqual(reconstructed,
                               self.rewards.stamps_in_block(BLOCK))

    def test_calculate_participant_reward_shaves_off_dust(self):
        rounded_reward = self.rewards.calculate_participant_reward(
            participant_ratio=1,
            number_of_participants=1,
            total_stamps_to_split=1.0000000000001)

        self.assertEqual(rounded_reward, 1)

    def test_distribute_rewards_adds_to_all_wallets(self):
        self.sync()
        self.client.set_var(contract='rewards',
                            variable='S',
                            arguments=['value'],
                            value=[0.4, 0.3, 0.1, 0.1, 0.1])
        self.client.set_var(contract='foundation',
                            variable='owner',
                            value='xxx')

        self.client.set_var(contract='stamp_cost',
                            variable='S',
                            arguments=['value'],
                            value=100)

        self.client.set_var(contract='thing_1',
                            variable='__developer__',
                            value='stu2')

        self.client.set_var(contract='thing_2',
                            variable='__developer__',
                            value='jeff')

        self.client.set_var(contract='thing_3',
                            variable='__developer__',
                            value='alex')

        total_tau_to_split = 4900

        m, d, f, mapping = self.rewards.calculate_all_rewards(
            client=self.client, block=BLOCK)

        self.rewards.distribute_rewards(m, d, f, mapping, client=self.client)

        masters = self.client.get_var(contract='masternodes',
                                      variable='S',
                                      arguments=['members'])
        delegates = self.client.get_var(contract='delegates',
                                        variable='S',
                                        arguments=['members'])

        for mn in masters:
            current_balance = self.client.get_var(contract='currency',
                                                  variable='balances',
                                                  arguments=[mn],
                                                  mark=False)
            self.assertEqual(current_balance, m / 100)

        for dl in delegates:
            current_balance = self.client.get_var(contract='currency',
                                                  variable='balances',
                                                  arguments=[dl],
                                                  mark=False)
            self.assertEqual(current_balance, d / 100)

        current_balance = self.client.get_var(contract='currency',
                                              variable='balances',
                                              arguments=['xxx'],
                                              mark=False)
        self.assertEqual(current_balance, f / 100)

    def test_stamps_in_block(self):
        block = {
            'number':
            2,
            'subblocks': [{
                'transactions': [{
                    'stamps_used': 1000
                }, {
                    'stamps_used': 2000
                }, {
                    'stamps_used': 3000
                }]
            }, {
                'transactions': [{
                    'stamps_used': 4500
                }, {
                    'stamps_used': 1250
                }, {
                    'stamps_used': 2750
                }]
            }]
        }

        self.assertEqual(self.rewards.stamps_in_block(block), 14500)

    def test_issue_rewards_full_loop_works(self):
        self.sync()
        self.client.set_var(contract='rewards',
                            variable='S',
                            arguments=['value'],
                            value=[0.4, 0.3, 0.1, 0.1, 0.1])
        self.client.set_var(contract='foundation',
                            variable='owner',
                            value='xxx')
        self.client.set_var(contract='stamp_cost',
                            variable='S',
                            arguments=['value'],
                            value=100)

        self.client.set_var(contract='thing_1',
                            variable='__developer__',
                            value='stu2')

        self.client.set_var(contract='thing_2',
                            variable='__developer__',
                            value='jeff')

        self.client.set_var(contract='thing_3',
                            variable='__developer__',
                            value='alex')

        block = {
            'number':
            1,
            'subblocks': [{
                'transactions': [{
                    'stamps_used': 1000,
                    'transaction': {
                        'payload': {
                            'contract': 'thing_1'
                        }
                    }
                }, {
                    'stamps_used': 2000,
                    'transaction': {
                        'payload': {
                            'contract': 'thing_2'
                        }
                    }
                }, {
                    'stamps_used': 3000,
                    'transaction': {
                        'payload': {
                            'contract': 'thing_3'
                        }
                    }
                }]
            }, {
                'transactions': [{
                    'stamps_used': 4500,
                    'transaction': {
                        'payload': {
                            'contract': 'thing_1'
                        }
                    }
                }, {
                    'stamps_used': 1250,
                    'transaction': {
                        'payload': {
                            'contract': 'thing_1'
                        }
                    }
                }, {
                    'stamps_used': 2750,
                    'transaction': {
                        'payload': {
                            'contract': 'thing_2'
                        }
                    }
                }]
            }]
        }

        # tau to distribute should be 145

        stamps = self.rewards.stamps_in_block(block)

        tau = stamps / 100

        self.assertEqual(tau, 145)

        self.rewards.issue_rewards(block, client=self.client)

        # Stu is owed: 6750 stamps / 100 / 3 =
        # Jeff is owed: 4750 stamps / 100 / 3= 47.5
        # Alex is owed:

        m, d, f, mapping = self.rewards.calculate_all_rewards(
            client=self.client, block=block)

        masters = self.client.get_var(contract='masternodes',
                                      variable='S',
                                      arguments=['members'])
        delegates = self.client.get_var(contract='delegates',
                                        variable='S',
                                        arguments=['members'])

        for mn in masters:
            current_balance = self.client.get_var(contract='currency',
                                                  variable='balances',
                                                  arguments=[mn],
                                                  mark=False)
            self.assertEqual(current_balance, m / 100)

        for dl in delegates:
            current_balance = self.client.get_var(contract='currency',
                                                  variable='balances',
                                                  arguments=[dl],
                                                  mark=False)
            self.assertEqual(current_balance, d / 100)

        current_balance = self.client.get_var(contract='currency',
                                              variable='balances',
                                              arguments=['xxx'],
                                              mark=False)
        self.assertEqual(current_balance, f / 100)

        for dev in mapping.keys():
            current_balance = self.client.get_var(contract='currency',
                                                  variable='balances',
                                                  arguments=[dev],
                                                  mark=False)

            self.assertAlmostEqual(current_balance, mapping[dev] / 100)
Exemple #8
0
class Node:
    def __init__(self,
                 socket_base,
                 ctx: zmq.asyncio.Context,
                 wallet,
                 constitution: dict,
                 bootnodes={},
                 blocks=storage.BlockStorage(),
                 driver=ContractDriver(),
                 debug=True,
                 store=False,
                 seed=None,
                 bypass_catchup=False,
                 node_type=None,
                 genesis_path=lamden.contracts.__path__[0],
                 reward_manager=rewards.RewardManager(),
                 nonces=storage.NonceStorage()):

        self.driver = driver
        self.nonces = nonces
        self.store = store

        self.seed = seed

        self.blocks = blocks
        self.event_writer = EventWriter()

        self.log = get_logger('Base')
        self.log.propagate = debug
        self.socket_base = socket_base
        self.wallet = wallet
        self.ctx = ctx

        self.genesis_path = genesis_path

        self.client = ContractingClient(driver=self.driver,
                                        submission_filename=genesis_path +
                                        '/submission.s.py')

        self.bootnodes = bootnodes
        self.constitution = constitution

        self.seed_genesis_contracts()

        self.socket_authenticator = authentication.SocketAuthenticator(
            bootnodes=self.bootnodes, ctx=self.ctx, client=self.client)

        self.upgrade_manager = upgrade.UpgradeManager(client=self.client,
                                                      wallet=self.wallet,
                                                      node_type=node_type)

        self.router = router.Router(socket_id=socket_base,
                                    ctx=self.ctx,
                                    wallet=wallet,
                                    secure=True)

        self.network = network.Network(wallet=wallet,
                                       ip_string=socket_base,
                                       ctx=self.ctx,
                                       router=self.router)

        self.new_block_processor = NewBlock(driver=self.driver)
        self.router.add_service(
            NEW_BLOCK_SERVICE,
            self.new_block_processor)  # Add this after catch up?

        self.running = False
        self.upgrade = False

        self.reward_manager = reward_manager

        self.current_height = storage.get_latest_block_height(self.driver)
        self.current_hash = storage.get_latest_block_hash(self.driver)

        self.bypass_catchup = bypass_catchup

    def seed_genesis_contracts(self):
        self.log.info('Setting up genesis contracts.')
        sync.setup_genesis_contracts(
            initial_masternodes=self.constitution['masternodes'],
            initial_delegates=self.constitution['delegates'],
            client=self.client,
            filename=self.genesis_path + '/genesis.json',
            root=self.genesis_path)

    async def catchup(self, mn_seed, mn_vk):
        # Get the current latest block stored and the latest block of the network
        self.log.info('Running catchup.')
        current = self.current_height
        latest = await get_latest_block_height(ip=mn_seed,
                                               vk=mn_vk,
                                               wallet=self.wallet,
                                               ctx=self.ctx)

        self.log.info(
            f'Current block: {current}, Latest available block: {latest}')

        if latest == 0 or latest is None or type(latest) == dict:
            self.log.info('No need to catchup. Proceeding.')
            return

        # Increment current by one. Don't count the genesis block.
        if current == 0:
            current = 1

        # Find the missing blocks process them
        for i in range(current, latest + 1):
            block = None
            while block is None:
                block = await get_block(block_num=i,
                                        ip=mn_seed,
                                        vk=mn_vk,
                                        wallet=self.wallet,
                                        ctx=self.ctx)
            self.process_new_block(block)

        # Process any blocks that were made while we were catching up
        while len(self.new_block_processor.q) > 0:
            block = self.new_block_processor.q.pop(0)
            self.process_new_block(block)

    def should_process(self, block):
        try:
            self.log.info(f'Processing block #{block.get("number")}')
        except:
            self.log.error('Malformed block :(')
            return False
        # Test if block failed immediately
        if block == {'response': 'ok'}:
            return False

        if block['hash'] == 'f' * 64:
            self.log.error('Failed Block! Not storing.')
            return False

        # Get current metastate
        # if len(block['subblocks']) < 1:
        #    return False

        # Test if block contains the same metastate
        # if block['number'] != self.current_height + 1:
        #     self.log.info(f'Block #{block["number"]} != {self.current_height + 1}. '
        #                   f'Node has probably already processed this block. Continuing.')
        #     return False

        # if block['previous'] != self.current_hash:
        #     self.log.error('Previous block hash != Current hash. Cryptographically invalid. Not storing.')
        #     return False

        # If so, use metastate and subblocks to create the 'expected' block
        # expected_block = canonical.block_from_subblocks(
        #     subblocks=block['subblocks'],
        #     previous_hash=self.current_hash,
        #     block_num=self.current_height + 1
        # )

        # Return if the block contains the expected information
        # good = block == expected_block
        # if good:
        #     self.log.info(f'Block #{block["number"]} passed all checks. Store.')
        # else:
        #     self.log.error(f'Block #{block["number"]} has an encoding problem. Do not store.')
        #
        # return good

        return True

    def update_state(self, block):
        self.driver.clear_pending_state()

        # Check if the block is valid
        if self.should_process(block):
            self.log.info('Storing new block.')
            # Commit the state changes and nonces to the database
            storage.update_state_with_block(block=block,
                                            driver=self.driver,
                                            nonces=self.nonces)

            self.log.info('Issuing rewards.')
            # Calculate and issue the rewards for the governance nodes
            self.reward_manager.issue_rewards(block=block, client=self.client)

        #self.nonces.flush_pending()

        self.log.info('Updating metadata.')
        self.current_height = storage.get_latest_block_height(self.driver)
        self.current_hash = storage.get_latest_block_hash(self.driver)

        self.new_block_processor.clean(self.current_height)

    def process_new_block(self, block):
        # Update the state and refresh the sockets so new nodes can join
        self.update_state(block)
        self.socket_authenticator.refresh_governance_sockets()

        # Store the block if it's a masternode
        if self.store:
            encoded_block = encode(block)
            encoded_block = json.loads(encoded_block)

            self.blocks.store_block(encoded_block)

            # create Event File
            self.event_writer.write_event(
                Event(topics=[NEW_BLOCK_EVENT], data=encoded_block))

        # Prepare for the next block by flushing out driver and notification state
        # self.new_block_processor.clean()

        # Finally, check and initiate an upgrade if one needs to be done
        self.driver.commit()

        self.driver.clear_pending_state()
        gc.collect()  # Force memory cleanup every block

        self.nonces.flush_pending()

    async def start(self):
        asyncio.ensure_future(self.router.serve())

        # Get the set of VKs we are looking for from the constitution argument
        vks = self.constitution['masternodes'] + self.constitution['delegates']

        for node in self.bootnodes.keys():
            self.socket_authenticator.add_verifying_key(node)

        self.socket_authenticator.configure()

        # Use it to boot up the network
        await self.network.start(bootnodes=self.bootnodes, vks=vks)

        if not self.bypass_catchup:
            masternode_ip = None
            masternode = None

            if self.seed is not None:
                for k, v in self.bootnodes.items():
                    self.log.info(k, v)
                    if v == self.seed:
                        masternode = k
                        masternode_ip = v
            else:
                masternode = self.constitution['masternodes'][0]
                masternode_ip = self.network.peers[masternode]

            self.log.info(f'Masternode Seed VK: {masternode}')

            # Use this IP to request any missed blocks
            await self.catchup(mn_seed=masternode_ip, mn_vk=masternode)

        # Refresh the sockets to accept new nodes
        self.socket_authenticator.refresh_governance_sockets()

        # Start running
        self.running = True

    def stop(self):
        # Kill the router and throw the running flag to stop the loop
        self.router.stop()
        self.running = False

    def _get_member_peers(self, contract_name):
        members = self.client.get_var(contract=contract_name,
                                      variable='S',
                                      arguments=['members'])

        member_peers = dict()

        for member in members:
            ip = self.network.peers.get(member)
            if ip is not None:
                member_peers[member] = ip

        return member_peers

    def get_delegate_peers(self):
        return self._get_member_peers('delegates')

    def get_masternode_peers(self):
        return self._get_member_peers('masternodes')

    def make_constitution(self):
        return {
            'masternodes': self.get_masternode_peers(),
            'delegates': self.get_delegate_peers()
        }
Exemple #9
0
def transaction_is_valid(transaction,
                         expected_processor,
                         client: ContractingClient,
                         nonces: storage.NonceStorage,
                         strict=True,
                         tx_per_block=15,
                         timeout=5):
    # Check basic formatting so we can access via __getitem__ notation without errors
    if not check_format(transaction, rules.TRANSACTION_RULES):
        return TransactionFormattingError

    transaction_is_not_expired(transaction, timeout)

    # Put in to variables for visual ease
    processor = transaction['payload']['processor']
    sender = transaction['payload']['sender']

    # Checks if correct processor and if signature is valid
    check_tx_formatting(transaction, expected_processor)

    # Gets the expected nonces
    nonce, pending_nonce = get_nonces(sender, processor, nonces)

    # Get the provided nonce
    tx_nonce = transaction['payload']['nonce']

    # Check to see if the provided nonce is valid to what we expect and
    # if there are less than the max pending txs in the block
    get_new_pending_nonce(tx_nonce,
                          nonce,
                          pending_nonce,
                          strict=strict,
                          tx_per_block=tx_per_block)

    # Get the senders balance and the current stamp rate
    balance = client.get_var(contract='currency',
                             variable='balances',
                             arguments=[sender],
                             mark=False)
    stamp_rate = client.get_var(contract='stamp_cost',
                                variable='S',
                                arguments=['value'],
                                mark=False)

    contract = transaction['payload']['contract']
    func = transaction['payload']['function']
    stamps_supplied = transaction['payload']['stamps_supplied']
    if stamps_supplied is None:
        stamps_supplied = 0

    if stamp_rate is None:
        stamp_rate = 0

    if balance is None:
        balance = 0

    # Get how much they are sending
    amount = transaction['payload']['kwargs'].get('amount')
    if amount is None:
        amount = 0

    # Check if they have enough stamps for the operation
    has_enough_stamps(balance,
                      stamp_rate,
                      stamps_supplied,
                      contract=contract,
                      function=func,
                      amount=amount)

    # Check if contract name is valid
    name = transaction['payload']['kwargs'].get('name')
    contract_name_is_valid(contract, func, name)