Ejemplo n.º 1
0
    def __init__(self, ip: str, signing_key: str, name='', *args, **kwargs):
        """
        IMPORTANT: This should not be overridden by subclasses. Instead, override the setup() method.

        Creates a Worker instance and starts the event loop. Instantiating this class blocks indefinitely, thus any
        setup must be done by overriding the setup() method (see comments below for explanation)
        :param args: This should never be set, as only kwargs are supported.
        :param kwargs: A list of named variables that will be set as instance attributes.
        """
        assert len(args) == 0, "Worker cannot be constructed with args. Only key word args are supported."

        self.name = name or type(self).__name__
        self.signing_key = signing_key
        self.ip = ip
        self.verifying_key = wallet.get_vk(self.signing_key)
        self.log = get_logger(name)

        # We set all kwargs to instance variables so they are accessible in the setup() function. Setup cannot be done
        # in subclasses by overriding __init__ because instantiating this instance involves running an event loop
        # forever (which would block the setup code upon calling 'super().__init__(..)' in the subclass)
        # TODO this pattern is questionable. Perhaps args/kwargs should be passed into setup(...)?  --davis
        for k, v in kwargs.items():
            self.log.important("setting k {} to v {}".format(k, v))
            setattr(self, k, v)

        self._router = Router(get_handler_func=lambda: self, name=name)
        self._manager = ExecutorManager(signing_key=signing_key, router=self._router, name=name)
        self.composer = Composer(manager=self._manager, signing_key=signing_key, ip=ip, name=name)
        self._router.composer = self.composer

        self.setup()

        self.log.notice("Starting Worker named {}".format(name))
        self._manager.start()  # This starts the event loop and blocks this process indefinitely
Ejemplo n.º 2
0
    def create_from_message(cls, message: MessageBase, signing_key: str, verifying_key: str=None, uuid: int=-1):
        """
        Creates an Envelope to package a MessageBase instance

        :param message: The MessageBase instance to create an envelope for
        :param signing_key: The sender's signing key, which is used to create the Seal.
        :param verifying_key: The sender's verifying key. This should be passed in for computational efficiency, but
        can be computed from the signing key if it is ommited
        :param uuid: The UUID to use for the Envelope's MessageMeta. If -1, a random UUID will be generated.
        :return: An Envelope instance
        """
        assert issubclass(type(message), MessageBase), "message arg must be a MessageBase subclass"
        assert type(message) in MessageBase.registry, "Message type {} not found in registry {}"\
            .format(type(message), MessageBase.registry)
        # TODO -- verify sk (valid hex, 128 char)

        # Create MessageMeta
        t = MessageBase.registry[type(message)]
        timestamp = str(time.time())
        meta = MessageMeta.create(type=t, timestamp=timestamp, uuid=uuid)

        # Create Seal
        if not verifying_key:
            verifying_key = wallet.get_vk(signing_key)
        seal_sig = EnvelopeAuth.seal(signing_key=signing_key, meta=meta, message=message)
        seal = Seal.create(signature=seal_sig, verifying_key=verifying_key)

        # Create Envelope
        obj = cls._create_from_objects(seal=seal, meta=meta, message=message.serialize())
        set_lazy_property(obj, 'message', message)

        return obj
Ejemplo n.º 3
0
def build_valid_block_data(num_transactions=4) -> dict:
    """
    Utility method to build a dictionary with all the params needed to invoke store_block
    :param num_transactions:
    :return:
    """
    mn_sk = masternodes[0]['sk']
    mn_vk = wallet.get_vk(mn_sk)
    timestamp = 9000

    raw_transactions = [
        build_test_transaction().serialize() for _ in range(num_transactions)
    ]

    tree = MerkleTree(raw_transactions)
    merkle_leaves = tree.leaves_as_concat_hex_str
    merkle_root = tree.root_as_hex

    bc = build_test_contender(tree=tree)

    prev_block_hash = '0' * 64

    mn_sig = wallet.sign(mn_sk, tree.root)

    return {
        'prev_block_hash': prev_block_hash,
        'block_contender': bc,
        'merkle_leaves': merkle_leaves,
        'merkle_root': merkle_root,
        'masternode_signature': mn_sig,
        'masternode_vk': mn_vk,
        'timestamp': timestamp
    }
Ejemplo n.º 4
0
    def __init__(self, ip, signing_key, loop, name='Node'):
        super().__init__()

        self.log = get_logger(name)
        self.ip = ip
        self.name = name

        self.signing_key = signing_key
        self.verifying_key = wallet.get_vk(self.signing_key)

        # DEBUG
        import os
        self.log.important3("Node with vk {} has ip {}".format(
            self.verifying_key, os.getenv("HOST_IP")))
        # END DEBUG

        self.loop = loop
        asyncio.set_event_loop(loop)

        self.log.notice("Starting overlay service")
        self.overlay_proc = Process(target=OverlayInterface.start_service,
                                    args=(signing_key, ))
        self.overlay_proc.start()

        self._composer = None

        self.tasks = []
Ejemplo n.º 5
0
def run_mgmt():
    from cilantro.logger import get_logger
    from cilantro.utils.test import MPComposer

    log = get_logger(__name__)
    sk = masternodes[0]['sk']
    vk = wallet.get_vk(sk)
    s, v = wallet.new()
    mpc = MPComposer(name='mgmt', sk=s)
    mpc.add_sub(filter='a', vk=vk)
Ejemplo n.º 6
0
    def __init__(self, signing_key, witness_list, url, sbb_index):
        self.log = get_logger("SubBlockBuilder_{}".format(sb_index))
        # Comment out below for more granularity in debugging
        # self.log.setLevel(logging.INFO)

        #self.log.important("SubBlockBuilder started with url {}".format(url))

        # Register signal handler to teardown
        signal.signal(signal.SIGTERM, self._signal_teardown)

        # need to revisit this when threading strategy is clear
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)

        self.signing_key = signing_key
        # witness_list should be comma separated list of ip:vk  
        self.witness_table = self._parse_witness_list(witness_list)
        self.url = url
        self.sbb_index = sbb_index
        self.block_num = (int) sbb_index / 16       # hard code this for now
        self.sub_block_num = (int) sb_index % 16
        self.num_txs = 0
        self.num_sub_blocks = 0
        self.tasks = []

        #SenecaInterpreter connect with BlockManager (parent process that spawned this one)
        self.context = zmq.asyncio.Context()
        self.socket = self.context.socket(zmq.PAIR)  # For communication with main process
        self.socket.connect(self.url)

        # do we need this still? or do we move it to a util methods
        self.verifying_key = wallet.get_vk(self.signing_key)
        skg = SigningKey(seed=bytes.fromhex(sk))
        self.vk = skg.verify_key.encode().hex()
        self.public_key = self.vk2pk(self.vk)
        self.private_key = crypto_sign_ed25519_sk_to_curve25519(skg._signing_key).hex()
        priv = PrivateKey(bytes.fromhex(self.private_key))
        publ = priv.public_key
        self.public_key = public_key = encode(publ._public_key)
        self.secret = secret_key = encode(priv._private_key)

        self.pending_txs = LinkedHashTable()
        self.interpreter = SenecaInterpreter()
        self._recently_seen = CappedSet(max_size=DUPE_TABLE_SIZE)

        try:
            self._subscribe_to_witnesses()
            # start event loop and start listening witness sockets as well as mgr
            self.run_loop_second_time()
        except Exception as e:
            err_msg = '\n' + '!' * 64 + '\nSBB terminating with exception:\n' + str(traceback.format_exc())
            err_msg += '\n' + '!' * 64 + '\n'
            self.log.error(err_msg)
        finally:
            self._teardown()
Ejemplo n.º 7
0
    def create_contract_tx(sender_sk: str, code_str: str):
        validate_hex(sender_sk, 64, 'sender signing key')

        struct = transaction_capnp.ContractTransaction.new_message()

        struct.payload.sender = wallet.get_vk(sender_sk)
        struct.payload.code = code_str

        payload_binary = struct.payload.copy().to_bytes()

        struct.metadata.proof = SHA3POW.find(payload_binary)[0]
        struct.metadata.signature = wallet.sign(sender_sk, payload_binary)

        return ContractTransaction.from_data(struct)
Ejemplo n.º 8
0
    def __init__(self, ip, signing_key, loop, name='Node'):
        super().__init__()

        self.log = get_logger(name)
        self.ip = ip
        self.name = name

        self.signing_key = signing_key
        self.verifying_key = wallet.get_vk(self.signing_key)

        self.loop = loop
        asyncio.set_event_loop(loop)

        self._composer = None

        self.tasks = []
Ejemplo n.º 9
0
    def _build_block_data(self,
                          num_transactions=4,
                          ref_prev_block=False) -> dict:
        """
        Utility method to build a dictionary with all the params needed to invoke store_block
        :param num_transactions: Number of raw transactions in the block
        :param ref_prev_block: True if the block data's prev_block_hash should reference the previous block.
        Otherwise, prev_block_hash is set to default 000000...
        :return:
        """
        mn_sk = masternodes[0]['sk']
        mn_vk = wallet.get_vk(mn_sk)
        timestamp = 9000

        raw_transactions = [
            build_test_transaction().serialize()
            for _ in range(num_transactions)
        ]

        tree = MerkleTree(raw_transactions)
        merkle_leaves = tree.leaves_as_concat_hex_str
        merkle_root = tree.root_as_hex

        bc = build_test_contender(tree=tree)

        if ref_prev_block:
            prev_block_hash = BlockStorageDriver.get_latest_block_hash()
        else:
            prev_block_hash = '0' * 64

        mn_sig = wallet.sign(mn_sk, tree.root)

        return {
            'prev_block_hash': prev_block_hash,
            'block_contender': bc,
            'merkle_leaves': merkle_leaves,
            'merkle_root': merkle_root,
            'masternode_signature': mn_sig,
            'masternode_vk': mn_vk,
            'timestamp': timestamp
        }
Ejemplo n.º 10
0
    def __init__(self,
                 manager: ExecutorManager,
                 signing_key: str,
                 ip,
                 name='Node'):
        super().__init__()
        self.log = get_logger("{}.Composer".format(name))
        self.manager = manager
        self.ip = ip
        self.signing_key = signing_key
        self.verifying_key = wallet.get_vk(self.signing_key)

        self.overlay_fut = asyncio.ensure_future(
            OverlayInterface.event_listener(self._handle_overlay_event))
        self.command_queue = {}  # dict of UUID to kwargs
        self.pending_commands = deque(
        )  # To hold commands until the event loop is started

        # The 'future' task to flush pending_commands when event loop starts
        self.flush_pending_fut = asyncio.ensure_future(
            self._flush_pending_commands())
Ejemplo n.º 11
0
    def store_block(cls,
                    block_contender: BlockContender,
                    raw_transactions: List[bytes],
                    publisher_sk: str,
                    timestamp: int = 0) -> str:
        """
        Persist a new block to the blockchain, along with the raw transactions associated with the block. An exception
        will be raised if an error occurs either validating the new block data, or storing the block. Thus, it is
        recommended that this method is wrapped in a try block. If the block was successfully stored, this method will
        return the hash of the stored block.

        :param block_contender: A BlockContender instance
        :param raw_transactions: A list of ordered raw transactions contained in the block
        :param publisher_sk: The signing key of the publisher (a Masternode) who is publishing the block
        :param timestamp: The time the block was published, in unix epoch time. If 0, time.time() is used
        :return: The hash of the stored block
        :raises: An assertion error if invalid args are passed into this function, or a BlockStorageValidationException
         if validation fails on the attempted block

        TODO -- think really hard and make sure that this is 'collision proof' (extremely unlikely, but still possible)
        - could there be a hash collision in the Merkle tree nodes?
        - hash collision in block hash space?
        - hash collision in transaction space?
        """
        assert isinstance(
            block_contender, BlockContender
        ), "Expected block_contender arg to be BlockContender instance"
        assert is_valid_hex(
            publisher_sk,
            64), "Invalid signing key {}. Expected 64 char hex str".format(
                publisher_sk)

        if not timestamp:
            timestamp = int(time.time())

        tree = MerkleTree.from_raw_transactions(raw_transactions)

        publisher_vk = wallet.get_vk(publisher_sk)
        publisher_sig = wallet.sign(publisher_sk, tree.root)

        # Build and validate block_data
        block_data = {
            'block_contender': block_contender,
            'timestamp': timestamp,
            'merkle_root': tree.root_as_hex,
            'merkle_leaves': tree.leaves_as_concat_hex_str,
            'prev_block_hash': cls.get_latest_block_hash(),
            'masternode_signature': publisher_sig,
            'masternode_vk': publisher_vk,
        }
        cls.validate_block_data(block_data)

        # Compute block hash
        block_hash = cls.compute_block_hash(block_data)

        # Encode block data for serialization
        log.info(
            "Attempting to persist new block with hash {}".format(block_hash))
        block_data = cls._encode_block(block_data)

        # Finally, persist the data
        with DB() as db:
            # Store block
            res = db.tables.blocks.insert([{
                'hash': block_hash,
                **block_data
            }]).run(db.ex)
            if res:
                log.success2(
                    "Successfully inserted new block with number {} and hash {}"
                    .format(res['last_row_id'], block_hash))
            else:
                raise BlockStorageDatabaseException(
                    "Error inserting block! Got None/False result back "
                    "from insert query. Result={}".format(res))

            # Store raw transactions
            log.info(
                "Attempting to store {} raw transactions associated with block hash {}"
                .format(len(raw_transactions), block_hash))
            tx_rows = [{
                'hash': Hasher.hash(raw_tx),
                'data': encode_tx(raw_tx),
                'block_hash': block_hash
            } for raw_tx in raw_transactions]

            res = db.tables.transactions.insert(tx_rows).run(db.ex)
            if res:
                log.info("Successfully inserted {} transactions".format(
                    res['row_count']))
            else:
                log.error(
                    "Error inserting raw transactions! Got None from insert query. Result={}"
                    .format(res))

            return block_hash
Ejemplo n.º 12
0
 def __init__(self, interface: ReactorInterface, signing_key: str, name='Node'):
     super().__init__()
     self.log = get_logger("{}.Composer".format(name))
     self.interface = interface
     self.signing_key = signing_key
     self.verifying_key = wallet.get_vk(self.signing_key)