def from_dict(obj: Ser) -> 'Transaction': inputs = map(lambda ti: TransactionInput.from_dict(ti), obj["inputs"]) outputs = map(lambda ti: TransactionOutput.from_dict(ti), obj["outputs"]) return Transaction(list(inputs), list(outputs), Timestamp.from_dict(obj["timestamp"]), Address.from_dict(obj["claimer"]))
def fetch_submissions(self, fetcher: Optional[Fetcher] = None, since: int = Timestamp.now() - 10 * day, limit: int = 1000) -> List[Submission]: """Fetches all submissions (up to 1000) since the given UTC UNIX timestamp, using the provided fetcher. If no argument is passed for 'fetcher' (or it is None), this method will request the newest posts from r/nearprog. """ def fetch(extended_limit: int) -> List: # if None is provided, use the default (live) Submission fetcher default_fetcher = Fetcher( lambda n: map(lambda s: Submission.wrap(s), self.nearprog().new(limit=n))) submissions = list( (fetcher or default_fetcher).fetch(since, extended_limit)) earliest = min(map(lambda s: s.timestamp, submissions)) # if we're limited only by our own self-imposed max # submissions, try again, but request more if (earliest > since and len(submissions) == extended_limit): return fetch(extended_limit * 2) else: # return the oldest submissions first (at the head of the list) return sorted(submissions, key=lambda x: x.timestamp)[:limit] return fetch(limit)
def from_dict(obj: Ser) -> 'HashedBlock': block = Block.from_dict(obj["block"]) mining_entropy = bytes.fromhex(obj["mining_entropy"]) mining_timestamp = Timestamp.from_dict(obj["mining_timestamp"]) hb = HashedBlock(block, mining_entropy=mining_entropy, mining_timestamp=mining_timestamp) return hb
def __init__(self, block: Block, mining_entropy: bytes = b"", mining_timestamp: Optional[Timestamp] = None) -> None: self.block = block self.mining_entropy = mining_entropy if mining_timestamp is None: self.mining_timestamp = Timestamp.now() else: self.mining_timestamp = mining_timestamp
def genesis() -> 'HashedBlock': """ The genesis is a block with num=0, no parent hash, a difficulty of 0, no transactions, no mining entropy and a mining time at the Unix epoch. """ b = Block(0, None, BlockConfig(0), []) return HashedBlock(b, mining_entropy=b"", mining_timestamp=Timestamp(0))
def start_transaction(self, accepted_trade): order = self.order_manager.order_repository.find_by_id(accepted_trade.order_id) if order: transaction = self.transaction_manager.create_from_accepted_trade(accepted_trade) start_transaction = StartTransaction(self.order_book.message_repository.next_identity(), transaction.transaction_id, order.order_id, accepted_trade.recipient_order_id, accepted_trade.price, accepted_trade.quantity, Timestamp.now()) self.send_start_transaction(transaction, start_transaction)
def test_fetch_submissions_since(): """Test that Connection.fetch_submissions() only returns submissions since the given UTC UNIX timestamp.""" config = get_config() connection: Connection = Connection(config) # remove 'fetcher' argument to test this method directly with Reddit since = Timestamp.now() - 24 * hour submissions = connection.fetch_submissions(fetcher=Fetcher( Fixture.fetcher), since=since) assert submissions[0].timestamp >= since
def test_fetch_submissions_ordering(): """Test that Connection.fetch_submissions() returns the Submissions sorted oldest -> newest.""" config = get_config() connection: Connection = Connection(config) # remove 'fetcher' argument to test this method directly with Reddit since = Timestamp.now() - 30 * day submissions = connection.fetch_submissions(fetcher=Fetcher( Fixture.fetcher), since=since) assert submissions[0].timestamp < submissions[1].timestamp assert submissions[1].timestamp < submissions[2].timestamp
def test_fetch_submissions_age(): """Test that Connection.fetch_submissions() returns the oldest-available submissions.""" config = get_config() connection: Connection = Connection(config) # remove 'fetcher' argument to test this method directly with Reddit old = connection.fetch_submissions(fetcher=Fetcher(Fixture.fetcher), since=0, limit=1) new = connection.fetch_submissions(fetcher=Fetcher(Fixture.fetcher), since=Timestamp.now() - week, limit=1) assert old[0].timestamp < new[0].timestamp
def on_counter_trade(self, messages): for message in messages: counter_trade = CounterTrade.from_network(message.payload) if str(counter_trade.recipient_order_id.trader_id) == str(self.pubkey): # The message is for this node order = self.order_manager.order_repository.find_by_id(counter_trade.recipient_order_id) if order: try: # Accept trade order.release_quantity_for_tick(counter_trade.order_id) self.accept_trade(order, counter_trade) except TickWasNotReserved: # Send cancel declined_trade = Trade.decline(self.order_book.message_repository.next_identity(), Timestamp.now(), counter_trade) self._logger.debug("Declined trade made with id: %s for counter trade with id: %s", str(declined_trade.message_id), str(counter_trade.message_id)) self.send_declined_trade(declined_trade)
def send_end_transaction(self, transaction): # Lookup the remote address of the peer with the pubkey self._logger.debug("Sending end transaction (quantity: %s)", transaction.total_quantity) candidate = Candidate(self.lookup_ip(transaction.partner_trader_id), False) message_id = self.order_book.message_repository.next_identity() meta = self.get_meta_message(u"end-transaction") message = meta.impl( authentication=(self.my_member,), distribution=(self.claim_global_time(),), destination=(candidate,), payload=( message_id.trader_id, message_id.message_number, transaction.transaction_id.trader_id, transaction.transaction_id.transaction_number, Timestamp.now(), ) ) self.dispersy.store_update_forward([message], True, False, True)
def on_proposed_trade(self, messages): for message in messages: proposed_trade = ProposedTrade.from_network(message.payload) self._logger.debug("Proposed trade received with id: %s", str(proposed_trade.message_id)) # Update the known IP address of the sender of this proposed trade self.update_ip(proposed_trade.message_id.trader_id, (message.payload.address.ip, message.payload.address.port)) if str(proposed_trade.recipient_order_id.trader_id) == str(self.pubkey): # The message is for this node order = self.order_manager.order_repository.find_by_id(proposed_trade.recipient_order_id) if order and order.is_valid() and order.available_quantity > Quantity(0): # Order is valid self._logger.debug("Proposed trade received with id: %s for order with id: %s", str(proposed_trade.message_id), str(order.order_id)) if order.available_quantity >= proposed_trade.quantity: # Enough quantity left self.accept_trade(order, proposed_trade) else: # Not enough quantity for trade counter_trade = Trade.counter(self.order_book.message_repository.next_identity(), order.available_quantity, Timestamp.now(), proposed_trade) order.reserve_quantity_for_tick(proposed_trade.order_id, order.available_quantity) self._logger.debug("Counter trade made with id: %s for proposed trade with id: %s", str(counter_trade.message_id), str(proposed_trade.message_id)) self.send_counter_trade(counter_trade) else: # Order invalid send cancel declined_trade = Trade.decline(self.order_book.message_repository.next_identity(), Timestamp.now(), proposed_trade) self._logger.debug("Declined trade made with id: %s for proposed trade with id: %s " "(valid? %s, available quantity of order: %s, reserved: %s, traded: %s)", str(declined_trade.message_id), str(proposed_trade.message_id), order.is_valid(), order.available_quantity, order.reserved_quantity, order.traded_quantity) self.send_declined_trade(declined_trade) else: self._logger.warning("Received proposed trade message that was not for this node " "(my id: %s, message recipient id: %s", str(self.pubkey), str(proposed_trade.recipient_order_id.trader_id))
def send_continue_transaction(self, transaction): assert isinstance(transaction, Transaction), type(transaction) # Lookup the remote address of the peer with the pubkey candidate = Candidate(self.lookup_ip(transaction.partner_trader_id), False) message_id = self.order_book.message_repository.next_identity() meta = self.get_meta_message(u"continue-transaction") message = meta.impl( authentication=(self.my_member,), distribution=(self.claim_global_time(),), destination=(candidate,), payload=( message_id.trader_id, message_id.message_number, transaction.transaction_id.trader_id, transaction.transaction_id.transaction_number, Timestamp.now(), ) ) self.dispersy.store_update_forward([message], True, False, True)
def replace_mining_entropy(self, new_entropy: bytes) -> None: self.mining_entropy = new_entropy self.mining_timestamp = Timestamp.now()
def accept_trade(self, order, proposed_trade): accepted_trade = Trade.accept(self.order_book.message_repository.next_identity(), Timestamp.now(), proposed_trade) self._logger.debug("Accepted trade made with id: %s for proposed/counter trade with id: %s (quantity: %s)", str(accepted_trade.message_id), str(proposed_trade.message_id), accepted_trade.quantity) self.check_history(accepted_trade) # Set the message received as true self.order_book.insert_trade(accepted_trade) order.add_trade(accepted_trade.recipient_order_id, accepted_trade.quantity) self.order_book.trade_tick(accepted_trade.order_id, accepted_trade.recipient_order_id, accepted_trade.quantity) self.send_accepted_trade(accepted_trade) self.start_transaction(accepted_trade)
def fetcher(limit: int) -> Iterator[Submission]: # assume 1 submission every ~6 hours on average end_time = int(Timestamp.now()) start_time = int(end_time - limit * 6 * hour) timestamps = sorted(random.sample(range(start_time, end_time), limit)) return iter(Submission(timestamp) for timestamp in timestamps)
def reward(amount: Amount, claimer: Address) -> 'Transaction': out = TransactionOutput(0, amount, claimer) return Transaction([], [out], Timestamp.now(), claimer)