def test_pkb_apply_transaction_on_coinbase(): public_key_0 = SECP256k1PublicKey(b'0' * 64) public_key_1 = SECP256k1PublicKey(b'1' * 64) output_0 = Output(40, public_key_0) output_1 = Output(34, public_key_1) unspent_transaction_outs = immutables.Map() public_key_balances = immutables.Map() transaction = Transaction(inputs=[ Input( construct_reference_to_thin_air(), CoinbaseData(0, b'coinbase of the first block'), ) ], outputs=[output_0, output_1]) result = pkb_apply_transaction(unspent_transaction_outs, public_key_balances, transaction, is_coinbase=True) assert public_key_0 in result assert public_key_1 in result assert result[public_key_0].value == 40 assert result[public_key_1].value == 34
def test_pkb_apply_transaction_on_non_coinbase_transaction(): public_key_0 = SECP256k1PublicKey(b'\x00' * 64) public_key_1 = SECP256k1PublicKey(b'\x01' * 64) public_key_2 = SECP256k1PublicKey(b'\x02' * 64) output_0 = Output(40, public_key_0) output_1 = Output(34, public_key_1) output_3 = Output(66, public_key_1) final_output = Output(30, public_key_2) previous_transaction_hash = b'a' * 32 unspent_transaction_outs = immutables.Map({ OutputReference(previous_transaction_hash, 0): output_0, OutputReference(previous_transaction_hash, 1): output_1, OutputReference(previous_transaction_hash, 2): output_3, }) public_key_balances = immutables.Map({ public_key_0: PKBalance(0, []), public_key_1: PKBalance(100, [ OutputReference(previous_transaction_hash, 1), OutputReference(previous_transaction_hash, 2), ]), }) transaction = Transaction(inputs=[ Input( OutputReference(previous_transaction_hash, 1), SECP256k1Signature(b'y' * 64), ) ], outputs=[final_output]) result = pkb_apply_transaction(unspent_transaction_outs, public_key_balances, transaction, is_coinbase=False) assert result[ public_key_0].value == 0 # not referenced in the transaction under consideration assert result[public_key_0].output_references == [] assert result[public_key_1].value == 100 - 34 assert result[public_key_1].output_references == [ OutputReference(previous_transaction_hash, 2) ] assert result[ public_key_2].value == 30 # the value of the transaction output assert result[public_key_2].output_references == [ OutputReference(transaction.hash(), 0) ]
def test_uto_apply_transaction_on_coinbase(): public_key = SECP256k1PublicKey(b'x' * 64) output_0 = Output(40, public_key) output_1 = Output(34, public_key) unspent_transaction_outs = immutables.Map() transaction = Transaction(inputs=[ Input( construct_reference_to_thin_air(), CoinbaseData(0, b'coinbase of the first block'), ) ], outputs=[output_0, output_1]) result = uto_apply_transaction(unspent_transaction_outs, transaction, is_coinbase=True) assert OutputReference(transaction.hash(), 0) in result assert OutputReference(transaction.hash(), 1) in result assert result[OutputReference(transaction.hash(), 0)] == output_0 assert result[OutputReference(transaction.hash(), 1)] == output_1
def test_uto_apply_transaction_on_non_coinbase_transaction(): public_key = SECP256k1PublicKey(b'x' * 64) output_0 = Output(40, public_key) output_1 = Output(34, public_key) output_2 = Output(30, public_key) previous_transaction_hash = b'a' * 32 unspent_transaction_outs = immutables.Map({ OutputReference(previous_transaction_hash, 0): output_0, OutputReference(previous_transaction_hash, 1): output_1, }) transaction = Transaction(inputs=[ Input( OutputReference(previous_transaction_hash, 1), SECP256k1Signature(b'y' * 64), ) ], outputs=[output_2]) result = uto_apply_transaction(unspent_transaction_outs, transaction, is_coinbase=False) assert OutputReference(previous_transaction_hash, 0) in result assert OutputReference(previous_transaction_hash, 1) not in result # spent assert OutputReference(transaction.hash(), 0) in result assert result[OutputReference(previous_transaction_hash, 0)] == output_0 assert result[OutputReference(transaction.hash(), 0)] == output_2
def test_transaction_serialization(): trans = Transaction( inputs=[ Input(output_reference=OutputReference(b"b" * 32, 1234), signature=SECP256k1Signature(b"b" * 64)) ], outputs=[Output(value=1582, public_key=SECP256k1PublicKey(b"g" * 64))], ) serialize_and_deserialize(trans)
def test_transaction_repr(): trans = Transaction( inputs=[ Input(output_reference=OutputReference(b"b" * 32, 1234), signature=SECP256k1Signature(b"b" * 64)) ], outputs=[Output(value=1582, public_key=SECP256k1PublicKey(b"g" * 64))], ) assert repr( trans ) == "Transaction #4025f3f13790dc96d857562dabcdd257ee9dfd95ce126e11d8cbbe64ac1bbec4"
def handle_request_scrypt_input_message(self, miner_id: int, data: int) -> None: nonce: int = data self.coinstate, transactions = self.network_thread.local_peer.chain_manager.get_state( ) increasing_time = max(int(time()), self.coinstate.head().timestamp + 1) summary, current_height, transactions = \ construct_block_pow_evidence_input(self.coinstate, transactions, SECP256k1PublicKey(self.public_key), increasing_time, b'', nonce) self.mining_args[miner_id] = summary, current_height, transactions self.send_message(miner_id, "scrypt_input", (summary, current_height))
def __call__(self) -> None: self.prepare() self.send_message("start_balance", self.get_balance()) self.send_message("info", "Starting mining: A repeat minter") try: public_key = self.get_key_for_mined_block() nonce = random.randrange(1 << 32) last_round_second = int(time()) hashes = 0 while True: current_second = int(time()) if current_second > last_round_second: self.send_message("hashes", (current_second, hashes)) last_round_second = current_second hashes = 0 self.coinstate, transactions = self.thread.local_peer.chain_manager.get_state( ) increasing_time = max(current_second, self.coinstate.head().timestamp + 1) block = construct_block_for_mining( self.coinstate, transactions, SECP256k1PublicKey(public_key), increasing_time, b'', nonce) hashes += 1 nonce = (nonce + 1) % (1 << 32) if block.hash() < block.target: self.handle_mined_block(block) public_key = self.get_key_for_mined_block() except KeyboardInterrupt: self.send_message("info", "KeyboardInterrupt") finally: self.send_message("info", "Stopping networking thread") self.thread.stop() self.send_message("info", "Waiting for networking thread to stop") self.thread.join() self.send_message("info", "Done; waiting for Python-exit")
def test_get_transaction_fee(): public_key = SECP256k1PublicKey(b'x' * 64) previous_transaction_hash = b'a' * 32 unspent_transaction_outs = immutables.Map({ OutputReference(previous_transaction_hash, 0): Output(40, public_key), OutputReference(previous_transaction_hash, 1): Output(34, public_key), }) transaction = Transaction( inputs=[Input( OutputReference(previous_transaction_hash, 1), SECP256k1Signature(b'y' * 64), )], outputs=[Output(30, public_key)] ) assert get_transaction_fee(transaction, unspent_transaction_outs) == 34 - 30 # 4
def test_block_serialization(): block = Block( header=BlockHeader( summary=example_block_summary, pow_evidence=example_pow_evidence, ), transactions=[ Transaction( inputs=[ Input(output_reference=OutputReference(b"b" * 32, 1234), signature=SECP256k1Signature(b"b" * 64)) ], outputs=[ Output(value=1582, public_key=SECP256k1PublicKey(b"g" * 64)) ], ) ] * 2, ) serialize_and_deserialize(block)
construct_pow_evidence, get_block_subsidy, get_transaction_fee, validate_coinbase_transaction_by_itself, validate_block_header_by_itself, validate_block_by_itself, # validate_non_coinbase_transaction_in_coinstate, ValidateBlockError, ValidateBlockHeaderError, ValidateTransactionError, ValidatePOWError, ) from skepticoin.signing import SECP256k1PublicKey, SECP256k1Signature from skepticoin.datatypes import Transaction, OutputReference, Input, Output, Block example_public_key = SECP256k1PublicKey(b'x' * 64) def get_example_genesis_block(): return Block.deserialize( computer( """000000000000000000000000000000000000000000000000000000000000000000008278968af4bd613aa24a5ccd5280211b3101e3""" """ff62621bb11500509d1bbe2a956046240b0100000000000000000000000000000000000000000000000000000000000000000000d7""" """38f2c472180cb401f650b12be96ec25bfd9b4e9908c6c9089d9bf26401646f87000000000000000000000000000000000000000000""" """0000000000000000000000077a14cfbe21d47f367f23f9a464c765541b1b07bef9f5a95901e0bffe3a1a2f01000100000000000000""" """000000000000000000000000000000000000000000000000000000000001000000000001000000012a05f200027878787878787878""" """7878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878""" """787878""")) def test_construct_minable_summary_no_transactions():
def main() -> None: parser = DefaultArgumentParser() parser.add_argument("amount", help="The amount of to send", type=int) parser.add_argument("denomination", help="'skepticoin' or 'sashimi'", choices=['skepticoin', 'sashimi']) parser.add_argument("address", help="The address to send to") args = parser.parse_args() configure_logging_from_args(args) value = args.amount * (SASHIMI_PER_COIN if args.denomination == 'skepticoin' else 1) if not is_valid_address(args.address): print("Invalid address") return check_chain_dir() coinstate = read_chain_from_disk() wallet = open_or_init_wallet() thread = start_networking_peer_in_background(args, coinstate) try: # we need a fresh chain because our wallet doesn't track spending/receiving, so we need to look at the real # blockchain to know what we can spend. check_for_fresh_chain(thread) print("Chain up to date") target_address = SECP256k1PublicKey(parse_address(args.address)) change_address = SECP256k1PublicKey(wallet.get_annotated_public_key("change")) save_wallet(wallet) transaction = create_spend_transaction( wallet, coinstate, value, 0, # we'll get to paying fees later target_address, change_address, ) validate_non_coinbase_transaction_by_itself(transaction) assert coinstate.current_chain_hash validate_non_coinbase_transaction_in_coinstate(transaction, coinstate.current_chain_hash, coinstate) print("Broadcasting transaction on the network", transaction) thread.local_peer.network_manager.broadcast_transaction(transaction) print("Monitoring...") while True: sleep(5) # it's late and I'm too lazy for the efficient & correct implementation. coinstate = thread.local_peer.chain_manager.coinstate max_height = coinstate.head().height for i in range(10): block = coinstate.by_height_at_head()[max(max_height - i, 0)] if transaction in block.transactions: print("Transaction confirmed at", block.height, "with", i, "confirmation blocks") if i == 6: # this is the magic number of confirmations according to the "literature" on the subject thread.stop() return except KeyboardInterrupt: print("KeyboardInterrupt") finally: print("Stopping networking thread") thread.stop() print("Waiting for networking thread to stop") thread.join() print("Done; waiting for Python-exit")
def test_output_serialization(): out = Output( value=1582, public_key=SECP256k1PublicKey(b"g" * 64), ) serialize_and_deserialize(out)
def test_publickey_serialization(): pk = SECP256k1PublicKey(b"5" * 64) serialize_and_deserialize(pk, PublicKey)
def test_broadcast_transaction(caplog, mocker): # just testing the basics: is a broadcast transaction stored in the transaction pool on the other side? # By turning off transaction-validation, we can use an invalid transaction in this test. mocker.patch( "skepticoin.networking.peer.validate_non_coinbase_transaction_by_itself" ) mocker.patch( "skepticoin.networking.peer.validate_non_coinbase_transaction_in_coinstate" ) caplog.set_level(logging.INFO) coinstate = _read_chain_from_disk(5) thread_a = NetworkingThread(coinstate, 12412, FakeDiskInterface()) thread_a.start() _try_to_connect('127.0.0.1', 12412) thread_b = NetworkingThread(coinstate, 12413, FakeDiskInterface()) thread_b.local_peer.network_manager.disconnected_peers = load_peers_from_list( [('127.0.0.1', 12412, "OUTGOING")]) thread_b.start() previous_hash = coinstate.at_head.block_by_height[0].transactions[0].hash() # Not actually a valid transaction (not signed) transaction = Transaction( inputs=[ Input(OutputReference(previous_hash, 0), SignableEquivalent()) ], outputs=[Output(10, SECP256k1PublicKey(b'x' * 64))], ) try: # give both peers some time to find each other start_time = time() while True: if (len(thread_a.local_peer.network_manager.get_active_peers()) > 0 and len(thread_b.local_peer.network_manager.get_active_peers()) > 0): break if time() > start_time + 5: print("\n".join(str(r) for r in caplog.records)) raise Exception("Peers can't connect") sleep(0.01) # broadcast_transaction... the part that we're testing thread_a.local_peer.network_manager.broadcast_transaction(transaction) # wait until it's picked up on the other side start_time = time() while True: if len(thread_b.local_peer.chain_manager.transaction_pool) > 0: break if time() > start_time + 5: print("\n".join(str(r) for r in caplog.records)) raise Exception("Transaction broadcast failed") sleep(0.01) finally: thread_a.stop() thread_a.join() thread_b.stop() thread_b.join()
def main(): parser = DefaultArgumentParser() args = parser.parse_args() configure_logging_from_args(args) create_chain_dir() coinstate = read_chain_from_disk() wallet = open_or_init_wallet() initialize_peers_file() thread = start_networking_peer_in_background(args, coinstate) thread.local_peer.show_stats() if check_for_fresh_chain(thread): thread.local_peer.show_stats() print("Starting mining: A repeat minter") try: print("Starting main loop") while True: public_key = wallet.get_annotated_public_key( "reserved for potentially mined block") save_wallet(wallet) nonce = random.randrange(1 << 32) last_round_second = int(time()) i = 0 while True: if int(time()) > last_round_second: print("Hashrate:", i) last_round_second = int(time()) i = 0 coinstate, transactions = thread.local_peer.chain_manager.get_state( ) increasing_time = max(int(time()), coinstate.head().timestamp + 1) block = construct_block_for_mining( coinstate, transactions, SECP256k1PublicKey(public_key), increasing_time, b'', nonce) i += 1 nonce = (nonce + 1) % (1 << 32) if block.hash() < block.target: break coinstate = coinstate.add_block(block, int(time())) with open(Path('chain') / block_filename(block), 'wb') as f: f.write(block.serialize()) print("FOUND", block_filename(block)) print("Wallet balance: %s skepticoin" % (wallet.get_balance(coinstate) / Decimal(SASHIMI_PER_COIN))) thread.local_peer.chain_manager.set_coinstate(coinstate) thread.local_peer.network_manager.broadcast_block(block) except KeyboardInterrupt: print("KeyboardInterrupt") finally: print("Stopping networking thread") thread.stop() print("Waiting for networking thread to stop") thread.join() print("Done; waiting for Python-exit")
def main(): parser = DefaultArgumentParser() args = parser.parse_args() configure_logging_from_args(args) create_chain_dir() coinstate = read_chain_from_disk() wallet = open_or_init_wallet() initialize_peers_file() thread = start_networking_peer_in_background(args, coinstate) thread.local_peer.show_stats() if check_for_fresh_chain(thread): thread.local_peer.show_stats() if thread.local_peer.chain_manager.coinstate.head().height <= MAX_KNOWN_HASH_HEIGHT: print("Your blockchain is not just old, it is ancient; ABORTING") return start_time = datetime.now() start_balance = wallet.get_balance(coinstate) / Decimal(SASHIMI_PER_COIN) balance = start_balance print("Wallet balance: %s skepticoin" % start_balance) print("Starting mining: A repeat minter") try: print("Starting main loop") while True: public_key = wallet.get_annotated_public_key("reserved for potentially mined block") save_wallet(wallet) nonce = random.randrange(1 << 32) last_round_second = int(time()) hashes = 0 while True: if int(time()) > last_round_second: now = datetime.now() now_str = now.strftime("%Y-%m-%d %H:%M:%S") uptime = now - start_time uptime_str = str(uptime).split(".")[0] mined = balance - start_balance mine_speed = (float(mined) / uptime.total_seconds()) * 60 * 60 print(f"{now_str} | uptime: {uptime_str} | {hashes:>2} hash/sec" + f" | mined: {mined:>3} SKEPTI | {mine_speed:5.2f} SKEPTI/h") last_round_second = int(time()) hashes = 0 coinstate, transactions = thread.local_peer.chain_manager.get_state() increasing_time = max(int(time()), coinstate.head().timestamp + 1) block = construct_block_for_mining( coinstate, transactions, SECP256k1PublicKey(public_key), increasing_time, b'', nonce) hashes += 1 nonce = (nonce + 1) % (1 << 32) if block.hash() < block.target: break coinstate = coinstate.add_block(block, int(time())) with open(Path('chain') / block_filename(block), 'wb') as f: f.write(block.serialize()) print("FOUND", block_filename(block)) balance = (wallet.get_balance(coinstate) / Decimal(SASHIMI_PER_COIN)) print("Wallet balance: %s skepticoin" % balance) thread.local_peer.chain_manager.set_coinstate(coinstate) thread.local_peer.network_manager.broadcast_block(block) except KeyboardInterrupt: print("KeyboardInterrupt") finally: print("Stopping networking thread") thread.stop() print("Waiting for networking thread to stop") thread.join() print("Done; waiting for Python-exit")